- Memory allocation safety checks for event storage (thanks David.A for bug report)
- Fix off-by-one boundary check in seqmap code (thanks David.A for bug report)
-- The minimum value for the per-host interval (-i flag) is now 0.001 (milliseconds),
- since it probably never makes sense to use a smaller value, and to avoid trying
- to do a too large memory allocation.
+- The minimum value for the period (-p flag) is now 0.001 milliseconds,
+ since it probably never makes sense to use a smaller value, and to avoid doing
+ a very large memory allocation for event storage.
fping 5.3 (2025-01-02)
======================
--- /dev/null
+fping 5.4 (2025-08-19)
+======================
+
+## Bugfixes
+
+- Memory allocation safety checks for event storage (thanks David.A for bug report)
+- Fix off-by-one boundary check in seqmap code (thanks David.A for bug report)
+- The minimum value for the per-host interval (-i flag) is now 0.001 (milliseconds),
+ since it probably never makes sense to use a smaller value, and to avoid trying
+ to do a too large memory allocation.
+
+fping 5.3 (2025-01-02)
+======================
+
+## New features
+
+- New option --icmp-timestamp to send ICMP timestamp requests (ICMP type 13)
+ instead of ICMP Echo requests (#353 #363, thanks @auerswal and @gsnw-sebast)
+- New option --print-ttl to print returned TTL value (#354, thanks @nalves599)
+- New option --print-tos to print returned TOS value (#335 #346 #347, thanks
+ @auerswal and @gsnw-sebast)
+- New option --check-source (#334, thanks @auerswal)
+- Predefined various timestamp formats (#321, thanks @auerswal and @gsnw-sebast)
+- Print cumulative stats with -Q SECS,cumulative (#315, thanks @auerswal)
+
+## Bugfixes and other changes
+
+- ci: Upgrade actions/upload-artifact to v4 (#360, thanks @gsnw-sebast)
+- ci: Azure Pipeline only trigger when changes are made in the development branch
+ (#359, thanks @gsnw-sebast)
+- ci: Upgrade actions/upload-artifact to v3 (#355, thanks @pevik)
+- ci: Azure Pipeline YAML add docker build (#354, thanks @gsnw-sebast)
+- Dockerfile: change distribution from ubuntu to debian (#350, thanks
+ @gsnw-sebast)
+- Fix warning unused parameter 'reply_timestamp' under macOS (#348, thanks
+ @gsnw-sebast)
+- Fix increase maximum -s value to 65507 (#344, thanks @pevik)
+- ci: use File::Temp to create temporary directory (#343, thanks @auerswal)
+- Fix -k, --fwmark with setuid fping executable (#342, thanks @auerswal)
+- Another batch of additional tests (take 2) (#341, thanks @auerswal)
+- Document that -a and -u are overridden by -c and -C (#338, thanks @auerswal)
+- Fix macOS build warning sets SEQMAP_TIMEOUT_IN_NSSEQMAP_TIMEOUT_IN_NS as INT64_C
+ (#336, thanks @gsnw-sebast)
+- Fix inconsistent limits for address generation via -g, --generator using either
+ range or CIDR (#331, thanks @auerswal)
+- Some additional tests (#329, thanks @auerswal)
+- ci: skip an unreliable test on macOS (#328, thanks @auerswal)
+- Fix incorrect return-value check for a scanf like function (CWE-253) (#323,
+ thanks @gsnw-sebast)
+- A few more tests to increase code coverage a little bit (#320, thanks @auerswal)
+- Github fix: Change to codeql-action-v2 (#319, thanks @gsnw-sebast)
+- Developer function: Debug with Visual Studio Code (#318, thanks @gsnw-sebast)
+
+fping 5.2 (2024-04-21)
+======================
+
+## New features
+
+- New option -X / --fast-reachable to exit immediately once N hosts have been
+ found (#260, thanks @chriscray and @gsnw)
+
+- New option -k / -fwmark to set Linux fwmark mask (#289, thanks @tomangert and
+ @deepkv)
+
+## Bugfixes and other changes
+
+- Always output fatal error messages (#303, thanks @auerswal)
+- Fallback to SO\_TIMESTAMP if SO\_TIMESTAMPNS is not available (#279, thanks
+ @gsnw)
+- Fix "not enough sequence numbers available" error on BSD-like systems (#307,
+ thanks @cagney, @gsnw)
+- Fix running in unprivileged mode (#248, thanks @sfan5)
+- Fix build issue for NetBSD/alpha (#255, thanks @0-wiz-0)
+- Fix build issue for OpenBSD/alpha (#275, thanks @gsnw)
+- Fix build warning for long int usage (#258, thanks @gsnw)
+- Fix build error with musl libc (#263, thanks @kraj)
+- Fix to guard against division by zero (#293, thanks @auerswal)
+- Decouple -a/-u effects from -c (#298, thanks @auerswal)
+- Added contrib/Dockerfile (#224, thanks @darless)
+- Remove host from Netdata chart titles (#253, thanks @ilyam8)
+- Add additional tests (#292, #297, thanks @auerswal)
+- Update github action os images (#282, thanks @gsnw)
+- Fix Azure pipeline tests (#308, thanks @gsnw)
+- Various autoconf fixes (#286, #283, thanks @gsnw)
+- Extended configure script with --enable-debug and output cpu usage (#311,
+ thanks @gsnw)
+- Documentation: Update Netdata website link (#257, thanks @ilyam8)
+- Documentation: fix description of --file option (#268, thanks @MohGeek)
+- Documentation: improve exit status description (#294, thanks @auerswal)
+- Documentation: move description of -i MSEC (#298, thanks @auerswal)
+- Documentation: improve help output for options -c and -C (#302, #auerswal)
+
+
+fping 5.1 (2022-02-06)
+======================
+
+## Bugfixes and other changes
+
+- Use setcap to specify specific files in fping.spec (#232, thanks @zdyxry)
+- Netdata: use host instead name as family label (#226, thanks @k0ste)
+- Netdata: use formatstring macro PRId64 (#229, thanks @gsnw)
+- Allow -4 option to be given multiple times (#215, thanks @normanr)
+- Documentation fix (#208, thanks @timgates42)
+- Retain privileges until after privileged setsockopt (#200, thanks @simetnicbr)
+- Set bind to source only when option is set (#198, thanks @dinoex)
+- Update Azure test pipeline (#197, thanks @gsnw)
+- Fix getnameinfo not called properly for IPv4 (#227, thanks @aafbsd)
+- Fixed wrong timestamp under Free- and OpenBSD and macOS (#217, thanks @gsnw)
+- Documentation updates (#240, thanks @auerswal)
+- Updated autotools (autoconf 2.71, automake 1.16.5, libtool 2.4.6)
+
+
+fping 5.0 (2020-08-05)
+======================
+
+## Incompatible Changes
+
+- In non-quiet loop and count mode, a line is printed for every lost packet
+ (#175, thanks @kbucheli):
+
+ ```
+ $ fping -D -c2 8.8.8.8 8.8.8.7
+ [1596092373.18423] 8.8.8.8 : [0], 64 bytes, 12.8 ms (12.8 avg, 0% loss)
+ [1596092374.18223] 8.8.8.7 : [0], timed out (NaN avg, 100% loss)
+ [1596092374.18424] 8.8.8.8 : [1], 64 bytes, 12.3 ms (12.5 avg, 0% loss)
+ [1596092375.18344] 8.8.8.7 : [1], timed out (NaN avg, 100% loss)
+
+ 8.8.8.8 : xmt/rcv/%loss = 2/2/0%, min/avg/max = 12.3/12.5/12.8
+ 8.8.8.7 : xmt/rcv/%loss = 2/0/100%
+ ```
+
+- The returned size in bytes now always excludes the IP header, so if before it
+ reported '84 bytes' e.g. when using 'fping -l', now it reports '64 bytes'.
+ This is to make the reported size consistent with ping(8) from iputils and
+ also with fping when pinging a IPv6 host (which never included the IPv6
+ header size).
+
+## New features
+
+- The number of sent pings is only counted when the pings are received or have
+ timed out, ensuring that the loss ratio will be always correct. This makes it
+ possible, for example, to use loop mode (-l) with interval statistics (-Q)
+ and a timeout larger than period, without having the issue that initially
+ some pings would be reported as missing (#193)
+
+- Improved precision of measurements from 10us to 1us (#136, thanks @tycho)
+
+## Bugfixes and other changes
+
+- The reported size of received packets is now always correct on Linux even for
+ packets > 4096 bytes (#180)
+
+- Travis CI automated testing now also macos testing and additional ubuntu
+ distributions (#196)
+
+fping 4.4 (2020-07-24)
+======================
+## Bugfixes and other changes
+
+- Fix wrong ident used for normal (non-unprivileged) pings (#191, thanks @tycho)
+- Fix build with --disable-ipv6 (#187, thanks Polynomial-C)
+
+fping 4.3 (2020-07-11)
+======================
+
+## New features
+
+- Linux unprivileged ping support (#173, thanks @tycho)
+- Add SIGQUIT summary support similar to ping (#185, thanks @laddp)
+
+## Bugfixes and other changes
+
+- Corrected long option name of -s to --stats (#148, thanks @wopfel)
+- Do not fail if using fping6 with -6 flag (#149, thanks @stromnet)
+- Fail if interface binding (-I) does not work (#162, thanks @kbucheli)
+- Fix using option -4 when fping is compiled IPv4-only (#154, thanks @pbhenson)
+- Add Azure pipeline test build (#153 and #170, thanks @gsnw)
+- GCC 10 compatibility fixes (#167 and #168, thanks @cranderson)
+- Macos build fix (#174, thanks @tycho)
+- Fix xmt stats in Netdata output (#172, thanks @vlvkobal)
+- Only increase num_alive if response is not a duplicate (#151, thanks @brownowski)
+- Use line buffering for stdout (#179, thanks @bg6cq)
+
+fping 4.2 (2019-02-19)
+======================
+
+## New features
+
+- New option -x / --reachable to check if the number of reachable hosts is >= a certain
+ number. Useful for example to implement connectivity-checks (#138, thanks @deepak0004)
+
+## Bugfixes and other changes
+
+- Allow decimal numbers for '-t', '-i', '-p', and '-Q'
+- Fix build with --disable-ipv6 (#134, thanks @Polynomial-C)
+- Fix hang with '-6', with ipv6 kernel module, but not loaded (#140, thanks @abelbeck)
+- Assume '-6' if the binary is named 'fping6' (this is mostly for special
+ embedded-distro use cases, and not meant to be used generally in place of
+ compiling IPv6-only binary or using '-6', see also the notes in #139, thanks
+ abelbeck)
+- Get rid of warning "timeout (-t) value larger than period (-p) produces unexpected results"
+ (#142, thanks @MrDragon1122)
+
+
+fping 4.1 (2018-09-17)
+======================
+
+## Bugfixes and other changes
+
+- Fix problem when socket fd is 0 (#125, thanks Ramón Novoa!)
+- Fix running on servers with disabled IPv6 (#118, thanks Simon Matter)
+- Allow running "fping -h" or "--help" even when raw socket can't be opened (#131, thanks @teto)
+- Fix build issue with FreeBSD and IPv6 (#132, thanks @gsnw)
+
+fping 4.0 (2017-04-23)
+======================
+
+## Incompatible Changes
+
+##### fping and fping6 unification
+
+fping and fping6 are now unified into one binary. It means that, for example,
+doing 'fping google.com' is going to ping the IPv6 IP of google.com on
+IPv6-enabled hosts.
+
+If you need exact compatibility with old versions, you can configure and
+install fping twice: once for ipv4, and once for ipv6:
+
+ ./configure --disable-ipv6; make clean install
+ ./configure --disable-ipv4 --program-suffix=6; make clean install
+
+##### Option -n, not the same as -d anymore
+
+Option -n / --name is now doing a reverse-DNS lookups on host addresses,
+only if they are given as IP address, but not for hostnames. For example,
+if you write 'fping -n google.com', fping would previously do a
+forward-DNS lookup on google.com, and then a reverse-DNS lookup on the
+resolved IP address. Now, it is just going to keep the name 'google.com'.
+That same behavior can be achieved with the option -d / --rdns (which was
+previously an alias for -n).
+
+ fping<4.0 fping>=4.0
+ fping -n NAME NAME->IP->IPNAME NAME
+ fping -d NAME NAME->IP->IPNAME NAME->IP->IPNAME
+
+##### Discarding of late packets
+
+fping will now discard replies, if they arrive after the defined timeout
+for reply packets, specified with -t. This change is relevant only for the
+count and loop modes, where the measured times should be now more
+consistent (see github issue [#32][i32] for details).
+
+To prevent loosing reply packets because of this change, the default
+timeout in count and loop modes is now automatically adjusted to the
+period interval (up to 2000 ms), but it can be overriden with the -t
+option. The default timeout for non-loop/count modes remains 500 ms.
+
+##### No restrictions by default
+
+fping will not enforce -i >= 1 and -p >= 10 anymore, except if you
+'./configure --enable-safe-limits'.
+
+The reasoning to removing the restrictions by default, is that users can
+clog the network with other tools anyway, and these restrictions are
+sometimes getting in the way (for example if you try to ping a lot of
+hosts).
+
+##### Default interval (-i) changed from 25ms to 10ms
+
+The default minimum interval between ping probes has been changed from
+25ms to 10ms. The reason is that 25ms is very high, considering today's
+fast networks: it generates at most 31 kbps of traffic (for IPv4 and
+default payload size).
+
+## New features
+
+- Unified 'fping' and 'fping6' into one binary ([#80][i80])
+- Long option names for all options
+- IPv6 enabled by default
+- New option -4 to force IPv4
+- New option -6 to force IPv6
+- Keep original name if a hostname is given with -n/--name
+- Option -d/--rdns now always does a rdns-lookup, even for names, as '-n' was doing until now
+- Enforce -t timeout on reply packets, by discarding late packets ([#32][i32])
+- Auto-adjust timeout for -c/-C/-l mode to value of -p
+
+## Bugfixes and other changes
+
+- -i/-p restrictions disabled by default (enable with --enable-safe-limits)
+- Default interval -i changed from 25ms to 10ms
+- Fix compatibility issue with GNU Hurd
+- A C99 compiler is now required
+- Option parsing with optparse (https://github.com/skeeto/optparse). Thanks Christopher Wellons!
+- New changelog file format
+
+[i32]: https://github.com/schweikert/fping/issues/32
+[i80]: https://github.com/schweikert/fping/issues/80
+
+(see doc/CHANGELOG.pre-v4 for older changes)
--- /dev/null
+ * Original author: Roland Schemers <schemers@stanford.edu>
+ * IPv6 Support: Jeroen Massar <jeroen@unfix.org / jeroen@ipng.nl>
+ * Improved main loop: David Schweikert <david@schweikert.ch>
+ * Debian Merge, TOS settings: Tobi Oetiker <tobi@oetiker.ch>
+ * Bugfixes, byte order & senseful seq.-numbers: Stephan Fuhrmann (stephan.fuhrmann AT 1und1.de)
+ *
+ * Redistribution and use in source and binary forms are permitted
+ * provided that the above copyright notice and this paragraph are
+ * duplicated in all such forms and that any documentation,
+ * advertising materials, and other materials related to such
+ * distribution and use acknowledge that the software was developed
+ * by Stanford University. The name of the University may not be used
+ * to endorse or promote products derived from this software without
+ * specific prior written permission.
+ * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED
+ * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE.
--- /dev/null
+Basic Installation
+==================
+
+ These are generic installation instructions.
+
+ --> See the README file for fping-specific instructions. <--
+
+ The `configure' shell script attempts to guess correct values for
+various system-dependent variables used during compilation. It uses
+those values to create a `Makefile' in each directory of the package.
+It may also create one or more `.h' files containing system-dependent
+definitions. Finally, it creates a shell script `config.status' that
+you can run in the future to recreate the current configuration, a file
+`config.cache' that saves the results of its tests to speed up
+reconfiguring, and a file `config.log' containing compiler output
+(useful mainly for debugging `configure').
+
+ If you need to do unusual things to compile the package, please try
+to figure out how `configure' could check whether to do them, and mail
+diffs or instructions to the address given in the `README' so they can
+be considered for the next release. If at some point `config.cache'
+contains results you don't want to keep, you may remove or edit it.
+
+ The file `configure.in' is used to create `configure' by a program
+called `autoconf'. You only need `configure.in' if you want to change
+it or regenerate `configure' using a newer version of `autoconf'.
+
+The simplest way to compile this package is:
+
+ 1. `cd' to the directory containing the package's source code and type
+ `./configure' to configure the package for your system. If you're
+ using `csh' on an old version of System V, you might need to type
+ `sh ./configure' instead to prevent `csh' from trying to execute
+ `configure' itself.
+
+ Running `configure' takes a while. While running, it prints some
+ messages telling which features it is checking for.
+
+ 2. Type `make' to compile the package.
+
+ 3. Optionally, type `make check' to run any self-tests that come with
+ the package.
+
+ 4. Type `make install' to install the programs and any data files and
+ documentation.
+
+ 5. You can remove the program binaries and object files from the
+ source code directory by typing `make clean'. To also remove the
+ files that `configure' created (so you can compile the package for
+ a different kind of computer), type `make distclean'. There is
+ also a `make maintainer-clean' target, but that is intended mainly
+ for the package's developers. If you use it, you may have to get
+ all sorts of other programs in order to regenerate files that came
+ with the distribution.
+
+Compilers and Options
+=====================
+
+ Some systems require unusual options for compilation or linking that
+the `configure' script does not know about. You can give `configure'
+initial values for variables by setting them in the environment. Using
+a Bourne-compatible shell, you can do that on the command line like
+this:
+ CC=c89 CFLAGS=-O2 LIBS=-lposix ./configure
+
+Or on systems that have the `env' program, you can do it like this:
+ env CPPFLAGS=-I/usr/local/include LDFLAGS=-s ./configure
+
+Compiling For Multiple Architectures
+====================================
+
+ You can compile the package for more than one kind of computer at the
+same time, by placing the object files for each architecture in their
+own directory. To do this, you must use a version of `make' that
+supports the `VPATH' variable, such as GNU `make'. `cd' to the
+directory where you want the object files and executables to go and run
+the `configure' script. `configure' automatically checks for the
+source code in the directory that `configure' is in and in `..'.
+
+ If you have to use a `make' that does not supports the `VPATH'
+variable, you have to compile the package for one architecture at a time
+in the source code directory. After you have installed the package for
+one architecture, use `make distclean' before reconfiguring for another
+architecture.
+
+Installation Names
+==================
+
+ By default, `make install' will install the package's files in
+`/usr/local/bin', `/usr/local/man', etc. You can specify an
+installation prefix other than `/usr/local' by giving `configure' the
+option `--prefix=PATH'.
+
+ You can specify separate installation prefixes for
+architecture-specific files and architecture-independent files. If you
+give `configure' the option `--exec-prefix=PATH', the package will use
+PATH as the prefix for installing programs and libraries.
+Documentation and other data files will still use the regular prefix.
+
+ If the package supports it, you can cause programs to be installed
+with an extra prefix or suffix on their names by giving `configure' the
+option `--program-prefix=PREFIX' or `--program-suffix=SUFFIX'.
+
+Optional Features
+=================
+
+ Some packages pay attention to `--enable-FEATURE' options to
+`configure', where FEATURE indicates an optional part of the package.
+They may also pay attention to `--with-PACKAGE' options, where PACKAGE
+is something like `gnu-as' or `x' (for the X Window System). The
+`README' should mention any `--enable-' and `--with-' options that the
+package recognizes.
+
+ For packages that use the X Window System, `configure' can usually
+find the X include and library files automatically, but if it doesn't,
+you can use the `configure' options `--x-includes=DIR' and
+`--x-libraries=DIR' to specify their locations.
+
+Specifying the System Type
+==========================
+
+ There may be some features `configure' can not figure out
+automatically, but needs to determine by the type of host the package
+will run on. Usually `configure' can figure that out, but if it prints
+a message saying it can not guess the host type, give it the
+`--host=TYPE' option. TYPE can either be a short name for the system
+type, such as `sun4', or a canonical name with three fields:
+ CPU-COMPANY-SYSTEM
+
+See the file `config.sub' for the possible values of each field. If
+`config.sub' isn't included in this package, then this package doesn't
+need to know the host type.
+
+ If you are building compiler tools for cross-compiling, you can also
+use the `--target=TYPE' option to select the type of system they will
+produce code for and the `--build=TYPE' option to select the type of
+system on which you are compiling the package.
+
+Sharing Defaults
+================
+
+ If you want to set default values for `configure' scripts to share,
+you can create a site shell script called `config.site' that gives
+default values for variables like `CC', `cache_file', and `prefix'.
+`configure' looks for `PREFIX/share/config.site' if it exists, then
+`PREFIX/etc/config.site' if it exists. Or, you can set the
+`CONFIG_SITE' environment variable to the location of the site script.
+A warning: not all `configure' scripts look for a site script.
+
+Operation Controls
+==================
+
+ `configure' recognizes the following options to control how it
+operates.
+
+`--cache-file=FILE'
+ Use and save the results of the tests in FILE instead of
+ `./config.cache'. Set FILE to `/dev/null' to disable caching, for
+ debugging `configure'.
+
+`--help'
+ Print a summary of the options to `configure', and exit.
+
+`--quiet'
+`--silent'
+`-q'
+ Do not print messages saying which checks are being made.
+
+`--srcdir=DIR'
+ Look for the package's source code in directory DIR. Usually
+ `configure' can determine that directory automatically.
+
+`--version'
+ Print the version of Autoconf used to generate the `configure'
+ script, and exit.
+
+`configure' also accepts some other, not widely useful, options.
--- /dev/null
+SUBDIRS = doc src
+
+EXTRA_DIST = CHANGELOG.md contrib README.md ci/*.sh ci/*.pl
--- /dev/null
+[](https://travis-ci.org/schweikert/fping)
+[](https://coveralls.io/github/schweikert/fping?branch=develop)
+[](https://scan.coverity.com/projects/schweikert-fping)
+
+# fping
+
+fping is a program to send ICMP echo probes to network hosts, similar to ping,
+but much better performing when pinging multiple hosts. fping has a long long
+story: Roland Schemers did publish a first version of it in 1992 and it has
+established itself since then as a standard tool.
+
+_Current maintainer_:
+ David Schweikert \<david@schweikert.ch\>
+
+_Website_:
+ https://fping.org/
+
+_Mailing-list_:
+ https://groups.google.com/group/fping-users
+
+## Installation
+
+If you want to install fping from source, proceed as follows:
+
+0. Run `./autogen.sh`
+ (only if you got the source from Github).
+1. Run `./configure` with the correct arguments.
+ (see: `./configure --help`)
+2. Run `make; make install`.
+3. Make fping either setuid, or, if under Linux:
+ `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. The --fwmark option needs root or
+cap_net_admin.
+
+## Usage
+
+Have a look at the [fping(8)](doc/fping.pod) manual page for usage help.
+(`fping -h` will also give a minimal help output.)
+
+## Credits
+
+* Original author: Roland Schemers (schemers@stanford.edu)
+* Previous maintainer: RL "Bob" Morgan (morgan@stanford.edu)
+* Initial IPv6 Support: Jeroen Massar (jeroen@unfix.org / jeroen@ipng.nl)
+* Other contributors: see [CHANGELOG.md](CHANGELOG.md)
--- /dev/null
+#!/bin/bash
+
+set -e
+set -x
+
+if [[ "$OSTYPE" == "darwin"* ]]; then
+ exit 0
+fi
+
+AUTOCONF=http://ftp.gnu.org/gnu/autoconf/autoconf-2.71.tar.gz
+AUTOMAKE=http://ftp.gnu.org/gnu/automake/automake-1.16.5.tar.gz
+LIBTOOL=http://ftp.gnu.org/gnu/libtool/libtool-2.4.6.tar.gz
+PREFIX=$(pwd)/ci/build
+PATH=$(pwd)/ci/build/bin:$PATH
+
+if [ ! -d ci ]; then
+ echo "you must run this in the root fping directory" >&2
+ exit 1
+fi
+
+# remove standard versions
+sudo apt-get remove -qq autoconf automake autotools-dev libtool
+
+# prepare build environment
+cd ci
+rm -rf build
+mkdir -p build/src
+cd build/src
+
+# autoconf
+(
+AUTOCONF_FILE=$(basename $AUTOCONF)
+AUTOCONF_DIR=$(echo $AUTOCONF_FILE | sed -e 's/\.tar.*//')
+wget $AUTOCONF
+tar xf $AUTOCONF_FILE
+cd $AUTOCONF_DIR
+./configure --prefix=$PREFIX
+make install
+)
+
+# automake
+(
+AUTOMAKE_FILE=$(basename $AUTOMAKE)
+AUTOMAKE_DIR=$(echo $AUTOMAKE_FILE | sed -e 's/\.tar.*//')
+wget $AUTOMAKE
+tar xf $AUTOMAKE_FILE
+cd $AUTOMAKE_DIR
+./configure --prefix=$PREFIX
+make install
+)
+
+# libtool
+(
+LIBTOOL_FILE=$(basename $LIBTOOL)
+LIBTOOL_DIR=$(echo $LIBTOOL_FILE | sed -e 's/\.tar.*//')
+wget $LIBTOOL
+tar xf $LIBTOOL_FILE
+cd $LIBTOOL_DIR
+./configure --prefix=$PREFIX
+make install
+)
--- /dev/null
+#!/bin/sh
+
+set -ex
+
+curl -L http://cpanmin.us | perl - -L $HOME/perl5 App::cpanminus
+$HOME/perl5/bin/cpanm --sudo Test::Command
--- /dev/null
+#!/bin/bash
+
+# only do this on Mac OS X
+if [ `uname -s` != "Darwin" ]; then
+ exit 0;
+fi
+
+if [[ ! `ifconfig lo0` =~ 127\.0\.0\.2 ]]; then
+ sudo ifconfig lo0 127.0.0.2/8 alias
+ sudo ifconfig lo0 127.0.0.3/8 alias
+ sudo ifconfig lo0 127.0.0.4/8 alias
+ sudo ifconfig lo0 127.0.0.5/8 alias
+fi
--- /dev/null
+#!/bin/bash
+set -x
+
+PATH=$(pwd)/ci/build/bin:$PATH
+
+if [ ! -d ci ]; then
+ echo "you must run this in the root fping directory" >&2
+ exit 1
+fi
+
+autoreconf -i
+./configure --enable-ipv4 --enable-ipv6 --enable-safe-limits --prefix=/opt/fping
+make CFLAGS="-g -O0 -fprofile-arcs -ftest-coverage"
+## setcap currently doesn't work anymore on travis-ci
+#sudo setcap cap_net_raw+ep src/fping
+## setcap debugging:
+#pwd
+#df -k .
+#which setcap
+#uname -a
+#mount
+#
+#sudo apt-get install strace
+#sudo strace setcap cap_net_raw+ep src/fping
+
+# use setuid, since setcap is not available
+sudo chown root src/fping
+sudo chmod u+s src/fping
--- /dev/null
+#!/bin/bash
+
+# upload to bintray.com/schweikert
+
+set -e
+
+VERSION=$(ls fping-*.tar.gz | sed -e 's/^fping-//' | sed -e 's/\.tar\.gz$//')
+if [[ "$VERSION" =~ ^[0-9]+\.[0-9]+$ ]]; then
+ REPO=release
+else
+ REPO=beta
+fi
+curl -T fping-$VERSION.tar.gz -uschweikert:$BINTRAY_API_KEY https://api.bintray.com/content/schweikert/$REPO/fping/$VERSION/fping-$VERSION.tar.gz
+echo
--- /dev/null
+#!/bin/sh
+
+set -xe
+
+if [ "$TRAVIS_DIST" = "trusty" ]; then
+ echo "skip coveralls on trusty because coveralls errors out due to python issues"
+ exit 0
+elif [ "$TRAVIS_OS_NAME" = "osx" ]; then
+ pip3 install --user cpp-coveralls
+ PATH="${PATH}:$(python3 -c 'import site; print(site.USER_BASE)')/bin"
+else
+ pip install --user cpp-coveralls
+fi
+
+export COVERALLS_PARALLEL=true
+coveralls --build-root src --exclude src/optparse.c --exclude ci --exclude config.h --gcov-options '\-lp'
--- /dev/null
+#!/bin/sh
+
+set -e
+
+COVERITY_SCAN_PROJECT_NAME=schweikert/fping
+COVERITY_SCAN_EMAIL=david@schweikert.ch
+
+if [ -z "$COVERITY_SCAN_TOKEN" ]; then
+ echo "ERROR: COVERITY_SCAN_TOKEN not defined." >&2
+ exit 1
+fi
+
+curl -o /tmp/cov-analysis-linux64.tgz https://scan.coverity.com/download/linux64 \
+ --form project=$COVERITY_SCAN_PROJECT_NAME --form token=$COVERITY_SCAN_TOKEN
+tar xfz /tmp/cov-analysis-linux64.tgz
+./autogen.sh
+./configure --enable-ipv4 --enable-ipv6 --enable-safe-limits --prefix=/opt/fping
+cov-analysis-linux64-*/bin/cov-build --dir cov-int make -j4
+tar cfz cov-int.tar.gz cov-int
+curl https://scan.coverity.com/builds?project=$COVERITY_SCAN_PROJECT_NAME \
+ --form token=$COVERITY_SCAN_TOKEN \
+ --form email=$COVERITY_SCAN_EMAIL \
+ --form file=@cov-int.tar.gz \
+ --form version="`git rev-parse --short HEAD`" \
+ --form description="`git rev-parse --short HEAD` / $TRAVIS_BUILD_ID "
--- /dev/null
+#!/bin/bash
+
+sudo setcap cap_net_raw,cap_net_admin+ep src/fping
+
+if [[ ! $PATH =~ fping/src ]]; then
+ PATH=/home/dws/checkouts/fping/src:$PATH
+fi
--- /dev/null
+#!/bin/sh
+
+lcov -c -no-external \
+ -d . \
+ -b src \
+ -o lcov-all.info
+
+lcov --remove lcov-all.info -o lcov.info \
+ '*/optparse.c'
--- /dev/null
+#!/bin/sh
+
+set -ex
+
+PATH=`pwd`/src:$PATH
+
+prove ci/test-*.pl
+ci/test-tarball.sh
--- /dev/null
+#!/usr/bin/perl -w
+
+use Test::Command tests => 15;
+use Test::More;
+
+# ping 127.0.0.1
+{
+ my $cmd = Test::Command->new(cmd => "fping 127.0.0.1");
+ $cmd->exit_is_num(0);
+ $cmd->stdout_is_eq("127.0.0.1 is alive\n");
+ $cmd->stderr_is_eq("");
+}
+
+# ping ::1
+SKIP: {
+ #system("/sbin/ifconfig >&2");
+ if($ENV{SKIP_IPV6}) {
+ skip 'Skip IPv6 tests', 3;
+ }
+ my $cmd = Test::Command->new(cmd => "fping ::1");
+ $cmd->exit_is_num(0);
+ $cmd->stdout_is_eq("::1 is alive\n");
+ $cmd->stderr_is_eq("");
+}
+
+# ping ff02::1
+SKIP: {
+ #system("/sbin/ifconfig >&2");
+ if($ENV{SKIP_IPV6}) {
+ skip 'Skip IPv6 tests', 3;
+ }
+ my $cmd = Test::Command->new(cmd => "fping ff02::1");
+ $cmd->exit_is_num(0);
+ $cmd->stdout_is_eq("ff02::1 is alive\n");
+ $cmd->stderr_like(qr{ \[<- .*\]});
+}
+
+# ping 3 times 127.0.0.1
+{
+ my $cmd = Test::Command->new(cmd => "fping -p 100 -C3 127.0.0.1");
+ $cmd->exit_is_num(0);
+ $cmd->stdout_like(qr{127\.0\.0\.1 : \[0\], 64 bytes, \d\.\d+ ms \(\d\.\d+ avg, 0% loss\)
+127\.0\.0\.1 : \[1\], 64 bytes, \d\.\d+ ms \(\d\.\d+ avg, 0% loss\)
+127\.0\.0\.1 : \[2\], 64 bytes, \d\.\d+ ms \(\d\.\d+ avg, 0% loss\)
+});
+ $cmd->stderr_like(qr{127\.0\.0\.1 : \d\.\d+ \d\.\d+ \d\.\d+\n});
+}
+
+# invalid target name
+{
+ my $cmd = Test::Command->new(cmd => "fping host.name.invalid");
+ $cmd->exit_is_num(2);
+ $cmd->stdout_is_eq("");
+ $cmd->stderr_like(qr{host\.name\.invalid: .+\n});
+}
--- /dev/null
+#!/usr/bin/perl -w
+
+use Test::Command tests => 15;
+
+my $I_HELP = " -I, --iface=IFACE bind to a particular interface\n";
+$I_HELP = '' if $^O eq 'darwin';
+
+# fping -h (special pre-parse code path)
+my $cmd1 = Test::Command->new(cmd => "fping -h");
+$cmd1->exit_is_num(0);
+$cmd1->stdout_like(qr{Usage: fping \[options\] \[targets\.\.\.\]
+
+Probing options:
+.*
+ -v, --version show version
+}s);
+$cmd1->stderr_is_eq("");
+
+# fping -4 -h (normal option parsing code path)
+my $cmd4 = Test::Command->new(cmd => "fping -4 -h");
+$cmd4->exit_is_num(0);
+$cmd4->stdout_like(qr{Usage: fping \[options\] \[targets\.\.\.\]
+
+Probing options:
+.*
+ -v, --version show version
+}s);
+$cmd4->stderr_is_eq("");
+
+# fping -v
+my $cmd2 = Test::Command->new(cmd => "fping -v");
+$cmd2->exit_is_num(0);
+$cmd2->stdout_like(qr{fping: Version \S+});
+$cmd2->stderr_is_eq("");
+
+# fping with unknown option
+my $cmd3 = Test::Command->new(cmd => "fping -Z");
+$cmd3->exit_is_num(1);
+$cmd3->stdout_is_eq("");
+$cmd3->stderr_like(qr{^fping: (illegal|invalid) option -- '?Z'?\nsee 'fping -h' for usage information\n$});
+
+# fping with unknown long option
+my $cmd5 = Test::Command->new(cmd => "fping --unknown-long-option");
+$cmd5->exit_is_num(1);
+$cmd5->stdout_is_eq("");
+$cmd5->stderr_like(qr{^fping: (illegal|invalid) option -- '?unknown-long-option'?\nsee 'fping -h' for usage information\n$});
--- /dev/null
+#!/usr/bin/perl -w
+
+use Test::Command tests => 36;
+
+# fping -i 0
+my $cmd1 = Test::Command->new(cmd => "fping -i 0 -T10 -g 127.0.0.1/29");
+$cmd1->exit_is_num(1);
+$cmd1->stdout_is_eq("");
+$cmd1->stderr_is_eq(<<END);
+fping: -i must be >= 1
+END
+
+# fping -p 9
+my $cmd2 = Test::Command->new(cmd => "fping -c3 -p 9 127.0.0.1");
+$cmd2->exit_is_num(1);
+$cmd2->stdout_is_eq("");
+$cmd2->stderr_is_eq(<<END);
+fping: -p must be >= 10
+END
+
+# fping -H 300
+my $cmd5 = Test::Command->new(cmd => "fping -H 300 127.0.0.1");
+$cmd5->exit_is_num(1);
+$cmd5->stdout_is_eq("");
+$cmd5->stderr_is_eq("fping: ttl 300 out of range\n");
+
+# fping -a -u
+my $cmd6 = Test::Command->new(cmd => "fping -a -u 127.0.0.1");
+$cmd6->exit_is_num(1);
+$cmd6->stdout_is_eq("");
+$cmd6->stderr_is_eq("fping: specify only one of a, u\n");
+
+# fping -c -l
+my $cmd7 = Test::Command->new(cmd => "fping -c3 -l 127.0.0.1");
+$cmd7->exit_is_num(1);
+$cmd7->stdout_is_eq("");
+$cmd7->stderr_is_eq("fping: specify only one of c, l\n");
+
+# fping -b 65508
+my $cmd8 = Test::Command->new(cmd => "fping -b 65508 127.0.0.1");
+$cmd8->exit_is_num(1);
+$cmd8->stdout_is_eq("");
+$cmd8->stderr_is_eq("fping: data size 65508 not valid, must not be larger than 65507\n");
+
+# fping -B 0.9
+my $cmd9 = Test::Command->new(cmd => "fping -B 0.9 127.0.0.1");
+$cmd9->exit_is_num(1);
+$cmd9->stdout_is_eq("");
+$cmd9->stderr_is_eq("fping: backoff factor 0.9 not valid, must be between 1.0 and 5.0\n");
+
+# fping -B 5.1
+my $cmd10 = Test::Command->new(cmd => "fping -B 5.1 127.0.0.1");
+$cmd10->exit_is_num(1);
+$cmd10->stdout_is_eq("");
+$cmd10->stderr_is_eq("fping: backoff factor 5.1 not valid, must be between 1.0 and 5.0\n");
+
+# non-negative only
+for my $arg (qw(i p Q t)) {
+ my $cmd = Test::Command->new(cmd => "fping -$arg -1");
+ $cmd->exit_is_num(1);
+ $cmd->stdout_is_eq("");
+ $cmd->stderr_like(qr{Usage:});
+}
--- /dev/null
+#!/usr/bin/perl -w
+
+use Test::Command tests => 68;
+use Test::More;
+use Time::HiRes qw(gettimeofday tv_interval);
+
+# -4 only use IPv4 addresses
+# -6 only use IPv6 addresses
+# -a show targets that are alive
+# -A show targets by address
+# -b n amount of ping data to send, in bytes (default 56)
+# -B f set exponential backoff factor to f
+
+# fping -4 -6
+{
+my $cmd = Test::Command->new(cmd => "fping -4 -6 127.0.0.1");
+$cmd->exit_is_num(1);
+$cmd->stdout_is_eq("");
+$cmd->stderr_is_eq("fping: can't specify both -4 and -6\n");
+}
+
+# fping -6 -4
+{
+my $cmd = Test::Command->new(cmd => "fping -6 -4 127.0.0.1");
+$cmd->exit_is_num(1);
+$cmd->stdout_is_eq("");
+$cmd->stderr_is_eq("fping: can't specify both -4 and -6\n");
+}
+
+# fping -4
+{
+my $cmd = Test::Command->new(cmd => "fping -4 127.0.0.1");
+$cmd->exit_is_num(0);
+$cmd->stdout_is_eq("127.0.0.1 is alive\n");
+$cmd->stderr_is_eq("");
+}
+
+{
+my $cmd = Test::Command->new(cmd => "fping -4 ::1");
+$cmd->exit_is_num(2);
+$cmd->stdout_is_eq("");
+$cmd->stderr_like(qr{^::1:.*(not supported|not known)});
+}
+
+# fping -6
+SKIP: {
+ if($ENV{SKIP_IPV6}) {
+ skip 'Skip IPv6 tests', 3;
+ }
+ my $cmd = Test::Command->new(cmd => "fping -6 ::1");
+ $cmd->exit_is_num(0);
+ $cmd->stdout_is_eq("::1 is alive\n");
+ $cmd->stderr_is_eq("");
+}
+
+{
+my $cmd = Test::Command->new(cmd => "fping -6 127.0.0.1");
+$cmd->exit_is_num(2);
+$cmd->stdout_is_eq("");
+$cmd->stderr_like(qr{127\.0\.0\.1:.*(not supported|not known)});
+}
+
+# fping -a
+{
+my $cmd = Test::Command->new(cmd => "fping -a 127.0.0.1 127.0.0.2");
+$cmd->exit_is_num(0);
+$cmd->stdout_is_eq("127.0.0.1\n127.0.0.2\n");
+$cmd->stderr_is_eq("");
+}
+
+# fping -a --print-ttl
+{
+my $cmd = Test::Command->new(cmd => "fping -a --print-ttl 127.0.0.1 127.0.0.2");
+$cmd->exit_is_num(0);
+$cmd->stdout_like(qr{127\.0\.0\.1 \(TTL \d+\)\n127\.0\.0\.2 \(TTL \d+\)\n});
+$cmd->stderr_is_eq("");
+}
+
+# fping -a --icmp-timestamp
+SKIP: {
+if($^O eq 'darwin') {
+ skip 'On macOS, this test is unreliable', 3;
+}
+my $cmd = Test::Command->new(cmd => "fping -a --icmp-timestamp 127.0.0.1");
+$cmd->exit_is_num(0);
+$cmd->stdout_like(qr{127\.0\.0\.1 timestamps: Originate=\d+ Receive=\d+ Transmit=\d+ Localreceive=\d+});
+$cmd->stderr_is_eq("");
+}
+
+# fping --print-ttl
+{
+my $cmd = Test::Command->new(cmd => "fping --print-ttl 127.0.0.1");
+$cmd->exit_is_num(0);
+$cmd->stdout_like(qr{127\.0\.0\.1 is alive \(TTL \d+\)});
+$cmd->stderr_is_eq("");
+}
+
+# fping --icmp-timestamp
+SKIP: {
+if($^O eq 'darwin') {
+ skip 'On macOS, this test is unreliable', 3;
+}
+my $cmd = Test::Command->new(cmd => "fping --icmp-timestamp 127.0.0.1");
+$cmd->exit_is_num(0);
+$cmd->stdout_like(qr{127\.0\.0\.1 is alive, timestamps: Originate=\d+ Receive=\d+ Transmit=\d+ Localreceive=\d+});
+$cmd->stderr_is_eq("");
+}
+
+# fping --icmp-timestamp --print-tos --print-ttl -e
+SKIP: {
+if($^O eq 'darwin') {
+ skip 'On macOS, this test is unreliable', 3;
+}
+my $cmd = Test::Command->new(cmd => "fping --icmp-timestamp --print-tos --print-ttl -e 127.0.0.1");
+$cmd->exit_is_num(0);
+$cmd->stdout_like(qr{127\.0\.0\.1 is alive, timestamps: Originate=\d+ Receive=\d+ Transmit=\d+ Localreceive=\d+ \(TOS \d+\) \(TTL \d+\) \(\d+(\.\d*)? ms\)});
+$cmd->stderr_is_eq("");
+}
+
+# fping --icmp-timestamp ::1
+SKIP: {
+ if($ENV{SKIP_IPV6}) {
+ skip 'Skip IPv6 tests', 3;
+ }
+ my $cmd = Test::Command->new(cmd => "fping --icmp-timestamp ::1");
+ $cmd->exit_is_num(2);
+ $cmd->stdout_is_eq("");
+ $cmd->stderr_like(qr{^::1:.*(not supported|not known)});
+}
+
+# fping --print-ttl with IPv6
+SKIP: {
+ if($ENV{SKIP_IPV6}) {
+ skip 'Skip IPv6 tests', 3;
+ }
+ my $cmd = Test::Command->new(cmd => "fping --print-ttl ::1");
+ $cmd->exit_is_num(0);
+ $cmd->stdout_like(qr{::1 is alive \(TTL unknown\)\n});
+ $cmd->stderr_is_eq("");
+}
+
+# fping -A
+{
+my $cmd = Test::Command->new(cmd => "fping -4 -A localhost");
+$cmd->exit_is_num(0);
+$cmd->stdout_is_eq("127.0.0.1 is alive\n");
+$cmd->stderr_is_eq("");
+}
+
+# fping -b
+{
+my $cmd = Test::Command->new(cmd => "fping -b 1000 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 --icmp-timestamp -b
+{
+my $cmd = Test::Command->new(cmd => "fping --icmp-timestamp -b 1000 127.0.0.1");
+$cmd->exit_is_num(1);
+$cmd->stdout_is_eq("");
+$cmd->stderr_like(qr{cannot change ICMP Timestamp size});
+}
+
+# fping -b --icmp-timestamp
+{
+my $cmd = Test::Command->new(cmd => "fping -b 1000 --icmp-timestamp 127.0.0.1");
+$cmd->exit_is_num(1);
+$cmd->stdout_is_eq("");
+$cmd->stderr_like(qr{cannot change ICMP Timestamp size});
+}
+
+# fping --icmp-timestamp -b 12 (ICMP Timestamp data size)
+{
+my $cmd = Test::Command->new(cmd => "fping --icmp-timestamp -b 12 127.0.0.1");
+$cmd->exit_is_num(1);
+$cmd->stdout_is_eq("");
+$cmd->stderr_like(qr{cannot change ICMP Timestamp size});
+}
+
+# fping -6 --icmp-timestamp
+SKIP: {
+ if($ENV{SKIP_IPV6}) {
+ skip 'Skip IPv6 tests', 3;
+ }
+ my $cmd = Test::Command->new(cmd => "fping -6 --icmp-timestamp ::1");
+ $cmd->exit_is_num(1);
+ $cmd->stdout_is_eq("");
+ $cmd->stderr_like(qr{ICMP Timestamp is IPv4 only});
+}
+
+# fping --icmp-timestamp -6
+SKIP: {
+ if($ENV{SKIP_IPV6}) {
+ skip 'Skip IPv6 tests', 3;
+ }
+ my $cmd = Test::Command->new(cmd => "fping --icmp-timestamp -6 ::1");
+ $cmd->exit_is_num(1);
+ $cmd->stdout_is_eq("");
+ $cmd->stderr_is_eq("fping: can't specify both -4 and -6\n");
+}
+
+# fping -B
+SKIP: {
+ if($^O eq 'darwin') {
+ skip 'timing test not reliable on macOS', 5;
+ }
+ my $t0 = [gettimeofday];
+ my $cmd = Test::Command->new(cmd => "fping -t 100 -r 3 -B 2 8.8.8.7");
+ $cmd->exit_is_num(1);
+ $cmd->stdout_is_eq("8.8.8.7 is unreachable\n");
+ $cmd->stderr_like(qr{^(|(8.8.8.7: error while sending ping: No route to host\n)+)$});
+ my $elapsed = tv_interval($t0);
+ # 0.1 + 0.2 + 0.4 + 0.8 = 1.5
+ cmp_ok($elapsed, '>=', 1.5);
+ cmp_ok($elapsed, '<', 1.9);
+}
--- /dev/null
+#!/usr/bin/perl -w
+
+use Test::Command tests => 87;
+use Test::More;
+
+# -c n count of pings to send to each target (default 1)
+# -C n same as -c, report results in verbose format
+# --check-source discard replies not from target address
+# -d reverse name lookup
+# -D print timestamp before each output line
+# -e show elapsed time on return packets
+
+# fping -c n
+{
+my $cmd = Test::Command->new(cmd => "fping -4 -c 2 -p 100 localhost 127.0.0.1");
+$cmd->exit_is_num(0);
+$cmd->stdout_like(qr{localhost : \[0\], 64 bytes, \d\.\d+ ms \(\d\.\d+ avg, 0% loss\)
+127\.0\.0\.1 : \[0\], 64 bytes, \d\.\d+ ms \(\d\.\d+ avg, 0% loss\)
+localhost : \[1\], 64 bytes, \d\.\d+ ms \(\d\.\d+ avg, 0% loss\)
+127\.0\.0\.1 : \[1\], 64 bytes, \d\.\d+ ms \(\d\.\d+ avg, 0% loss\)
+});
+
+$cmd->stderr_like(qr{localhost : xmt/rcv/%loss = 2/2/0%, min/avg/max = \d\.\d+/\d\.\d+/\d\.\d+
+127\.0\.0\.1 : xmt/rcv/%loss = 2/2/0%, min/avg/max = \d\.\d+/\d\.\d+/\d\.\d+
+});
+}
+
+# fping -c n --print-tos --print-ttl
+{
+my $cmd = Test::Command->new(cmd => "fping -4 -c 2 -p 100 --print-tos --print-ttl localhost 127.0.0.1");
+$cmd->exit_is_num(0);
+$cmd->stdout_like(qr{localhost : \[0\], 64 bytes, \d\.\d+ ms \(\d\.\d+ avg, 0% loss\) \(TOS \d+\) \(TTL \d+\)
+127\.0\.0\.1 : \[0\], 64 bytes, \d\.\d+ ms \(\d\.\d+ avg, 0% loss\) \(TOS \d+\) \(TTL \d+\)
+localhost : \[1\], 64 bytes, \d\.\d+ ms \(\d\.\d+ avg, 0% loss\) \(TOS \d+\) \(TTL \d+\)
+127\.0\.0\.1 : \[1\], 64 bytes, \d\.\d+ ms \(\d\.\d+ avg, 0% loss\) \(TOS \d+\) \(TTL \d+\)
+});
+
+$cmd->stderr_like(qr{localhost : xmt/rcv/%loss = 2/2/0%, min/avg/max = \d\.\d+/\d\.\d+/\d\.\d+
+127\.0\.0\.1 : xmt/rcv/%loss = 2/2/0%, min/avg/max = \d\.\d+/\d\.\d+/\d\.\d+
+});
+}
+
+# fping -c n -q
+{
+my $cmd = Test::Command->new(cmd => "fping -q -c 2 -p 100 localhost 127.0.0.1");
+$cmd->exit_is_num(0);
+$cmd->stdout_is_eq("");
+$cmd->stderr_like(qr{localhost : xmt/rcv/%loss = 2/2/0%, min/avg/max = \d\.\d+/\d\.\d+/\d\.\d+
+127\.0\.0\.1 : xmt/rcv/%loss = 2/2/0%, min/avg/max = \d\.\d+/\d\.\d+/\d\.\d+
+});
+}
+
+# fping -c n -a (-a is ignored)
+{
+my $cmd = Test::Command->new(cmd => "fping -a -c 2 -p 100 localhost 127.0.0.1");
+$cmd->exit_is_num(0);
+$cmd->stdout_like(qr{localhost : \[0\], 64 bytes, \d\.\d+ ms \(\d\.\d+ avg, 0% loss\)
+127\.0\.0\.1 : \[0\], 64 bytes, \d\.\d+ ms \(\d\.\d+ avg, 0% loss\)
+localhost : \[1\], 64 bytes, \d\.\d+ ms \(\d\.\d+ avg, 0% loss\)
+127\.0\.0\.1 : \[1\], 64 bytes, \d\.\d+ ms \(\d\.\d+ avg, 0% loss\)
+});
+
+$cmd->stderr_like(qr{localhost : xmt/rcv/%loss = 2/2/0%, min/avg/max = \d\.\d+/\d\.\d+/\d\.\d+
+127\.0\.0\.1 : xmt/rcv/%loss = 2/2/0%, min/avg/max = \d\.\d+/\d\.\d+/\d\.\d+
+});
+}
+
+# fping -c n -u (-u is ignored)
+{
+my $cmd = Test::Command->new(cmd => "fping -u -c 2 -p 100 localhost 127.0.0.1");
+$cmd->exit_is_num(0);
+$cmd->stdout_like(qr{localhost : \[0\], 64 bytes, \d\.\d+ ms \(\d\.\d+ avg, 0% loss\)
+127\.0\.0\.1 : \[0\], 64 bytes, \d\.\d+ ms \(\d\.\d+ avg, 0% loss\)
+localhost : \[1\], 64 bytes, \d\.\d+ ms \(\d\.\d+ avg, 0% loss\)
+127\.0\.0\.1 : \[1\], 64 bytes, \d\.\d+ ms \(\d\.\d+ avg, 0% loss\)
+});
+
+$cmd->stderr_like(qr{localhost : xmt/rcv/%loss = 2/2/0%, min/avg/max = \d\.\d+/\d\.\d+/\d\.\d+
+127\.0\.0\.1 : xmt/rcv/%loss = 2/2/0%, min/avg/max = \d\.\d+/\d\.\d+/\d\.\d+
+});
+}
+
+# fping -c n ff02::1
+SKIP: {
+ if($ENV{SKIP_IPV6}) {
+ skip 'Skip IPv6 tests', 3;
+ }
+ my $cmd = Test::Command->new(cmd => "fping -c 1 ff02::1");
+ $cmd->exit_is_num(0);
+ $cmd->stdout_like(qr{ff02::1 : \[0\], 64 bytes, \d\.\d+ ms \(\d\.\d+ avg, 0% loss\)\n});
+ $cmd->stderr_like(qr{ \[<- .*\]
+ff02::1 : xmt/rcv/%loss = 1/1/0%, min/avg/max = \d\.\d+/\d\.\d+/\d\.\d+\n});
+}
+
+# fping --icmp-timestamp -c n 127.0.0.1
+SKIP: {
+if($^O eq 'darwin') {
+ skip 'On macOS, this test is unreliable', 3;
+}
+my $cmd = Test::Command->new(cmd => "fping -4 --icmp-timestamp -c 2 127.0.0.1");
+$cmd->exit_is_num(0);
+$cmd->stdout_like(qr{127\.0\.0\.1 : \[0\], 20 bytes, \d\.\d+ ms \(\d\.\d+ avg, 0% loss\), timestamps: Originate=\d+ Receive=\d+ Transmit=\d+ Localreceive=\d+
+127\.0\.0\.1 : \[1\], 20 bytes, \d\.\d+ ms \(\d\.\d+ avg, 0% loss\), timestamps: Originate=\d+ Receive=\d+ Transmit=\d+ Localreceive=\d+
+});
+
+$cmd->stderr_like(qr{127\.0\.0\.1 : xmt/rcv/%loss = 2/2/0%, min/avg/max = \d\.\d+/\d\.\d+/\d\.\d+
+});
+}
+
+# fping --icmp-timestamp --print-tos --print-ttl -c n 127.0.0.1
+SKIP: {
+if($^O eq 'darwin') {
+ skip 'On macOS, this test is unreliable', 3;
+}
+my $cmd = Test::Command->new(cmd => "fping -4 --icmp-timestamp --print-tos --print-ttl -p 100 -c 2 127.0.0.1");
+$cmd->exit_is_num(0);
+$cmd->stdout_like(qr{127\.0\.0\.1 : \[0\], 20 bytes, \d\.\d+ ms \(\d\.\d+ avg, 0% loss\), timestamps: Originate=\d+ Receive=\d+ Transmit=\d+ Localreceive=\d+ \(TOS \d+\) \(TTL \d+\)
+127\.0\.0\.1 : \[1\], 20 bytes, \d\.\d+ ms \(\d\.\d+ avg, 0% loss\), timestamps: Originate=\d+ Receive=\d+ Transmit=\d+ Localreceive=\d+ \(TOS \d+\) \(TTL \d+\)
+});
+
+$cmd->stderr_like(qr{127\.0\.0\.1 : xmt/rcv/%loss = 2/2/0%, min/avg/max = \d\.\d+/\d\.\d+/\d\.\d+
+});
+}
+
+# fping -C n
+{
+my $cmd = Test::Command->new(cmd => "fping -4 -C 2 -p 100 localhost 127.0.0.1");
+$cmd->exit_is_num(0);
+$cmd->stdout_like(qr{localhost : \[0\], 64 bytes, \d\.\d+ ms \(\d\.\d+ avg, 0% loss\)
+127\.0\.0\.1 : \[0\], 64 bytes, \d\.\d+ ms \(\d\.\d+ avg, 0% loss\)
+localhost : \[1\], 64 bytes, \d\.\d+ ms \(\d\.\d+ avg, 0% loss\)
+127\.0\.0\.1 : \[1\], 64 bytes, \d\.\d+ ms \(\d\.\d+ avg, 0% loss\)
+});
+
+$cmd->stderr_like(qr{localhost : \d\.\d+ \d\.\d+
+127\.0\.0\.1 : \d\.\d+ \d\.\d+
+});
+}
+
+# fping -C n --print-tos --print-ttl
+{
+my $cmd = Test::Command->new(cmd => "fping -4 -C 2 -p 100 --print-tos --print-ttl localhost 127.0.0.1");
+$cmd->exit_is_num(0);
+$cmd->stdout_like(qr{localhost : \[0\], 64 bytes, \d\.\d+ ms \(\d\.\d+ avg, 0% loss\) \(TOS \d+\) \(TTL \d+\)
+127\.0\.0\.1 : \[0\], 64 bytes, \d\.\d+ ms \(\d\.\d+ avg, 0% loss\) \(TOS \d+\) \(TTL \d+\)
+localhost : \[1\], 64 bytes, \d\.\d+ ms \(\d\.\d+ avg, 0% loss\) \(TOS \d+\) \(TTL \d+\)
+127\.0\.0\.1 : \[1\], 64 bytes, \d\.\d+ ms \(\d\.\d+ avg, 0% loss\) \(TOS \d+\) \(TTL \d+\)
+});
+
+$cmd->stderr_like(qr{localhost : \d\.\d+ \d\.\d+
+127\.0\.0\.1 : \d\.\d+ \d\.\d+
+});
+}
+
+# fping -C n -q
+{
+my $cmd = Test::Command->new(cmd => "fping -C 5 -q -p 100 localhost");
+$cmd->exit_is_num(0);
+$cmd->stdout_is_eq("");
+$cmd->stderr_like(qr{localhost :( \d\.\d+){5}
+});
+}
+
+# fping -C n -a (-a is ignored)
+{
+my $cmd = Test::Command->new(cmd => "fping -a -C 2 -p 100 localhost 127.0.0.1");
+$cmd->exit_is_num(0);
+$cmd->stdout_like(qr{localhost : \[0\], 64 bytes, \d\.\d+ ms \(\d\.\d+ avg, 0% loss\)
+127\.0\.0\.1 : \[0\], 64 bytes, \d\.\d+ ms \(\d\.\d+ avg, 0% loss\)
+localhost : \[1\], 64 bytes, \d\.\d+ ms \(\d\.\d+ avg, 0% loss\)
+127\.0\.0\.1 : \[1\], 64 bytes, \d\.\d+ ms \(\d\.\d+ avg, 0% loss\)
+});
+
+$cmd->stderr_like(qr{localhost : \d\.\d+ \d\.\d+
+127\.0\.0\.1 : \d\.\d+ \d\.\d+
+});
+}
+
+# fping -C n -u (-u is ignored)
+{
+my $cmd = Test::Command->new(cmd => "fping -u -C 2 -p 100 localhost 127.0.0.1");
+$cmd->exit_is_num(0);
+$cmd->stdout_like(qr{localhost : \[0\], 64 bytes, \d\.\d+ ms \(\d\.\d+ avg, 0% loss\)
+127\.0\.0\.1 : \[0\], 64 bytes, \d\.\d+ ms \(\d\.\d+ avg, 0% loss\)
+localhost : \[1\], 64 bytes, \d\.\d+ ms \(\d\.\d+ avg, 0% loss\)
+127\.0\.0\.1 : \[1\], 64 bytes, \d\.\d+ ms \(\d\.\d+ avg, 0% loss\)
+});
+
+$cmd->stderr_like(qr{localhost : \d\.\d+ \d\.\d+
+127\.0\.0\.1 : \d\.\d+ \d\.\d+
+});
+}
+
+# fping -C n -i -q
+{
+my $cmd = Test::Command->new(cmd => "fping --quiet --interval=1 --vcount=20 --period=50 127.0.0.1 127.0.0.2");
+$cmd->exit_is_num(0);
+$cmd->stdout_is_eq("");
+$cmd->stderr_like(qr{127\.0\.0\.1 :( \d\.\d+){20}
+127\.0\.0\.2 :( \d\.\d+){20}
+});
+}
+
+# fping -C n ff02::1
+SKIP: {
+ if($ENV{SKIP_IPV6}) {
+ skip 'Skip IPv6 tests', 3;
+ }
+ my $cmd = Test::Command->new(cmd => "fping -C 1 ff02::1");
+ $cmd->exit_is_num(0);
+ $cmd->stdout_like(qr{ff02::1 : \[0\], 64 bytes, \d\.\d+ ms \(\d\.\d+ avg, 0% loss\)\n});
+ $cmd->stderr_like(qr{ \[<- .*\]\nff02::1 : \d\.\d+\n});
+}
+
+# fping --check-source
+{
+my $cmd = Test::Command->new(cmd => "fping --check-source 127.0.0.1 127.0.0.2");
+$cmd->exit_is_num(0);
+$cmd->stdout_is_eq("127.0.0.1 is alive\n127.0.0.2 is alive\n");
+$cmd->stderr_is_eq("");
+}
+
+# fping --check-source (to IPv6 multicast address -> accept no reply)
+SKIP: {
+ if($ENV{SKIP_IPV6}) {
+ skip 'Skip IPv6 tests', 3;
+ }
+ my $cmd = Test::Command->new(cmd => "fping --check-source ff02::1");
+ $cmd->exit_is_num(1);
+ $cmd->stdout_is_eq("ff02::1 is unreachable\n");
+ $cmd->stderr_is_eq("");
+}
+
+# fping -c N --check-source
+SKIP: {
+ if($ENV{SKIP_IPV6}) {
+ skip 'Skip IPv6 tests', 3;
+ }
+ my $cmd = Test::Command->new(cmd => "fping -c1 --check-source 127.0.0.1 ff02::1");
+ $cmd->exit_is_num(1);
+ $cmd->stdout_like(qr{127\.0\.0\.1 : \[0\], 64 bytes, \d\.\d+ ms \(\d\.\d+ avg, 0% loss\)
+ff02::1 : \[0\], timed out \(NaN avg, 100% loss\)
+});
+ $cmd->stderr_like(qr{
+127\.0\.0\.1 : xmt/rcv/%loss = 1/1/0%, min/avg/max = \d\.\d+/\d\.\d+/\d\.\d+
+ff02::1 : xmt/rcv/%loss = 1/0/100%
+});
+}
+
+# fping -C N --check-source
+SKIP: {
+ if($ENV{SKIP_IPV6}) {
+ skip 'Skip IPv6 tests', 3;
+ }
+ my $cmd = Test::Command->new(cmd => "fping -C1 --check-source 127.0.0.1 ff02::1");
+ $cmd->exit_is_num(1);
+ $cmd->stdout_like(qr{127\.0\.0\.1 : \[0\], 64 bytes, \d\.\d+ ms \(\d\.\d+ avg, 0% loss\)
+ff02::1 : \[0\], timed out \(NaN avg, 100% loss\)
+});
+ $cmd->stderr_like(qr{
+127\.0\.0\.1 : \d\.\d+
+ff02::1 : -
+});
+}
+
+# fping -d
+{
+my $cmd = Test::Command->new(cmd => "fping -d 127.0.0.1");
+$cmd->exit_is_num(0);
+$cmd->stdout_is_eq("localhost is alive\n");
+$cmd->stderr_is_eq("");
+}
+
+# fping -D
+{
+my $cmd = Test::Command->new(cmd => "fping -D -c 2 -p 100 127.0.0.1");
+$cmd->exit_is_num(0);
+$cmd->stdout_like(qr{\[\d+\.\d+\] 127\.0\.0\.1 : \[0\], 64 bytes, \d\.\d+ ms \(\d\.\d+ avg, 0% loss\)
+\[\d+\.\d+\] 127\.0\.0\.1 : \[1\], 64 bytes, \d\.\d+ ms \(\d\.\d+ avg, 0% loss\)
+});
+
+$cmd->stderr_like(qr{127\.0\.0\.1 : xmt/rcv/%loss = 2/2/0%, min/avg/max = \d\.\d+/\d\.\d+/\d\.\d+
+});
+}
+
+# fping -D (timestamp not before 2001-09-09)
+{
+my $cmd = Test::Command->new(cmd => "fping -D -c 2 -p 100 127.0.0.1");
+$cmd->exit_is_num(0);
+$cmd->stdout_like(qr{\[[1-9]\d{9,}\.\d+\] 127\.0\.0\.1 : \[0\], 64 bytes, \d\.\d+ ms \(\d\.\d+ avg, 0% loss\)
+\[[1-9]\d{9,}\.\d+\] 127\.0\.0\.1 : \[1\], 64 bytes, \d\.\d+ ms \(\d\.\d+ avg, 0% loss\)
+});
+$cmd->stderr_like(qr{127\.0\.0\.1 : xmt/rcv/%loss = 2/2/0%, min/avg/max = \d\.\d+/\d\.\d+/\d\.\d+
+});
+}
+
+# fping -D --timestamp-format=ctime
+{
+my $cmd = Test::Command->new(cmd => "fping -D --timestamp-format=ctime -c 2 -p 100 127.0.0.1");
+$cmd->exit_is_num(0);
+$cmd->stdout_like(qr{\[\w+\s\w+\s+\d+\s[\d+:]+\s\d+\] 127\.0\.0\.1 : \[0\], 64 bytes, \d\.\d+ ms \(\d\.\d+ avg, 0% loss\)
+\[\w+\s\w+\s+\d+\s[\d+:]+\s\d+\] 127\.0\.0\.1 : \[1\], 64 bytes, \d\.\d+ ms \(\d\.\d+ avg, 0% loss\)
+});
+
+$cmd->stderr_like(qr{127\.0\.0\.1 : xmt/rcv/%loss = 2/2/0%, min/avg/max = \d\.\d+/\d\.\d+/\d\.\d+
+});
+}
+
+# fping -D --timestamp-format=iso
+{
+my $cmd = Test::Command->new(cmd => "fping -D --timestamp-format=iso -c 2 -p 100 127.0.0.1");
+$cmd->exit_is_num(0);
+$cmd->stdout_like(qr{\[[\d+-]+T[\d+:]+\+\d+\] 127\.0\.0\.1 : \[0\], 64 bytes, \d\.\d+ ms \(\d\.\d+ avg, 0% loss\)
+\[[\d+-]+T[\d+:]+\+\d+\] 127\.0\.0\.1 : \[1\], 64 bytes, \d\.\d+ ms \(\d\.\d+ avg, 0% loss\)
+});
+
+$cmd->stderr_like(qr{127\.0\.0\.1 : xmt/rcv/%loss = 2/2/0%, min/avg/max = \d\.\d+/\d\.\d+/\d\.\d+
+});
+}
+
+# fping -D --timestamp-format=rfc3339
+{
+my $cmd = Test::Command->new(cmd => "fping -D --timestamp-format=rfc3339 -c 2 -p 100 127.0.0.1");
+$cmd->exit_is_num(0);
+$cmd->stdout_like(qr{\[[\d+-]+\s[\d+:]+\] 127\.0\.0\.1 : \[0\], 64 bytes, \d\.\d+ ms \(\d\.\d+ avg, 0% loss\)
+\[[\d+-]+\s[\d+:]+\] 127\.0\.0\.1 : \[1\], 64 bytes, \d\.\d+ ms \(\d\.\d+ avg, 0% loss\)
+});
+
+$cmd->stderr_like(qr{127\.0\.0\.1 : xmt/rcv/%loss = 2/2/0%, min/avg/max = \d\.\d+/\d\.\d+/\d\.\d+
+});
+}
+
+# fping -D --timestamp-format
+{
+my $cmd = Test::Command->new(cmd => "fping -D --timestamp-format -c 2 -p 100 127.0.0.1");
+$cmd->exit_is_num(1);
+$cmd->stdout_is_eq("");
+$cmd->stderr_like(qr{Usage:});
+}
+
+# fping -D --timestamp-format="%Y-%m-%d %H:%M:%S"
+{
+my $cmd = Test::Command->new(cmd => "fping -D --timestamp-format=\"%Y-%m-%d %H:%M:%S\" -c 2 -p 100 127.0.0.1");
+$cmd->exit_is_num(1);
+$cmd->stdout_is_eq("");
+$cmd->stderr_like(qr{Usage:});
+}
+
+# fping -e
+{
+my $cmd = Test::Command->new(cmd => "fping -e 127.0.0.1");
+$cmd->exit_is_num(0);
+$cmd->stdout_like(qr{127\.0\.0\.1 is alive \(\d\.\d+ ms\)
+});
+$cmd->stderr_is_eq("");
+}
+
+# fping -e -a
+{
+my $cmd = Test::Command->new(cmd => "fping -e -a 127.0.0.1");
+$cmd->exit_is_num(0);
+$cmd->stdout_like(qr{127\.0\.0\.1 \(\d\.\d+ ms\)
+});
+$cmd->stderr_is_eq("");
+}
--- /dev/null
+#!/usr/bin/perl -w
+
+use Test::Command tests => 75;
+use Test::More;
+use File::Temp;
+
+# -f file read list of targets from a file ( - means stdin) (only if no -g specified)
+# -g generate target list (only if no -f specified)
+# (specify the start and end IP in the target list, or supply a IP netmask)
+# (ex. ../src/fping -g 192.168.1.0 192.168.1.255 or ../src/fping -g 192.168.1.0/24)
+# -H n Set the IP TTL value (Time To Live hops)
+
+my $tmpfile = File::Temp->new();
+print $tmpfile "127.0.0.1\n127.0.0.2\n";
+close($tmpfile);
+
+my $tmpfile2 = File::Temp->new();
+print $tmpfile2 "# comment\n127.0.0.1\n\n127.0.0.2\n";
+close($tmpfile2);
+
+# fping without option (-> equivalent to 'fping -f -')
+{
+my $cmd = Test::Command->new(cmd => "cat ".$tmpfile->filename." | fping");
+$cmd->exit_is_num(0);
+$cmd->stdout_is_eq("127.0.0.1 is alive\n127.0.0.2 is alive\n");
+$cmd->stderr_is_eq("");
+}
+
+# fping -f -
+{
+my $cmd = Test::Command->new(cmd => "cat ".$tmpfile->filename." | fping -f -");
+$cmd->exit_is_num(0);
+$cmd->stdout_is_eq("127.0.0.1 is alive\n127.0.0.2 is alive\n");
+$cmd->stderr_is_eq("");
+}
+
+# fping -f file
+{
+my $cmd = Test::Command->new(cmd => "fping -f ".$tmpfile->filename);
+$cmd->exit_is_num(0);
+$cmd->stdout_is_eq("127.0.0.1 is alive\n127.0.0.2 is alive\n");
+$cmd->stderr_is_eq("");
+}
+
+# fping -f file (with comment and empty line)
+{
+my $cmd = Test::Command->new(cmd => "fping -f ".$tmpfile2->filename);
+$cmd->exit_is_num(0);
+$cmd->stdout_is_eq("127.0.0.1 is alive\n127.0.0.2 is alive\n");
+$cmd->stderr_is_eq("");
+}
+
+# fping -f non-existing-file (error)
+{
+my $cmd = Test::Command->new(cmd => "fping -f file-does-not-exist");
+$cmd->exit_is_num(4);
+$cmd->stdout_is_eq("");
+$cmd->stderr_like(qr{: fopen :});
+}
+
+# fping -g (error: no argument)
+{
+my $cmd = Test::Command->new(cmd => "fping -g");
+$cmd->exit_is_num(1);
+$cmd->stdout_is_eq("");
+$cmd->stderr_like(qr{^Usage: fping \[options\] \[targets\.\.\.\]});
+}
+
+# fping -g (error: single argument, but not in cidr format)
+{
+my $cmd = Test::Command->new(cmd => "fping -g 127.0.0.1");
+$cmd->exit_is_num(1);
+$cmd->stdout_is_eq("");
+$cmd->stderr_like(qr{^Usage: fping \[options\] \[targets\.\.\.\]});
+}
+
+# fping -g (error: CIDR network is not an IP address)
+{
+my $cmd = Test::Command->new(cmd => "fping -g xxx/32");
+$cmd->exit_is_num(1);
+$cmd->stdout_is_eq("");
+$cmd->stderr_like(qr{can't parse address xxx});
+}
+
+# fping -g (error: start of range is not an IP address)
+{
+my $cmd = Test::Command->new(cmd => "fping -g xxx 127.0.0.1");
+$cmd->exit_is_num(1);
+$cmd->stdout_is_eq("");
+$cmd->stderr_like(qr{can't parse address xxx});
+}
+
+# fping -g (error: end of range is not an IP address)
+{
+my $cmd = Test::Command->new(cmd => "fping -g 127.0.0.1 yyy");
+$cmd->exit_is_num(1);
+$cmd->stdout_is_eq("");
+$cmd->stderr_like(qr{can't parse address yyy});
+}
+
+# fping -g (error: too many arguments)
+{
+my $cmd = Test::Command->new(cmd => "fping -g 127.0.0.1 127.0.0.2 127.0.0.3");
+$cmd->exit_is_num(1);
+$cmd->stdout_is_eq("");
+$cmd->stderr_like(qr{^Usage: fping \[options\] \[targets\.\.\.\]});
+}
+
+# fping -g (range)
+{
+my $cmd = Test::Command->new(cmd => "fping -g 127.0.0.1 127.0.0.5");
+$cmd->exit_is_num(0);
+$cmd->stdout_is_eq("127.0.0.1 is alive\n127.0.0.2 is alive\n127.0.0.3 is alive\n127.0.0.4 is alive\n127.0.0.5 is alive\n");
+$cmd->stderr_is_eq("");
+}
+
+# fping -g (empty range)
+{
+my $cmd = Test::Command->new(cmd => "fping -g 127.0.0.2 127.0.0.1");
+$cmd->exit_is_num(1);
+$cmd->stdout_is_eq("");
+$cmd->stderr_is_eq("");
+}
+
+# fping -g (too large range)
+{
+my $cmd = Test::Command->new(cmd => "fping -g 127.0.0.1 127.255.255.254");
+$cmd->exit_is_num(1);
+$cmd->stdout_is_eq("");
+$cmd->stderr_is_eq("fping: -g parameter generates too many addresses\n");
+}
+
+# fping -g (cidr)
+{
+my $cmd = Test::Command->new(cmd => "fping -g 127.0.0.1/30");
+$cmd->exit_is_num(0);
+$cmd->stdout_is_eq("127.0.0.1 is alive\n127.0.0.2 is alive\n");
+$cmd->stderr_is_eq("");
+}
+
+# fping -g (cidr - long prefixes: point-to-point)
+{
+my $cmd = Test::Command->new(cmd => "fping -g 127.0.0.2/31");
+$cmd->exit_is_num(0);
+$cmd->stdout_is_eq("127.0.0.2 is alive\n127.0.0.3 is alive\n");
+$cmd->stderr_is_eq("");
+}
+
+# fping -g (cidr - long prefixes: host)
+{
+my $cmd = Test::Command->new(cmd => "fping -g 127.0.0.2/32");
+$cmd->exit_is_num(0);
+$cmd->stdout_is_eq("127.0.0.2 is alive\n");
+$cmd->stderr_is_eq("");
+}
+
+# fping -g (cidr - too long prefixes)
+{
+my $cmd = Test::Command->new(cmd => "fping -g 127.0.0.2/33");
+$cmd->exit_is_num(1);
+$cmd->stdout_is_eq("");
+$cmd->stderr_is_eq("fping: netmask must be between 1 and 32 (is: 33)\n");
+}
+
+# fping -g (cidr - too short prefixes)
+{
+my $cmd = Test::Command->new(cmd => "fping -g 127.0.0.2/0");
+$cmd->exit_is_num(1);
+$cmd->stdout_is_eq("");
+$cmd->stderr_is_eq("fping: netmask must be between 1 and 32 (is: 0)\n");
+}
+
+# fping -g (cidr - too many addresses)
+{
+my $cmd = Test::Command->new(cmd => "fping -g 127.0.0.0/8");
+$cmd->exit_is_num(1);
+$cmd->stdout_is_eq("");
+$cmd->stderr_is_eq("fping: -g parameter generates too many addresses\n");
+}
+
+# fping -g (range - no IPv6 generator)
+SKIP: {
+ if($ENV{SKIP_IPV6}) {
+ skip 'Skip IPv6 tests', 3;
+ }
+ my $cmd = Test::Command->new(cmd => "fping -6 -g ::1 ::1");
+ $cmd->exit_is_num(1);
+ $cmd->stdout_is_eq("");
+ $cmd->stderr_is_eq("fping: -g works only with IPv4 addresses\n");
+}
+
+# fping -g (range - no IPv6 generator - start address IPv6)
+SKIP: {
+ if($ENV{SKIP_IPV6}) {
+ skip 'Skip IPv6 tests', 3;
+ }
+ my $cmd = Test::Command->new(cmd => "fping -6 -g ::1 127.0.0.1");
+ $cmd->exit_is_num(1);
+ $cmd->stdout_is_eq("");
+ $cmd->stderr_is_eq("fping: -g works only with IPv4 addresses\n");
+}
+
+# fping -g (range - no IPv6 generator - end address IPv6)
+SKIP: {
+ if($ENV{SKIP_IPV6}) {
+ skip 'Skip IPv6 tests', 3;
+ }
+ my $cmd = Test::Command->new(cmd => "fping -6 -g 127.0.0.1 ::1");
+ $cmd->exit_is_num(1);
+ $cmd->stdout_is_eq("");
+ $cmd->stderr_is_eq("fping: -g works only with IPv4 addresses\n");
+}
+
+# fping -g (CIDR - no IPv6 generator)
+SKIP: {
+ if($ENV{SKIP_IPV6}) {
+ skip 'Skip IPv6 tests', 3;
+ }
+ my $cmd = Test::Command->new(cmd => "fping -6 -g ::1/128");
+ $cmd->exit_is_num(1);
+ $cmd->stdout_is_eq("");
+ $cmd->stderr_is_eq("fping: -g works only with IPv4 addresses\n");
+}
+
+# fping -H
+{
+my $cmd = Test::Command->new(cmd => "fping -H 1 127.0.0.1");
+$cmd->exit_is_num(0);
+$cmd->stdout_is_eq("127.0.0.1 is alive\n");
+$cmd->stderr_is_eq("");
+}
--- /dev/null
+#!/usr/bin/perl -w
+
+use Test::Command tests => 16;
+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
+
+# fping -i n
+{
+my $cmd = Test::Command->new(cmd => "fping -i 100 127.0.0.1 127.0.0.2");
+$cmd->exit_is_num(0);
+$cmd->stdout_is_eq("127.0.0.1 is alive\n127.0.0.2 is alive\n");
+$cmd->stderr_is_eq("");
+}
+
+# fping -l
+{
+my $cmd = Test::Command->new(cmd => '(sleep 2; pkill fping)& fping -p 900 -l 127.0.0.1');
+$cmd->stdout_like(qr{127\.0\.0\.1 : \[0\], 64 bytes, \d\.\d+ ms \(\d\.\d+ avg, 0% loss\)
+127\.0\.0\.1 : \[1\], 64 bytes, \d\.\d+ ms \(\d\.\d+ avg, 0% loss\)
+});
+}
+
+# fping -l --print-tos --print-ttl
+{
+my $cmd = Test::Command->new(cmd => '(sleep 2; pkill fping)& fping -p 900 --print-ttl --print-tos -l 127.0.0.1');
+$cmd->stdout_like(qr{127\.0\.0\.1 : \[0\], 64 bytes, \d\.\d+ ms \(\d\.\d+ avg, 0% loss\) \(TOS \d+\) \(TTL \d+\)
+127\.0\.0\.1 : \[1\], 64 bytes, \d\.\d+ ms \(\d\.\d+ avg, 0% loss\) \(TOS \d+\) \(TTL \d+\)
+});
+}
+
+# fping -k
+SKIP: {
+if($^O ne 'linux') {
+ skip '-k option is only supported on Linux', 3;
+}
+my $cmd = Test::Command->new(cmd => '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');
+$cmd->stdout_like(qr{127\.0\.0\.1 : \[0\], 64 bytes, \d\.\d+ ms \(\d\.\d+ avg, 0% loss\)
+127\.0\.0\.1 : \[1\], 64 bytes, \d\.\d+ ms \(\d\.\d+ avg, 0% loss\)
+127\.0\.0\.1 : \[2\], 64 bytes, \d\.\d+ ms \(\d\.\d+ avg, 0% loss\)
+127\.0\.0\.1 : \[3\], 64 bytes, \d\.\d+ ms \(\d\.\d+ avg, 0% loss\)
+127\.0\.0\.1 : \[4\], 64 bytes, \d\.\d+ ms \(\d\.\d+ avg, 0% loss\)
+});
+$cmd->stderr_like(qr{\[\d+:\d+:\d+\]
+127\.0\.0\.1 : xmt/rcv/%loss = \d+/\d+/\d+%, min/avg/max = \d+\.\d+/\d+\.\d+/\d+\.\d+
+});
+}
+
+# fping -l -Q
+SKIP: {
+if($^O eq 'darwin') {
+ skip 'On macOS, this test is unreliable', 2;
+}
+my $cmd = Test::Command->new(cmd => '(sleep 2; pkill fping)& fping -p 850 -l -Q 1 127.0.0.1');
+$cmd->stdout_is_eq("");
+$cmd->stderr_like(qr{\[\d\d:\d\d:\d\d\]
+127\.0\.0\.1 : xmt/rcv/%loss = \d/\d/\d%, min/avg/max = \d\.\d+/\d\.\d+/\d\.\d+
+\[\d\d:\d\d:\d\d\]
+127\.0\.0\.1 : xmt/rcv/%loss = \d/\d/\d%, min/avg/max = \d\.\d+/\d\.\d+/\d\.\d+
+});
+}
+
+# fping -l -t
+{
+my $cmd = Test::Command->new(cmd => '(sleep 2; pkill fping)& fping -p 900 -t 1500 -l 127.0.0.1');
+$cmd->stdout_like(qr{127\.0\.0\.1 : \[0\], 64 bytes, \d\.\d+ ms \(\d\.\d+ avg, 0% loss\)
+127\.0\.0\.1 : \[1\], 64 bytes, \d\.\d+ ms \(\d\.\d+ avg, 0% loss\)
+});
+}
+
+# fping -M
+SKIP: {
+ if($^O eq 'darwin') {
+ skip '-M option not supported on macOS', 3;
+ }
+ my $cmd = Test::Command->new(cmd => "fping -M 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 -m -> test-14-internet-hosts
--- /dev/null
+#!/usr/bin/perl -w
+
+use Test::Command tests => 48;
+use Test::More;
+
+# -n show targets by name (-d is equivalent)
+# -O n set the type of service (tos) flag on the ICMP packets
+# -p n interval between ping packets to one target (in millisec)
+# (in looping and counting modes, default 1000)
+# -q quiet (don't show per-target/per-ping results)
+# -Q n same as -q, but show summary every n seconds
+
+# fping -n -> test-14-internet-hosts
+
+# fping -d -n
+{
+my $cmd = Test::Command->new(cmd => "fping -d -n 127.0.0.1");
+$cmd->exit_is_num(1);
+$cmd->stdout_is_eq("");
+$cmd->stderr_is_eq("fping: use either one of -d or -n\n");
+}
+
+# fping -n -d
+{
+my $cmd = Test::Command->new(cmd => "fping -n -d 127.0.0.1");
+$cmd->exit_is_num(1);
+$cmd->stdout_is_eq("");
+$cmd->stderr_is_eq("fping: use either one of -d or -n\n");
+}
+
+# fping -o
+{
+my $cmd = Test::Command->new(cmd => "fping -t100 -p 100 -o -c 5 8.8.8.7");
+$cmd->exit_is_num(1);
+$cmd->stdout_is_eq("8.8.8.7 : [0], timed out (NaN avg, 100% loss)
+8.8.8.7 : [1], timed out (NaN avg, 100% loss)
+8.8.8.7 : [2], timed out (NaN avg, 100% loss)
+8.8.8.7 : [3], timed out (NaN avg, 100% loss)
+8.8.8.7 : [4], timed out (NaN avg, 100% loss)
+");
+$cmd->stderr_like(qr{^\s*8\.8\.8\.7 : xmt/rcv/%loss = 5/0/100%, outage\(ms\) = 50\d\s*$});
+}
+
+# fping -O
+{
+my $cmd = Test::Command->new(cmd => "fping -O 2 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 -O --print-tos
+{
+my $cmd = Test::Command->new(cmd => "fping -O 2 --print-tos 127.0.0.1");
+$cmd->exit_is_num(0);
+$cmd->stdout_like(qr{127\.0\.0\.1 is alive \(TOS \d+\)
+});
+$cmd->stderr_is_eq("");
+}
+
+# fping -a -O --print-tos
+{
+my $cmd = Test::Command->new(cmd => "fping -a -O 32 --print-tos 127.0.0.1");
+$cmd->exit_is_num(0);
+$cmd->stdout_like(qr{127\.0\.0\.1 \(TOS \d+\)
+});
+$cmd->stderr_is_eq("");
+}
+
+# fping --print-tos
+{
+my $cmd = Test::Command->new(cmd => "fping --print-tos 127.0.0.1");
+$cmd->exit_is_num(0);
+$cmd->stdout_like(qr{127\.0\.0\.1 is alive \(TOS 0\)
+});
+$cmd->stderr_is_eq("");
+}
+
+# fping --print-tos with IPv6
+SKIP: {
+ if($ENV{SKIP_IPV6}) {
+ skip 'Skip IPv6 tests', 3;
+ }
+ my $cmd = Test::Command->new(cmd => "fping --print-tos ::1");
+ $cmd->exit_is_num(0);
+ $cmd->stdout_like(qr{::1 is alive \(TOS unknown\)\n});
+ $cmd->stderr_is_eq("");
+}
+
+# fping -q
+{
+my $cmd = Test::Command->new(cmd => "fping -q -p 100 -c 3 127.0.0.1");
+$cmd->exit_is_num(0);
+$cmd->stdout_is_eq("");
+$cmd->stderr_like(qr{127\.0\.0\.1 : xmt/rcv/%loss = 3/3/0%, min/avg/max = \d\.\d+/\d\.\d+/\d\.\d+
+});
+}
+
+# fping -Q
+{
+my $cmd = Test::Command->new(cmd => "fping -Q 1 -p 400 -c 4 127.0.0.1");
+$cmd->exit_is_num(0);
+$cmd->stdout_is_eq("");
+$cmd->stderr_like(qr{\[\d+:\d+:\d+\]
+127\.0\.0\.1 : xmt/rcv/%loss = 3/3/0%, min/avg/max = \d\.\d+/\d\.\d+/\d\.\d+
+127\.0\.0\.1 : xmt/rcv/%loss = 4/4/0%, min/avg/max = \d\.\d+/\d\.\d+/\d\.\d+
+});
+}
+
+# fping -Q (longer test to show two time stamps and reset statistics)
+{
+my $cmd = Test::Command->new(cmd => "fping -Q 1 -p 550 -c 5 127.0.0.1");
+$cmd->exit_is_num(0);
+$cmd->stdout_is_eq("");
+$cmd->stderr_like(qr{\[\d+:\d+:\d+\]
+127\.0\.0\.1 : xmt/rcv/%loss = 2/2/0%, min/avg/max = \d\.\d+/\d\.\d+/\d\.\d+
+\[\d+:\d+:\d+\]
+127\.0\.0\.1 : xmt/rcv/%loss = 2/2/0%, min/avg/max = \d\.\d+/\d\.\d+/\d\.\d+
+127\.0\.0\.1 : xmt/rcv/%loss = 5/5/0%, min/avg/max = \d\.\d+/\d\.\d+/\d\.\d+
+});
+}
+
+# fping -Q n ignores non-number characters after the number, except for keywords
+{
+my $cmd = Test::Command->new(cmd => "fping -Q 1whatever -p 550 -c 5 127.0.0.1");
+$cmd->exit_is_num(0);
+$cmd->stdout_is_eq("");
+$cmd->stderr_like(qr{\[\d+:\d+:\d+\]
+127\.0\.0\.1 : xmt/rcv/%loss = 2/2/0%, min/avg/max = \d\.\d+/\d\.\d+/\d\.\d+
+\[\d+:\d+:\d+\]
+127\.0\.0\.1 : xmt/rcv/%loss = 2/2/0%, min/avg/max = \d\.\d+/\d\.\d+/\d\.\d+
+127\.0\.0\.1 : xmt/rcv/%loss = 5/5/0%, min/avg/max = \d\.\d+/\d\.\d+/\d\.\d+
+});
+}
+
+# fping -Q n ignores unknown keywords
+{
+my $cmd = Test::Command->new(cmd => "fping -Q 1,not_a_keyword -p 550 -c 5 127.0.0.1");
+$cmd->exit_is_num(0);
+$cmd->stdout_is_eq("");
+$cmd->stderr_like(qr{\[\d+:\d+:\d+\]
+127\.0\.0\.1 : xmt/rcv/%loss = 2/2/0%, min/avg/max = \d\.\d+/\d\.\d+/\d\.\d+
+\[\d+:\d+:\d+\]
+127\.0\.0\.1 : xmt/rcv/%loss = 2/2/0%, min/avg/max = \d\.\d+/\d\.\d+/\d\.\d+
+127\.0\.0\.1 : xmt/rcv/%loss = 5/5/0%, min/avg/max = \d\.\d+/\d\.\d+/\d\.\d+
+});
+}
+
+# fping -Q n,cumulative
+{
+my $cmd = Test::Command->new(cmd => "fping -Q 1,cumulative -p 550 -c 5 127.0.0.1");
+$cmd->exit_is_num(0);
+$cmd->stdout_is_eq("");
+$cmd->stderr_like(qr{\[\d+:\d+:\d+\]
+127\.0\.0\.1 : xmt/rcv/%loss = 2/2/0%, min/avg/max = \d\.\d+/\d\.\d+/\d\.\d+
+\[\d+:\d+:\d+\]
+127\.0\.0\.1 : xmt/rcv/%loss = 4/4/0%, min/avg/max = \d\.\d+/\d\.\d+/\d\.\d+
+127\.0\.0\.1 : xmt/rcv/%loss = 5/5/0%, min/avg/max = \d\.\d+/\d\.\d+/\d\.\d+
+});
+}
+
+# fping -Q -o
+{
+my $cmd = Test::Command->new(cmd => "fping -c4 -Q1 -p550 -o 8.8.8.7");
+$cmd->exit_is_num(1);
+$cmd->stdout_is_eq("");
+$cmd->stderr_like(qr{\[\d+:\d+:\d+\]
+8\.8\.8\.7 : xmt/rcv/%loss = 1/0/100%, outage\(ms\) = 55\d
+\[\d+:\d+:\d+\]
+8\.8\.8\.7 : xmt/rcv/%loss = 2/0/100%, outage\(ms\) = 110\d
+8\.8\.8\.7 : xmt/rcv/%loss = 4/0/100%, outage\(ms\) = 220\d
+});
+}
+
+# fping -Q n,cumulative -o
+{
+my $cmd = Test::Command->new(cmd => "fping -c4 -Q1,cumulative -p550 -o 8.8.8.7");
+$cmd->exit_is_num(1);
+$cmd->stdout_is_eq("");
+$cmd->stderr_like(qr{\[\d+:\d+:\d+\]
+8\.8\.8\.7 : xmt/rcv/%loss = 1/0/100%, outage\(ms\) = 55\d
+\[\d+:\d+:\d+\]
+8\.8\.8\.7 : xmt/rcv/%loss = 3/0/100%, outage\(ms\) = 165\d
+8\.8\.8\.7 : xmt/rcv/%loss = 4/0/100%, outage\(ms\) = 220\d
+});
+}
+
--- /dev/null
+#!/usr/bin/perl -w
+
+use Test::Command tests => 30;
+use Test::More;
+
+# -R random bytes
+# -r n number of retries (default 3)
+# -s print final stats
+# -S addr set source address
+# -t n individual target initial timeout (in millisec) (default 500)
+# -T n ignored (for compatibility with fping 2.4)
+
+# fping -R
+{
+my $cmd = Test::Command->new(cmd => "fping -q -R -c3 -p100 127.0.0.1");
+$cmd->exit_is_num(0);
+$cmd->stdout_is_eq("");
+$cmd->stderr_like(qr{127\.0\.0\.1 : xmt/rcv/%loss = 3/3/0%.*});
+}
+
+SKIP: {
+ if($ENV{SKIP_IPV6}) {
+ skip 'Skip IPv6 tests', 3;
+ }
+ my $cmd = Test::Command->new(cmd => "fping -q -R -c3 -p100 ::1");
+ $cmd->exit_is_num(0);
+ $cmd->stdout_is_eq("");
+ $cmd->stderr_like(qr{::1 : xmt/rcv/%loss = 3/3/0%.*});
+}
+
+# fping -r tested in test-4-options-a-b.pl
+
+# fping -s
+{
+my $cmd = Test::Command->new(cmd => "fping -s 127.0.0.1");
+$cmd->exit_is_num(0);
+$cmd->stdout_is_eq("127.0.0.1 is alive\n");
+$cmd->stderr_like(qr{\s*
+\s*1 targets
+\s*1 alive
+\s*0 unreachable
+\s*0 unknown addresses
+\s*
+\s*0 timeouts \(waiting for response\)
+\s*1 ICMP Echos sent
+\s*1 ICMP Echo Replies received
+\s*0 other ICMP received
+
+\s*\d\.\d+ ms \(min round trip time\)
+\s*\d\.\d+ ms \(avg round trip time\)
+\s*\d\.\d+ ms \(max round trip time\)
+\s*\d\.\d+ sec \(elapsed real time\)
+});
+}
+
+# fping -s (no host reachable)
+{
+my $cmd = Test::Command->new(cmd => "fping -r0 -t100 -s 8.8.0.0");
+$cmd->exit_is_num(1);
+$cmd->stdout_is_eq("8.8.0.0 is unreachable\n");
+$cmd->stderr_like(qr{\s*
+\s*1 targets
+\s*0 alive
+\s*1 unreachable
+\s*0 unknown addresses
+\s*
+\s*1 timeouts \(waiting for response\)
+\s*1 ICMP Echos sent
+\s*0 ICMP Echo Replies received
+\s*0 other ICMP received
+
+\s*\d\.\d+ ms \(min round trip time\)
+\s*\d\.\d+ ms \(avg round trip time\)
+\s*\d\.\d+ ms \(max round trip time\)
+\s*\d\.\d+ sec \(elapsed real time\)
+});
+}
+
+# fping -s (both valid and invalid host name)
+{
+my $cmd = Test::Command->new(cmd => "fping -s 127.0.0.1 host.name.invalid");
+$cmd->exit_is_num(2);
+$cmd->stdout_is_eq("127.0.0.1 is alive\n");
+$cmd->stderr_like(qr{host\.name\.invalid: .+
+\s*
+\s*1 targets
+\s*1 alive
+\s*0 unreachable
+\s*1 unknown addresses
+\s*
+\s*0 timeouts \(waiting for response\)
+\s*1 ICMP Echos sent
+\s*1 ICMP Echo Replies received
+\s*0 other ICMP received
+
+\s*\d\.\d+ ms \(min round trip time\)
+\s*\d\.\d+ ms \(avg round trip time\)
+\s*\d\.\d+ ms \(max round trip time\)
+\s*\d\.\d+ sec \(elapsed real time\)
+});
+}
+
+# fping -S
+{
+my $cmd = Test::Command->new(cmd => "fping -S 127.0.0.1 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 -S (wrong source address)
+{
+my $cmd = Test::Command->new(cmd => "fping -S 192.0.2.47 127.0.0.1");
+$cmd->exit_is_num(4);
+$cmd->stdout_is_eq("");
+$cmd->stderr_like(qr{fping: cannot bind source address : .+\n});
+}
+
+# fping -S
+SKIP: {
+ if($ENV{SKIP_IPV6}) {
+ skip 'Skip IPv6 tests', 3;
+ }
+ my $cmd = Test::Command->new(cmd => "fping -S ::1 ::1");
+ $cmd->exit_is_num(0);
+ $cmd->stdout_is_eq("::1 is alive\n");
+ $cmd->stderr_is_eq("");
+}
+
+# fping -S (wrong IPv6 source address)
+SKIP: {
+ if($ENV{SKIP_IPV6}) {
+ skip 'Skip IPv6 tests', 3;
+ }
+ my $cmd = Test::Command->new(cmd => "fping -S 2001:db8::1 ::1");
+ $cmd->exit_is_num(4);
+ $cmd->stdout_is_eq("");
+ $cmd->stderr_like(qr{fping: cannot bind source address : .+\n});
+}
+
+# fping -S
+{
+my $cmd = Test::Command->new(cmd => "fping -S bla");
+$cmd->exit_is_num(1);
+$cmd->stdout_is_eq("");
+$cmd->stderr_is_eq("fping: can't parse source address: bla\n");
+}
+
+# (note: fping -t also tested in test-4-options-a-b.pl)
--- /dev/null
+#!/usr/bin/perl -w
+
+use Test::Command tests => 15;
+
+# -u show targets that are unreachable
+# -v show version
+# -x shows if >=N hosts are reachable or not
+# -X exits true immediately when N hosts are found
+
+# fping -u
+{
+my $cmd = Test::Command->new(cmd => "fping -r0 -u 8.8.0.0 127.0.0.1");
+$cmd->exit_is_num(1);
+$cmd->stdout_is_eq("8.8.0.0\n");
+$cmd->stderr_is_eq("");
+}
+
+# fping -v
+{
+my $cmd = Test::Command->new(cmd => "fping -v");
+$cmd->exit_is_num(0);
+$cmd->stdout_like(qr{ping: Version [45]\.\d+(-rc\d+)?});
+$cmd->stderr_is_eq("");
+}
+
+# fping -x
+{
+my $cmd = Test::Command->new(cmd => "fping -x 1 8.8.0.0 127.0.0.1");
+$cmd->exit_is_num(0);
+$cmd->stdout_is_eq("Enough hosts reachable (required: 1, reachable: 1)\n");
+$cmd->stderr_is_eq("");
+}
+
+# fping -x
+{
+my $cmd = Test::Command->new(cmd => "fping -x 2 8.8.0.0 127.0.0.1");
+$cmd->exit_is_num(1);
+$cmd->stdout_is_eq("Not enough hosts reachable (required: 2, reachable: 1)\n");
+$cmd->stderr_is_eq("");
+}
+
+# fping -X
+{
+my $cmd = Test::Command->new(cmd => "fping -X 1 --generate 127.0.0.0/29");
+$cmd->exit_is_num(0);
+$cmd->stdout_is_eq("Enough hosts reachable (required: 1, reachable: 1)\n");
+$cmd->stderr_is_eq("");
+}
--- /dev/null
+#!/usr/bin/perl -w
+
+use English;
+use File::Copy;
+use File::Temp qw/ tempdir /;
+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 = get_ping_gid_range();
+
+# Make a copy of the binary so that we get rid of setuid bit
+my $tmpdir = tempdir(CLEANUP => 1);
+my $fping_bin = `which fping`; chomp $fping_bin;
+my $fping_copy = "$tmpdir/fping.copy";
+copy($fping_bin, $fping_copy);
+chmod 0755, $fping_copy;
+
+# Determine what test to run, based on whether unprivileged
+# pings are allowed.
+if(scalar grep { $_ >= $allowed[0] && $_ <= $allowed[1] } @gids) {
+ diag('test unprivileged mode');
+ test_unprivileged_works();
+}
+else {
+ test_privileged_fails();
+}
+
+sub test_unprivileged_works {
+ plan tests => 15;
+
+ {
+ my $cmd = Test::Command->new(cmd => "$fping_copy 127.0.0.1");
+ $cmd->exit_is_num(0);
+ $cmd->stdout_is_eq("127.0.0.1 is alive\n");
+ $cmd->stderr_is_eq("");
+ }
+ {
+ my $cmd = Test::Command->new(cmd => "$fping_copy --print-tos 127.0.0.1");
+ $cmd->exit_is_num(0);
+ $cmd->stdout_is_eq("127.0.0.1 is alive (TOS unknown)\n");
+ $cmd->stderr_is_eq("");
+ }
+ {
+ my $cmd = Test::Command->new(cmd => "$fping_copy --print-ttl 127.0.0.1");
+ $cmd->exit_is_num(0);
+ $cmd->stdout_is_eq("127.0.0.1 is alive (TTL unknown)\n");
+ $cmd->stderr_is_eq("");
+ }
+ SKIP: {
+ if($^O ne 'linux') {
+ skip '-k option is only supported on Linux', 3;
+ }
+ my $cmd = Test::Command->new(cmd => "$fping_copy -4 -k 256 127.0.0.1");
+ $cmd->exit_is_num(0);
+ $cmd->stdout_is_eq("127.0.0.1 is alive\n");
+ $cmd->stderr_like(qr{fwmark ipv4: .+\n});
+ }
+ SKIP: {
+ if($^O ne 'linux') {
+ skip '-k option is only supported on Linux', 3;
+ }
+ if($ENV{SKIP_IPV6}) {
+ skip 'Skip IPv6 tests', 3;
+ }
+ my $cmd = Test::Command->new(cmd => "$fping_copy -6 -k 256 ::1");
+ $cmd->exit_is_num(0);
+ $cmd->stdout_is_eq("::1 is alive\n");
+ $cmd->stderr_like(qr{fwmark ipv6: .+\n});
+ }
+}
+
+sub test_privileged_fails {
+ plan tests => 3;
+
+ {
+ my $cmd = Test::Command->new(cmd => "$fping_copy 127.0.0.1");
+ $cmd->exit_is_num(4);
+ $cmd->stdout_is_eq("");
+ $cmd->stderr_like(qr{: can't create socket \(must run as root\?\)});
+ }
+}
--- /dev/null
+#!/usr/bin/perl -w
+
+use Test::Command tests => 84;
+use Test::More;
+
+# some options require a numeric argument
+for my $arg (qw(b B c C H i O p Q r t x X)) {
+ for my $test_input (qw(xxx '')) {
+ my $cmd = Test::Command->new(cmd => "fping -$arg $test_input");
+ $cmd->exit_is_num(1);
+ $cmd->stdout_is_eq("");
+ $cmd->stderr_like(qr{Usage:});
+ }
+}
+
+# fping -k, only supported on Linux, requires a number
+SKIP: {
+ if($^O ne 'linux') {
+ skip '-k option is only supported on Linux', 6;
+ }
+ for my $test_input (qw(xxx '')) {
+ my $cmd = Test::Command->new(cmd => "fping -k $test_input 127.0.0.1");
+ $cmd->exit_is_num(1);
+ $cmd->stdout_is_eq("");
+ $cmd->stderr_like(qr{Usage:});
+ }
+}
--- /dev/null
+#!/usr/bin/perl -w
+
+use Test::Command tests => 3;
+
+# fping
+{
+my $cmd = Test::Command->new(cmd => "fping nosuchname.example.com");
+$cmd->exit_is_num(2);
+$cmd->stdout_is_eq("");
+$cmd->stderr_like(qr{^nosuchname\.example\.com: .*not (known|found)});
+}
--- /dev/null
+#!/usr/bin/perl -w
+
+use Test::Command;
+use Test::More;
+
+# evaluate if we have internet
+if(!gethostbyname("www.google.com")) {
+ plan skip_all => 'Can\'t resolve www.google.com -> no internet?';
+ exit 0;
+}
+
+plan tests => 30;
+
+my $re_num = qr{\d+(?:\.\d+)?};
+
+# fping
+{
+my $cmd = Test::Command->new(cmd => "fping -q -i 10 -p 20 -c 3 -t200 8.8.8.8 www.france-telecom.fr www.google.com");
+$cmd->exit_is_num(0);
+$cmd->stdout_is_eq("");
+$cmd->stderr_like(qr{8\.8\.8\.8\s*: xmt/rcv/%loss = [123]/[123]/\d+%, min/avg/max = $re_num/$re_num/$re_num
+www\.france-telecom\.fr\s*: xmt/rcv/%loss = [123]/[123]/\d+%, min/avg/max = $re_num/$re_num/$re_num
+www\.google\.com\s*: xmt/rcv/%loss = [123]/[123]/\d+%, min/avg/max = $re_num/$re_num/$re_num
+});
+}
+
+# fping -A -n
+{
+my $cmd = Test::Command->new(cmd => "fping -A -n 8.8.8.8");
+$cmd->exit_is_num(0);
+$cmd->stdout_is_eq("dns.google (8.8.8.8) is alive\n");
+$cmd->stderr_is_eq("");
+}
+
+# fping -4 -A -n
+{
+my $cmd = Test::Command->new(cmd => "fping -4 -A -n dns.google");
+$cmd->exit_is_num(0);
+$cmd->stdout_like(qr{^dns.google \(8\.8\.(4\.4|8\.8)\) is alive\n$});
+$cmd->stderr_is_eq("");
+}
+
+# fping -4 --addr --rdns
+{
+my $cmd = Test::Command->new(cmd => "fping -4 --addr --rdns www.apple.com");
+$cmd->exit_is_num(0);
+$cmd->stdout_like(qr{^\S+\.akamaitechnologies\.com \(\d+\.\d+\.\d+\.\d+\) is alive\n$});
+$cmd->stderr_is_eq("");
+}
+
+# fping -4 --addr --name
+{
+my $cmd = Test::Command->new(cmd => "fping -4 --addr --name www.google.com");
+$cmd->exit_is_num(0);
+$cmd->stdout_like(qr{^www\.google\.com \(\d+\.\d+\.\d+\.\d+\) is alive\n$});
+$cmd->stderr_is_eq("");
+}
+
+# fping -A -n (IPv6)
+SKIP: {
+ if($ENV{SKIP_IPV6}) {
+ skip 'Skip IPv6 tests', 3;
+ }
+ my $cmd = Test::Command->new(cmd => "fping -6 -n -A dns.google");
+ $cmd->exit_is_num(0);
+ $cmd->stdout_like(qr{^dns.google \(2001:4860:4860::88(44|88)\) is alive\n$});
+ $cmd->stderr_is_eq("");
+}
+
+# fping -m
+SKIP: {
+ if($ENV{SKIP_IPV6}) {
+ skip 'Skip IPv6 tests', 3;
+ }
+ my $cmd = Test::Command->new(cmd => "fping -A -m dns.google");
+ $cmd->exit_is_num(0);
+ $cmd->stdout_like(qr{^.* is alive\n.* is alive\n.* is alive\n.* is alive\n});
+ $cmd->stderr_is_eq("");
+}
+
+# fping -m -A
+{
+my $cmd = Test::Command->new(cmd => "fping -4 -A -m www.cloudflare.com");
+$cmd->exit_is_num(0);
+$cmd->stdout_like(qr{\d+\.\d+\.\d+\.\d+ is alive\n\d+\.\d+\.\d+\.\d+ is alive\n});
+$cmd->stderr_is_eq("");
+}
+
+# fping -n
+{
+my $cmd = Test::Command->new(cmd => "fping -n 8.8.8.8");
+$cmd->exit_is_num(0);
+$cmd->stdout_is_eq("dns.google is alive\n");
+$cmd->stderr_is_eq("");
+}
+
+# fping -M
+SKIP: {
+ if($^O eq 'darwin') {
+ skip '-M option not supported on macOS', 3;
+ }
+ my $cmd = Test::Command->new(cmd => "fping -r 0 -b 10000 -M 8.8.8.8");
+ $cmd->exit_is_num(1);
+ $cmd->stdout_is_eq("8.8.8.8 is unreachable\n");
+ $cmd->stderr_is_eq("8.8.8.8: error while sending ping: Message too long\n");
+}
--- /dev/null
+#!/usr/bin/perl -w
+
+use Test::Command;
+use Test::More;
+
+plan tests => 3;
+
+# fping
+{
+my $cmd = Test::Command->new(cmd => "fping -c 2 -Q 1 -N 127.0.0.1");
+$cmd->exit_is_num(0);
+$cmd->stdout_like(qr{CHART fping\.127_0_0_1_packets '' 'FPing Packets' packets '127.0.0.1' fping\.packets line 110020 1
+DIMENSION xmt sent absolute 1 1
+DIMENSION rcv received absolute 1 1
+BEGIN fping\.127_0_0_1_packets
+SET xmt = 1
+SET rcv = 1
+END
+CHART fping\.127_0_0_1_quality '' 'FPing Quality' percentage '127.0.0.1' fping\.quality area 110010 1
+DIMENSION returned '' absolute 1 1
+BEGIN fping\.127_0_0_1_quality
+SET returned = 100
+END
+CHART fping\.127_0_0_1_latency '' 'FPing Latency' ms '127.0.0.1' fping\.latency area 110000 1
+DIMENSION min minimum absolute 1 1000000
+DIMENSION max maximum absolute 1 1000000
+DIMENSION avg average absolute 1 1000000
+BEGIN fping\.127_0_0_1_latency
+SET min = \d+
+SET avg = \d+
+SET max = \d+
+END}
+);
+$cmd->stderr_like(qr{127.0.0.1 : xmt/rcv/%loss = 2/2/0%, min/avg/max = \d\.\d+/\d\.\d+/\d\.\d+});
+}
--- /dev/null
+#!/usr/bin/perl -w
+
+# regression testing for github issue #56
+#
+use Test::Command;
+use Test::More;
+
+if( $^O eq 'darwin' ) {
+ plan skip_all => 'Test disabled on MacOS';
+ exit 0;
+}
+
+plan tests => 3;
+
+my $cmd1 = Test::Command->new(cmd => "fping -t100 -p100 -C3 255.255.255.255");
+$cmd1->exit_is_num(1);
+$cmd1->stdout_is_eq("");
+$cmd1->stderr_is_eq("\n255.255.255.255 : - - -\n");
--- /dev/null
+#!/usr/bin/perl -w
+
+# regression testing for github issue #58
+
+use Test::Command tests => 3;
+
+my $cmd1 = Test::Command->new(cmd => "fping -a -g 2001:db8:120:4161::4/64");
+$cmd1->exit_is_num(1);
+$cmd1->stdout_is_eq("");
+$cmd1->stderr_is_eq("fping: -g works only with IPv4 addresses\n");
--- /dev/null
+#!/bin/bash
+
+# make sure that the .tar.gz file contains everything necessary
+# to build fping
+
+set -e
+set -x
+
+# skip on macos
+if [[ "$OSTYPE" == "darwin"* ]]; then
+ exit 0
+fi
+
+make dist
+VERSION=$(ls fping-*.tar.gz | sed -e 's/^fping-//' | sed -e 's/\.tar\.gz$//')
+if [ -z "$VERSION" ]; then
+ echo "tar.gz file not found." >&2
+ exit 1
+fi
+
+# unarchive
+TMPDIR=$(mktemp -d --tmpdir=ci)
+cd $TMPDIR
+tar xf ../../fping-$VERSION.tar.gz
+DIRNAME=$(ls)
+
+# build
+cd $DIRNAME
+./configure --enable-ipv4 --enable-ipv6 --prefix=/opt/fping
+make
+sudo make install
--- /dev/null
+dnl Process this file with autoconf to produce a configure script.
+
+dnl Minimum Autoconf version required.
+AC_PREREQ(2.59)
+
+AC_INIT([fping],[5.4])
+
+m4_ifdef([AC_AUTOCONF_VERSION],[AC_USE_SYSTEM_EXTENSIONS], [AC_GNU_SOURCE])
+
+# Detect Operatingsystem
+AC_CANONICAL_TARGET
+only_clock_realtime=no
+
+case "${target}" in
+ *darwin*)
+ only_clock_realtime=yes
+ ;;
+ *freebsd*)
+ only_clock_realtime=yes
+ ;;
+ *openbsd*)
+ only_clock_realtime=yes
+ ;;
+esac
+
+dnl --disable-ipv4
+AC_ARG_ENABLE([ipv4],
+ AS_HELP_STRING([--disable-ipv4], [Disable support for pinging IPv4 hosts]))
+AM_CONDITIONAL([IPV4], [test "x$enable_ipv4" != "xno"])
+AM_COND_IF([IPV4], [AC_DEFINE([IPV4], [1], [IPv4 enabled])])
+
+dnl --disable-ipv6
+AC_ARG_ENABLE([ipv6],
+ AS_HELP_STRING([--disable-ipv6], [Disable support for pinging IPv6 hosts]))
+AS_IF([test "x$enable_ipv6" != "xno"], [
+ dnl Test if IPv6 is supported
+ AC_CHECK_HEADERS([netinet/icmp6.h], [have_ipv6="yes"], [], [[
+ #include <netinet/in.h>
+ #include <sys/types.h>
+ ]])
+])
+dnl Can't disable both IPv4 and IPv6
+AS_IF([test "x$enable_ipv4" = "xno" -a "x$enable_ipv6" = "xno"], [
+ AC_MSG_ERROR([Need to enable IPv4 or IPv6. Can't disable both!)])
+])
+dnl IPv6 required, but not supported?
+AS_IF([test \( "x$enable_ipv6" = "xyes" -o "x$enable_ipv4" = "xno" \) -a "x$have_ipv6" != "xyes" ], [
+ AC_MSG_ERROR([IPv6 not supported on this platform (netinet/icmp6.h header not found)])
+])
+AM_CONDITIONAL([IPV6], [test "x$have_ipv6" = "xyes"])
+AM_COND_IF([IPV6], [AC_DEFINE([IPV6], [1], [IPv6 enabled])])
+
+AC_ARG_ENABLE([timestamp],
+ AS_HELP_STRING([--disable-timestamp], [Disable kernel-based packet timestaping (SO_TIMESTAMPNS)]))
+AS_IF([test "x$enable_timestamp" != "xno"], [
+ AC_CHECK_DECL([SO_TIMESTAMPNS], [AC_DEFINE(HAVE_SO_TIMESTAMPNS, [1], [SO_TIMESTAMPNS is defined])], [have_so_timestamp="no"], [#include <sys/types.h>
+#include <sys/socket.h>])
+])
+dnl Test if --enable-timestamp is explicitely enabled and make an error if this platform doesn't support it
+AS_IF([test "x$enable_timestamp" = "xyes" -a "x$have_so_timestamp" = "xno"], [
+ AC_MSG_ERROR([--enable-timestamp not supported on this platform])
+])
+AS_IF([test "x$only_clock_realtime" = "xyes"], [AC_DEFINE(ONLY_CLOCK_REALTIME, [1], [ONLY_CLOCK_REALTIME is defined])])
+
+AC_ARG_ENABLE([safe-limits],
+ AS_HELP_STRING([--enable-safe-limits], [Restrict timing parameters (-i, -p) within "safe" limits]))
+AS_IF([test "x$enable_safe_limits" = "xyes"], [
+ AC_DEFINE(FPING_SAFE_LIMITS, [1], [safe limits should be enforced])])
+
+AC_ARG_ENABLE([debug],
+ AS_HELP_STRING([--enable-debug], [enable debugging @<:@default=no@:>@]), [enable_debug=$enableval], [enable_debug=no])
+AS_IF([test "x$enable_debug" = "xyes"], [
+ AC_DEFINE([DEBUG], [1], [Define if debugging is enabled])])
+
+AM_INIT_AUTOMAKE([-Wall -Werror foreign])
+AM_MAINTAINER_MODE
+
+AC_CONFIG_HEADERS([config.h])
+
+dnl Checks for programs.
+
+AC_PROG_CC
+AM_PROG_CC_C_O
+m4_version_prereq([2.70],,[AC_PROG_CC_STDC])
+AC_PROG_CPP
+AC_PROG_INSTALL
+
+dnl Checks for libraries.
+
+AC_CHECK_FUNC(gethostbyname)
+if test $ac_cv_func_gethostbyname = no; then
+ AC_CHECK_LIB(nsl, gethostbyname)
+fi
+AC_CHECK_FUNC(connect)
+if test $ac_cv_func_connect = no; then
+ AC_CHECK_LIB(socket, connect)
+fi
+AC_CHECK_FUNC(sigaction)
+if test $ac_cv_func_sigaction = yes; then
+ AC_DEFINE([USE_SIGACTION],[1],[Define if sigaction is available.])
+fi
+
+AC_CHECK_FUNCS([strftime], [],
+ [AC_MSG_ERROR([strftime function is required but not found])])
+
+AH_TOP([
+#ifndef CONFIG_H
+#define CONFIG_H
+])
+
+AH_BOTTOM([
+/* some OSes do not define this ... lets take a wild guess */
+
+#ifndef INADDR_NONE
+# define INADDR_NONE 0xffffffffU
+#endif
+
+#endif /* CONFIG_H */
+
+])
+
+dnl Checks for header files.
+AC_CHECK_HEADERS([unistd.h sys/file.h stdlib.h sys/select.h])
+
+AC_CONFIG_FILES([Makefile
+ doc/Makefile
+ src/Makefile])
+
+AC_OUTPUT
--- /dev/null
+FROM debian:stable
+
+# Base
+RUN apt-get update && apt-get install -y \
+ build-essential \
+ automake \
+ autoconf \
+ m4
+
+# Add source code
+COPY ./ /app
+
+# Compile
+WORKDIR /app
+RUN autoreconf --install
+RUN ./configure && make && make install
+ENTRYPOINT ["fping"]
\ No newline at end of file
--- /dev/null
+Summary: send ICMP echo probes to multiple hosts
+Name: fping
+Version: 4.2
+Release: 1
+License: Freely redistributable without restriction
+Group: Applications/System
+Source0: http://fping.org/dist/%{name}-%{version}.tar.gz
+BuildRoot: %{_tmppath}/%{name}-%{version}-%{release}-buildroot
+
+%description
+fping is a program to send ICMP echo probes to network hosts, similar to ping,
+but much better performing when pinging multiple hosts. fping has a very long
+history: Roland Schemers did publish a first version of it in 1992 and it has
+established itself since then as a standard tool for network diagnostics and
+statistics.
+
+%prep
+%setup -q
+
+%build
+
+if [ ! -f ./configure ] ; then
+ ./autogen.sh
+fi
+
+# fping
+%configure --enable-ipv4
+make
+
+# fping6
+%configure --enable-ipv6
+make
+%{__mv} -f src/fping src/fping6
+
+%install
+rm -rf $RPM_BUILD_ROOT
+make DESTDIR=$RPM_BUILD_ROOT install
+
+# fping6
+%{__install} -Dp -m4755 src/fping6 %{buildroot}%{_sbindir}/fping6
+%{__ln_s} -f fping.8 %{buildroot}%{_mandir}/man8/fping6.8
+
+%clean
+rm -rf $RPM_BUILD_ROOT
+
+%files
+%defattr(-,root,root,-)
+%attr(4755, root, root) /usr/sbin/fping
+%attr(4755, root, root) /usr/sbin/fping6
+%doc README.md COPYING CHANGELOG.md
+/usr/share/man/man8/fping.8.gz
+/usr/share/man/man8/fping6.8.gz
+
+%post
+if [ -x /usr/sbin/setcap ]; then
+ /bin/chmod 0755 /usr/sbin/fping*
+ /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
+* Mon Dec 24 2012 Marcus Vinicius Ferreira <ferreira.mv@gmail.com>
+- Missing './configure' script when cloning from master.
+- Making 'fping6'.
+- Fix setuid permission to 'rwsr-xr-x'.
+- doc files.
+- Replacing setuid permission if 'setcap' is present on post-install.
+- Using 'http://fping.org/dist/' for release source distributions.
+
+* Mon Jul 16 2012 Stephen Schaefer <sschaefer@acm.org>
+- Initial build
+
+# vim:ft=spec:
+
--- /dev/null
+2017-02-09 David Schweikert <david@schweikert.ch>
+ * Version 3.16
+ * (feature) Support kernel-timestamping of received packets (#46)
+ * (feature) Simplify restrictions: only -i >= 1 and -p >= 10 are enforced now
+ * (bugfix) Fix option -m to return all IPs of a hostname
+ * (bugfix) Fix option -H (ttl) for IPv6
+ * (bugfix) Fix option -M (don't fragment) for IPv6
+ * (bugfix) Fix option -O (ToS) for IPv6
+ * (bugfix) Fix compatibility issue with AIX (#69, @blentzgh)
+ * (bugfix) Fix option -q not suppressing some ICMP error messages (#83)
+ * (bugfix) Fix option -M expecting an argument, when it shouldn't
+ * (bugfix) Fix minor issues found by Coverity Scan
+
+2017-01-11 David Schweikert <david@schweikert.ch>
+ * Version 3.15
+ * (bugfix) Fix compiler errors on platforms other than Linux (related
+ to the new -M option, #109)
+ * Test suite fixes for macOS
+
+2017-01-10 David Schweikert <david@schweikert.ch>
+ * Version 3.14
+ * (feature) Ignore network and broadcast for cidrs /31 and /32 (#102, Martin Topholm)
+ * (feature) New option '-M' to set the "Don't Fragment" flag (#91, Don Bowman)
+ * (feature) New option '-N' to output statistics for netdata (see: http://my-netdata.io/, #105, Costa Tsaousis)
+ * (feature) New option '-o' to calculate total outage time (#90, @jgerbeck)
+ * (bugfix) Exit code should be 2 when the hostname can't be resolved
+ (fixes #98, reported by @green-fox)
+ * (bugfix) Fix issue compliling on RHEL/Centos 7 (#95, @jbackman)
+ * (bugfix) Lower -i limit to 1 instead of 10
+ * (bugfix) Improve interval preciseness of -Q reporting
+ * (bugfix) Fix occasional false positive in -Q reporting (#97)
+ * (bugfix) Solaris 10 portability fix (#107, Peter Bray)
+
+2015-10-21 David Schweikert <david@schweikert.ch>
+ * Version 3.13
+ * (bugfix) Fix ICMP errors sometimes causing crashes with fping >= 3.11
+ (fixes #85, reported by Jamie Heilman and Bill Blough)
+
+2015-10-14 David Schweikert <david@schweikert.ch>
+ * Version 3.12
+ * (bugfix) Fix fping6 -R (fixes #84, reported by Stuart Henderson)
+
+2015-10-12 David Schweikert <david@schweikert.ch>
+ * Version 3.11
+ * (feature) New option -R to use random bytes instead of NULLs (#72, Anthony DeRobertis)
+ * (feature) Small documentation and performance improvements (Ryan Underwood)
+ * (bugfix) Fix double entries with fping -u and unreachable hosts
+ * (internal) Use sockaddr_storage and simplify code, so that we can one day support both IPv4 and IPv6 with the same binary
+
+2014-05-03 David Schweikert <david@schweikert.ch>
+ * Version 3.10
+ * Fix confusing error message with -g and IPv6 addresses (#58, reported by Axel Beckert)
+ * Allow option '-f' also for non-root (since setuid privileges are dropped)
+ * Do not retry twice DNS lookup on DNS lookup problem
+ * Remove support for NIS groups
+ * Better document -B backoff-factor and when it can be used (#33, Oleksiy Zagorskyi)
+ * More tests added
+
+2014-03-08 David Schweikert <david@schweikert.ch>
+ * Version 3.9
+ * Fix random output on socket error (reported by Aleksandrs Saveljevs, #56)
+ * Support ppc64le architecture by including alpha libtool version
+ (reported by Amit Kumar Gupta and Aravinda B Thunug)
+ * Fix compilation problem on FreeBSD (#57)
+ * Initial test suite and continous intergration (with travis-ci.org / coveralls.io)
+ * Don't output usage information on error
+
+2013-11-08 David Schweikert <david@schweikert.ch>
+ * Version 3.8
+ * Fix segmentation fault introduced in version 3.7 with loop mode (reported
+ by Vlad Glagolev, #55)
+
+2013-11-04 David Schweikert <david@schweikert.ch>
+ * Version 3.7
+ * Allow running as non-root on Mac OS X by using non-privileged ICMP (#7)
+ * Remove unnecessary IPv6 socket options
+ * Fix again compatibility issue with FreeBSD (Shawn Chu)
+ * Fix fping hanging forever on permanent sendto failure (Shawn Chu)
+ * Fix duplicate echo reply packets causing early stop in count mode
+ (reported by Ramon Schwammberger, #53)
+
+2013-10-10 David Schweikert <david@schweikert.ch>
+ * Version 3.6
+ * Fix loop issue after 65536 pings (reported by Peter Folk and GBert, #12)
+ * Minimum ping data size is now 0
+ * Removed setsockopt IPV6_CHECKSUM, which shouldn't be set and breaks
+ compiling on Solaris (reported by Juergen Arndt)
+ * Fix wrong min RTT value with -Q option (reported by Alexander Ivanov, #51)
+
+2013-05-22 David Schweikert <david@schweikert.ch>
+ * Version 3.5
+ * Fix sprint_tm buffer size crash (reported by Japheth Cleaver)
+ * Addded -D flag to print timestamps (Toke Høiland-Jørgensen)
+ * Fix fping6 build on OS X 10.8 (unknown contributor)
+ * Fix compatibility issue with FreeBSD (Alexandre Raynaud, Jason Harris, #39)
+ * Fping.spec: fix setuid permissions and provides fping6 (Marcus Vinicius Ferreira)
+ * Re-create configure script with autoconf 2.69 for aarch64 support (Chuck Anderson, #45)
+
+2012-09-04 David Schweikert <david@schweikert.ch>
+ * Version 3.4
+ * Revert "Output statistics to stdout instead of stderr", because it breaks
+ tools assuming the output goes to stderr
+
+2012-08-19 David Schweikert <david@schweikert.ch>
+ * Version 3.3
+ * Do not output icmp errors with -q (#1)
+ * Add --enable-ipv4 and --enable-ipv6 options to configure (Niclas Zeising)
+ * Fix removing of unreachable hosts when doing loop (Thomas Liske, #13 #23)
+ * Fix -A for fping6 (reported by Matt LaPlante, #14)
+ * Fix "options inet6" breaking IPv4 name resolution (reported by Matt LaPlante, #17)
+ * Output statistics to stdout instead of stderr (suggested by Simon Leinen, #9)
+ * Set default data size to 56 bytes on all architectures (#18)
+ * Added contrib/fping.spec (Stephen Schaefer, #24)
+ * Convert man-page source to POD for easier maintenance
+ * Fix error message on DNS error for IPv6 hosts (#27)
+ * Fix -n flag in fping6 (#28)
+ * Man-page fix: TOS option typo (Thomas Liske, #23)
+ * Man-page fix: inconsistency in regards to numeric arguments (Robert Henney)
+ * Man-page fix: better description of option -q (#15)
+
+2012-05-29 David Schweikert <david@schweikert.ch>
+ * Version 3.2
+ * Improve documentation for -g option (G.W. Haywood)
+ * Performance optimization for big select timeouts (#10, Andrey Bondarenko)
+ * Fix restart of select call after interrupt signal (#8, Boian Bonev)
+ * Fix infinite loop caused by linked list corruption (#11, Boian Bonev)
+
+2012-04-26 David Schweikert <david@schweikert.ch>
+ * Version 3.1
+ * -g option (generate): exclude network and broadcast address for cidr
+ ranges (idea by Eric Brander)
+ * do not explicitely check if running as root, to make it possible to
+ install fping with linux capabilities instead of making it setuid
+ (setcap cap_net_raw+ep fping)
+ * ANSI C (C89) compiler now a requirement
+ * Portability fixes
+ * Reorganized source directory
+ * Bugfix: fix timeout issue on Solaris (Sandor Geller)
+ * Man-page fixes (Axel Beckert)
+ * Added -H option to specify number of hops (Paul Duda)
+ * Output usage information to stdout when called with -h (Paul Duda)
+
+2011-12-28 David Schweikert <david@schweikert.ch>
+ * Version 3.0
+ * rewritten main loop for improved performance
+ * -T parameter (select timeout) now obsolete
+ * Maintenance taken over from unresponsive previous maintainer
+ (anybody please step up, if you disagree)
+ * New homepage: www.fping.org
+
+2009-12-21 Tobi Oetiker <tobi@oetiker.ch>
+ * Version v2.4b2-to3-ipv6
+ * added -On option to set the TOS octet
+ * Removed unused variables from code
+ * updated to current autoconf standards
+ * Merged Debian changes (see below)
+
+----------------------------------------------------------------------
+
+fping (2.4b2-to-ipv6-16.1) unstable; urgency=low
+
+ * NMU during Moenchengladbach BSP
+ * Fixes FTBFS on kfreebsd (Closes: #555398)
+ * Fixes typo "Paramter" in binary
+
+ -- Axel Beckert <abe@deuxchevaux.org> Sat, 23 Jan 2010 16:22:02 +0100
+
+fping (2.4b2-to-ipv6-16) unstable; urgency=low
+
+ * Fix the following bugs
+ - Network byte order sensitivity was missing completely.
+ Added hopefully all missing calls.
+ - The sequence numbering scheme used led to packet drops.
+ Changed it to a more senseful numbering scheme.
+ - Some minor C programming mistakes ('=' instead of '==').
+ Patch by Stephan Fuhrmann; closes: #502569
+ * Add support for command line select timeout setting
+ Patch by Marton Balint; closes: #502575
+ * Remove symlinks in /usr/sbin; closes: #377732
+ * Standards-Version is 3.8.0
+
+ -- Anibal Monsalve Salazar <anibal@debian.org> Sat, 18 Oct 2008 12:04:52 +1100
+
+fping (2.4b2-to-ipv6-15) unstable; urgency=low
+
+ * Added interface binding (-I) for fping
+ Patch by Peter Naulls <peter@mushroomnetworks.com>
+ Closes: #439014
+ * Fixed a couple of typos in fping.8. Closes: #423180
+ * Added homepage control header
+ * Bumped Standards-Version to 3.7.3
+ * Fixed the following lintian issue:
+ - debian-rules-sets-DH_COMPAT
+
+ -- Anibal Monsalve Salazar <anibal@debian.org> Mon, 03 Mar 2008 17:46:17 +1100
+
+fping (2.4b2-to-ipv6-13) unstable; urgency=low
+
+ * Fixed stdout flush problem, closes: #340146.
+ Patch by Bart Martens <bart.martens@advalvas.be>.
+
+ -- Anibal Monsalve Salazar <anibal@debian.org> Fri, 30 Dec 2005 08:30:09 +1100
+
+fping (2.4b2-to-ipv6-12) unstable; urgency=low
+
+ * Fixed "problem with option -r (retry limit)", closes: #318402.
+ Patch by Qingning Huo <qingningh@lanware.co.uk>.
+
+ -- Anibal Monsalve Salazar <anibal@debian.org> Sat, 08 Oct 2005 21:26:35 +1000
+
+fping (2.4b2-to-ipv6-11) unstable; urgency=low
+
+ * Fixed "would be useful to specify 'source address' like ping for multi
+ homed machines", closes: #198486.
+ Patch by Marc Haber <mh+debian-bugs@zugschlus.de>.
+
+ -- Anibal Monsalve Salazar <anibal@debian.org> Thu, 02 Jun 2005 08:14:54 +1000
+
+fping (2.4b2-to-ipv6-10) unstable; urgency=low
+
+ * Fixed "unnecessary delay with the -c option after the last packet"
+ (Closes: #293856). Patch by Niko Tyni <ntyni@iki.fi>
+
+ -- Anibal Monsalve Salazar <anibal@debian.org> Sun, 06 Feb 2005 23:25:57 +1100
+
+fping (2.4b2-to-ipv6-9) unstable; urgency=low
+
+ * Fixed "fping6 always does reverse lookup" (Closes: #273647).
+ Patch by Jeroen Massar and forwarded by Bernhard Schmidt <berni@birkenwald.de>
+
+ -- Anibal Monsalve Salazar <A.Monsalve.Salazar@IEEE.org> Mon, 10 Jan 2005 00:01:32 +1100
+
+fping (2.4b2-to-ipv6-7) unstable; urgency=low
+
+ * Build fping in build/ipv[46] instead of build and build-ipv6.
+ * Made DNS errors non-fatal for IPv6 (closes: #198056).
+
+ -- Herbert Xu <herbert@debian.org> Fri, 20 Jun 2003 21:36:30 +1000
+
+fping (2.4b2-to-ipv6-6) unstable; urgency=low
+
+ * Do not use incorrect linux.h file (closes: #85468).
+
+ -- Herbert Xu <herbert@debian.org> Sat, 17 May 2003 14:13:11 +1000
+
+fping (2.4b2-to-ipv6-5) unstable; urgency=low
+
+ * Fixed yet another divide by zero bug (closes: #148445).
+
+ -- Herbert Xu <herbert@debian.org> Tue, 4 Jun 2002 12:18:03 +1000
+
+fping (2.4b2-to-ipv6-4) unstable; urgency=low
+
+ * Made fping6 setuid (closes: #136386).
+ * Moved fping back into bin.
+ * Partially applied IPv6 patch to fix IPv6 checksums (closes: #136479).
+
+ -- Herbert Xu <herbert@debian.org> Sun, 7 Apr 2002 20:36:56 +1000
+
+fping (2.4b2-to-ipv6-3) unstable; urgency=low
+
+ * Added compatibility symlink for fping (closes: #135203).
+
+ -- Herbert Xu <herbert@debian.org> Sat, 23 Feb 2002 08:34:11 +1100
+
+fping (2.4b2-to-ipv6-2) unstable; urgency=low
+
+ * Fixed another divide by zero error (closes: #132370).
+
+ -- Herbert Xu <herbert@debian.org> Thu, 7 Feb 2002 20:10:48 +1100
+
+fping (2.4b2-to-ipv6-1) unstable; urgency=low
+
+ * New upstream release.
+ * Install fping into sbin as done by upstream.
+
+ -- Herbert Xu <herbert@debian.org> Fri, 1 Feb 2002 22:11:59 +1100
+
+fping (2.2b2-3) unstable; urgency=low
+
+ * Removed INSTALL file from package (closes: #84050).
+ * Fixed alignment bug.
+
+ -- Herbert Xu <herbert@debian.org> Sat, 10 Feb 2001 19:25:18 +1100
+
+fping (2.2b2-2) unstable; urgency=low
+
+ * Made changes for dpkg-statoverride (closes: #83838).
+
+ -- Herbert Xu <herbert@debian.org> Sun, 28 Jan 2001 21:53:05 +1100
+
+fping (2.2b2-1) unstable; urgency=low
+
+ * New upstream release.
+ * Fixed typo that prevented -d from working (closes: #83255).
+ * Drop root privileges after opening the socket (closes: #81589).
+ * Fixed the options [tip], they were out by a factor of 10
+ (Richard Kettlewell, closes: #83742).
+
+ -- Herbert Xu <herbert@debian.org> Sun, 28 Jan 2001 00:09:41 +1100
+
+fping (2.2b1-2) unstable; urgency=low
+
+ * Fixed typo in control file, spotted by William Ono (closes: #49909).
+
+ -- Herbert Xu <herbert@debian.org> Mon, 15 May 2000 12:27:03 +1000
+
+fping (2.2b1-1) unstable; urgency=low
+
+ * Initial release.
+ * Fixed divide by zero error (closes: #29902).
+
+ -- Herbert Xu <herbert@debian.org> Sat, 30 Oct 1999 16:36:19 +1000
+---------------------------------------------------------------------------------
+
+Wed Jan 16 2002
+Jeroen Massar
+- Revision v2.4b2-to-IPv6
+ - Added IPv6 support.
+ - Added -I option for selecting source IPv4/IPv6 address.
+ - Makefile.in now generates a Makefile which will build both
+ fping (IPv4) and fping6 (IPv6). Thus it makes an fping (IPv4 only)
+ and an fping6 (IPv6 only).
+ - num_unreachable was counted twice when a sendto() generated errors.
+ - See http://unfix.org/projects/ipv6/
+
+Tue Mar 14 2001
+Jason Ewasiuk <jasone@remote.net>
+- Revision v2.4b1
+ - added -g option for generating IPs from a start to an end value
+ - two available options, generate IPs from start IP to end IP
+ or from a passed netmask, such as 192.168.1.0/24
+
+Thu Feb 15 2001
+Jason Ewasiuk <jasone@remote.net>
+- Revision v2.3b1
+ - formatting changes to code layout (fping.c)
+ NOTE: Best viewed with a tab stop of 4
+ - merged in changes from Debian c/o Herbert Xu
+ <herbert@gondor.apana.org.au>
+ - Compilation fix on alphas with glibc
+ - Alignment issues (note from JE: in wait_for_reply())
+ - A typo with the time specified on the command line
+ (note from JE: bug was using 10 instead of 100)
+ - Drop privileges after obtaining socket
+ (note from JE: might be moot, since prog exits if
+ user is not root)
+ - touched all files in package to this date
+ - couple new #ifdefs added for future WIN32 support
+ (Haven't got to adding this yet, will take a lot of rewriting.)
+
+Fri Dec 8 10:33:13 2000 Roland Schemers <schemers@stanford.edu>
+
+ * stop using sys_errlist and start using strerror
+ fixed bug in output of -C
+
+Wed Jan 8 11:18:37 1997 Roland Schemers <schemers@stanford.edu>
+
+ * Created ChangeLog file. What follows was from the CHANGES file.
+
+* Revision 2.0 1994/10/31 21:26:23 morgan
+
+ Substantial rewrite, including new features:
+
+ support some traditional ping features:
+ loop mode
+ specify size of data packets
+ specify how many pings to send
+ show per-response data
+ interpret ICMPs other than ICMP Echo response
+
+ also
+
+ rewrote main loop completely
+ make timings in tenths of milliseconds
+ do exponential backoff on retries
+ port to more systems
+ add some debugging stuff
+ do better checking on whether received ICMP is for us
+
+* Revision 1.24 1993/12/10 23:11:39 schemers
+
+ commented out seteuid(getuid()) since it isn't needed
+
+* Revision 1.23 1993/12/10 18:33:41 schemers
+
+ Took out the -f option for non-root users. This can be enabled by
+ defining ENABLE_F_OPTION before compiling. There is a call to
+ access before opening the file, but there is a race condition.
+ Reading from stdin is much safer.
+
+
+* Revision 1.22 1993/11/16 19:49:24 schemers
+
+ Took out setuid(getuid()) and used access() system call to
+ check for access to the file specified with "-f".
+
+* Revision 1.21 1993/07/20 18:08:19 schemers
+
+ commented out the test to make sure the ping packet came from the
+ same IP address as the one we sent to. This could cause problems on
+ multi-homed hosts.
+
+* Revision 1.20 1993/02/23 00:16:38 schemers
+
+fixed syntax error (should have compiled before checking in...)
+
+* Revision 1.19 1993/02/23 00:15:15 schemers
+
+turned off printing of "is alive" when -a is specified.
+
+* Revision 1.18 1992/07/28 15:16:44 schemers
+
+added a fflush(stdout) call before the summary is sent to stderr, so
+everything shows up in the right order.
+
+* Revision 1.17 1992/07/23 03:29:42 schemers
+* Revision 1.16 1992/07/22 19:24:37 schemers
+
+Fixed declaration of timeval_diff. Didn't notice the problem because
+I use 'cc' in stead of gcc under Ultrix. Time to switch? :-)
+
+Modified file reaing so it would skip blank lines or lines starting
+with a '#'. Now you can do something like:
+
+fping -ad < /etc/hosts
+
+* Revision 1.15 1992/07/21 17:07:18 schemers
+
+Put in sanity checks so only root can specify "dangerous" options.
+Changed usage to show switchs in alphabetical order.
+* Revision 1.14 1992/07/21 16:40:52 schemers
+* Revision 1.13 1992/07/17 21:02:17 schemers
+
+Changed the default timeout to 2500 msec, and retry to 3. This was
+due to suggestions from people with slow (WAN) networks. The default
+1 sec timeout was too fast.
+
+
+Added '-e' option for showing elapsed (round-trip) times on pakets, and
+modified the -s option to include min, max, and average round-trip times,
+and over all elapsed time.
+
+Modified action taken when a error is returned from sendto. The action
+taken now considers the host unreachable and prints the hostname
+followed by the errno message. The program will not exit and will continue
+to try other hosts.
+
+* Revision 1.12 1992/07/17 16:38:54 schemers
+* Revision 1.11 1992/07/17 16:28:38 schemers
+
+ move socket create call so I could do a setuid(getuid()) before the
+ fopen call is made. Once the socket is created root privs aren't needed
+ to send stuff out on it.
+
+ moved num_timeout counter. It really was for debug purposes and didn't
+ make sense to the general public :-) Now it is the number of timeouts
+ (pings that didn't get received with the time limit).
+
+
+* Revision 1.10 1992/07/16 16:24:38 schemers
+* Revision 1.9 1992/07/16 16:00:04 schemers
+* Revision 1.8 1992/07/16 05:44:41 schemers
+
+Added _NO_PROTO stuff for older compilers, and _POSIX_SOURCE
+for unistd.h, and _POSIX_SOURCE for stdlib.h. Also added
+check for __cplusplus.
+
+Now compiles ok under Ultrix 3.1, and Sun4 using cc. Also compiled
+ok using g++ 2.2.2.
+
+Changed '-a' and '-u' flags to be mutually exclusive (makes sense, since
+specifiying both '-a' and '-u' is the same as not specifiying anything.
+Since '-a' and '-u' are mutually exclusive, these options now only print
+the hostname, and not the 'is alive' or 'is unreachable' messages.
+This makes it much easier to do stuff like:
+
+#!/usr/local/bin/perl
+$hosts_to_backup=`cat /etc/hosts.backup|fping -a`;
+
+Since you don't have to strip off the 'is alive' messages.
+
+Changed usage to and stats to print to stderr instead of stdout.
+
+-----------------------------------------------------------------------------
+
+RCS header info from original fping.c package (no longer required)
+
+/*
+ ***************************************************
+ *
+ * Standard RCS Header information (see co(1))
+ *
+ * $Author: schemers $
+ *
+ * $Date: 1997/01/08 20:29:33 $
+ *
+ * $Revision: 2.2 $
+ *
+ * $Locker: $
+ *
+ * $Source: /afs/ir/group/networking/src/fping/fping-2.2/src/RCS/fping.c,v $
+ *
+ * $State: Exp $
+ *
+ * $Log: fping.c,v $
+ *
+ * Revision 2.2 1997/01/08 20:29:33 schemers
+ * changes for autoconf/automake
+ *
+ * Revision 2.1 1997/01/08 19:07:18 schemers
+ * checked in RL "Bob"'s changes before configure'ing
+ *
+ * Revision 2.0 1994/10/31 21:26:23 schemers
+ * many changes by RL "Bob" Morgan
+ * add timing data collection, loop mode, per-packet output, etc
+ *
+ * Revision 1.24 1993/12/10 23:11:39 schemers
+ * commented out seteuid(getuid()) since it isn't needed
+ *
+ * Revision 1.23 1993/12/10 18:33:41 schemers
+ * Took out the -f option for non-root users. This can be enabled by
+ * defining ENABLE_F_OPTION before compiling. There is a call to
+ * access before opening the file, but there is a race condition.
+ * Reading from stdin is much safer.
+ *
+ * Revision 1.22 1993/11/16 19:49:24 schemers
+ * Took out setuid(getuid()) and used access() system call to
+ * check for access to the file specified with "-f".
+ *
+ * Revision 1.21 1993/07/20 18:08:19 schemers
+ * commented out the test to make sure the ping packet came from the
+ * same IP address as the one we sent to. This could cause problems on
+ * multi-homed hosts.
+ *
+ * Revision 1.20 1993/02/23 00:16:38 schemers
+ * fixed syntax error (should have compiled before checking in...)
+ *
+ * Revision 1.19 1993/02/23 00:15:15 schemers
+ * turned off printing of "is alive" when -a is specified.
+ *
+ * Revision 1.18 1992/07/28 15:16:44 schemers
+ * added a fflush(stdout) call before the summary is sent to stderr, so
+ * everything shows up in the right order.
+ *
+ * Revision 1.17 1992/07/23 03:29:42 schemers
+ * fixed declaration of timeval_diff.
+ *
+ * Revision 1.16 1992/07/22 19:24:37 schemers
+ * Modified file reading so it would skip blanks lines or lines starting
+ * with a '#'. Now you can do something like:
+ *
+ * fping -ad < /etc/hosts
+ *
+ * Revision 1.15 1992/07/21 17:07:18 schemers
+ * Put in sanity checks so only root can specify "dangerous" options.
+ * Changed usage to show switchs in alphabetical order.
+ *
+ * Revision 1.14 1992/07/21 16:40:52 schemers
+ * Now when sendto returns an error, the host is considered unreachable and
+ * and the error message (from errno) is displayed.
+ *
+ * Revision 1.13 1992/07/17 21:02:17 schemers
+ * changed default timeout to 2500 msec (for WANs), and default try
+ * to 3. This gives 10 second overall timeout.
+ *
+ * Added -e option for showing elapsed (round-trip) time on packets
+ *
+ * Modified -s option to inlude to round-trip stats
+ *
+ * Added #ifndef DEFAULT_* stuff its easier to change the defaults
+ *
+ * Reorganized main loop.
+ *
+ * cleaned up timeval stuff. removed set_timeval and timeval_expired
+ * since they aren't needed anymore. Just use timeval_diff.
+ *
+ * Revision 1.12 1992/07/17 16:38:54 schemers
+ * move socket create call so I could do a setuid(getuid()) before the
+ * fopen call is made. Once the socket is created root privs aren't needed
+ * to send stuff out on it.
+ *
+ * Revision 1.11 1992/07/17 16:28:38 schemers
+ * moved num_timeout counter. It really was for debug purposes and didn't
+ * make sense to the general public :-) Now it is the number of timeouts
+ * (pings that didn't get received with the time limit).
+ *
+ * Revision 1.10 1992/07/16 16:24:38 schemers
+ * changed usage() to use fprintf(stderr,"...");
+ *
+ * Revision 1.9 1992/07/16 16:00:04 schemers
+ * Added _NO_PROTO stuff for older compilers, and _POSIX_SOURCE
+ * for unistd.h, and _POSIX_SOURCE for stdlib.h. Also added
+ * check for __cplusplus.
+ *
+ * Revision 1.8 1992/07/16 05:44:41 schemers
+ * changed -a and -u to only show hostname in results. This is
+ * for easier parsing. Also added -v flag
+ *
+ * Revision 1.7 1992/07/14 18:45:23 schemers
+ * initialized last_time in add_host function
+ *
+ * Revision 1.6 1992/07/14 18:32:40 schemers
+ * changed select to use FD_ macros
+ *
+ * Revision 1.5 1992/07/14 17:21:22 schemers
+ * standardized exit status codes
+ *
+ * Revision 1.4 1992/06/26 15:25:35 schemers
+ * changed name from rrping to fping
+ *
+ * Revision 1.3 1992/06/24 15:39:32 schemers
+ * added -d option for unreachable systems
+ *
+ * Revision 1.2 1992/06/23 03:01:23 schemers
+ * misc fixes from R.L. "Bob" Morgan
+ *
+ * Revision 1.1 1992/06/19 18:23:52 schemers
+ * Initial revision
+ *
+ *--------------------------------------------------
+ * Copyright (c) 1992, 1994, 1997 Board of Trustees
+ * Leland Stanford Jr. University
+ ***************************************************
+ */
+
+
--- /dev/null
+man_MANS = fping.8
+
+EXTRA_DIST = fping.8 fping.pod README.1992 CHANGELOG.pre-v4
+
+fping.8: fping.pod
+ pod2man -c "" -s 8 -r "fping" $< >$@
--- /dev/null
+Original README (from 1992)
+
+ fping - A tool to quickly ping N number of hosts to determine
+ their reachability.
+
+ Roland J. Schemers III - Stanford University
+ schemers@Stanford.EDU
+
+ fping is a ping(1) like program which uses the Internet Control
+ Message Protocol (ICMP) echo request to determine if a host is
+ up. fping is different from ping in that you can specify any
+ number of hosts on the command line, or specify a file containing
+ the lists of hosts to ping. Instead of trying one host until it
+ timeouts or replies, fping will send out a ping packet and move
+ on to the next host in a round-robin fashion. If a host replies,
+ it is noted and removed from the list of hosts to check. If a host
+ does not respond within a certain time limit and/or retry limit it
+ will be considered unreachable.
+
+Site
+ Stanford University has a large TCP/IP network with over 16,000
+ assigned IP addresses and over 100 IP subnets.
+
+Problem and Issues
+
+ With a large a number of IP addresses in use, its becomes more and
+ more time consuming to check on which IP addresses are actively
+ in use, and which critical machines (routers, bridges, servers, etc)
+ are reachable. One example is we have a program which goes through
+ all of our routers arp caches looking for IP addresses that are in
+ use. After finding a list of IP addresses that aren't in any arp
+ caches fping can then be used to see if these IP addresses really
+ aren't being used, or are just behind the routers. Checking 2500
+ hosts (99% of which are unreachable) via ping can take hours.
+
+ fping was written to solve the problem of pinging N number of hosts
+ in an efficient manner. By sending out pings in a round-robin fashion
+ and checking on responses as they come in at random, a large number of
+ hosts can be checked at once.
+
+ Unlike ping, fping is meant to be used in scripts and its
+ output is easy to parse.
+
--- /dev/null
+.\" Automatically generated by Pod::Man 4.14 (Pod::Simple 3.43)
+.\"
+.\" Standard preamble:
+.\" ========================================================================
+.de Sp \" Vertical space (when we can't use .PP)
+.if t .sp .5v
+.if n .sp
+..
+.de Vb \" Begin verbatim text
+.ft CW
+.nf
+.ne \\$1
+..
+.de Ve \" End verbatim text
+.ft R
+.fi
+..
+.\" Set up some character translations and predefined strings. \*(-- will
+.\" give an unbreakable dash, \*(PI will give pi, \*(L" will give a left
+.\" double quote, and \*(R" will give a right double quote. \*(C+ will
+.\" give a nicer C++. Capital omega is used to do unbreakable dashes and
+.\" therefore won't be available. \*(C` and \*(C' expand to `' in nroff,
+.\" nothing in troff, for use with C<>.
+.tr \(*W-
+.ds C+ C\v'-.1v'\h'-1p'\s-2+\h'-1p'+\s0\v'.1v'\h'-1p'
+.ie n \{\
+. ds -- \(*W-
+. ds PI pi
+. if (\n(.H=4u)&(1m=24u) .ds -- \(*W\h'-12u'\(*W\h'-12u'-\" diablo 10 pitch
+. if (\n(.H=4u)&(1m=20u) .ds -- \(*W\h'-12u'\(*W\h'-8u'-\" diablo 12 pitch
+. ds L" ""
+. ds R" ""
+. ds C` ""
+. ds C' ""
+'br\}
+.el\{\
+. ds -- \|\(em\|
+. ds PI \(*p
+. ds L" ``
+. ds R" ''
+. ds C`
+. ds C'
+'br\}
+.\"
+.\" Escape single quotes in literal strings from groff's Unicode transform.
+.ie \n(.g .ds Aq \(aq
+.el .ds Aq '
+.\"
+.\" If the F register is >0, we'll generate index entries on stderr for
+.\" titles (.TH), headers (.SH), subsections (.SS), items (.Ip), and index
+.\" entries marked with X<> in POD. Of course, you'll have to process the
+.\" output yourself in some meaningful fashion.
+.\"
+.\" Avoid warning from groff about undefined register 'F'.
+.de IX
+..
+.nr rF 0
+.if \n(.g .if rF .nr rF 1
+.if (\n(rF:(\n(.g==0)) \{\
+. if \nF \{\
+. de IX
+. tm Index:\\$1\t\\n%\t"\\$2"
+..
+. if !\nF==2 \{\
+. nr % 0
+. nr F 2
+. \}
+. \}
+.\}
+.rr rF
+.\"
+.\" Accent mark definitions (@(#)ms.acc 1.5 88/02/08 SMI; from UCB 4.2).
+.\" Fear. Run. Save yourself. No user-serviceable parts.
+. \" fudge factors for nroff and troff
+.if n \{\
+. ds #H 0
+. ds #V .8m
+. ds #F .3m
+. ds #[ \f1
+. ds #] \fP
+.\}
+.if t \{\
+. ds #H ((1u-(\\\\n(.fu%2u))*.13m)
+. ds #V .6m
+. ds #F 0
+. ds #[ \&
+. ds #] \&
+.\}
+. \" simple accents for nroff and troff
+.if n \{\
+. ds ' \&
+. ds ` \&
+. ds ^ \&
+. ds , \&
+. ds ~ ~
+. ds /
+.\}
+.if t \{\
+. ds ' \\k:\h'-(\\n(.wu*8/10-\*(#H)'\'\h"|\\n:u"
+. ds ` \\k:\h'-(\\n(.wu*8/10-\*(#H)'\`\h'|\\n:u'
+. ds ^ \\k:\h'-(\\n(.wu*10/11-\*(#H)'^\h'|\\n:u'
+. ds , \\k:\h'-(\\n(.wu*8/10)',\h'|\\n:u'
+. ds ~ \\k:\h'-(\\n(.wu-\*(#H-.1m)'~\h'|\\n:u'
+. ds / \\k:\h'-(\\n(.wu*8/10-\*(#H)'\z\(sl\h'|\\n:u'
+.\}
+. \" troff and (daisy-wheel) nroff accents
+.ds : \\k:\h'-(\\n(.wu*8/10-\*(#H+.1m+\*(#F)'\v'-\*(#V'\z.\h'.2m+\*(#F'.\h'|\\n:u'\v'\*(#V'
+.ds 8 \h'\*(#H'\(*b\h'-\*(#H'
+.ds o \\k:\h'-(\\n(.wu+\w'\(de'u-\*(#H)/2u'\v'-.3n'\*(#[\z\(de\v'.3n'\h'|\\n:u'\*(#]
+.ds d- \h'\*(#H'\(pd\h'-\w'~'u'\v'-.25m'\f2\(hy\fP\v'.25m'\h'-\*(#H'
+.ds D- D\\k:\h'-\w'D'u'\v'-.11m'\z\(hy\v'.11m'\h'|\\n:u'
+.ds th \*(#[\v'.3m'\s+1I\s-1\v'-.3m'\h'-(\w'I'u*2/3)'\s-1o\s+1\*(#]
+.ds Th \*(#[\s+2I\s-2\h'-\w'I'u*3/5'\v'-.3m'o\v'.3m'\*(#]
+.ds ae a\h'-(\w'a'u*4/10)'e
+.ds Ae A\h'-(\w'A'u*4/10)'E
+. \" corrections for vroff
+.if v .ds ~ \\k:\h'-(\\n(.wu*9/10-\*(#H)'\s-2\u~\d\s+2\h'|\\n:u'
+.if v .ds ^ \\k:\h'-(\\n(.wu*10/11-\*(#H)'\v'-.4m'^\v'.4m'\h'|\\n:u'
+. \" for low resolution devices (crt and lpr)
+.if \n(.H>23 .if \n(.V>19 \
+\{\
+. ds : e
+. ds 8 ss
+. ds o a
+. ds d- d\h'-1'\(ga
+. ds D- D\h'-1'\(hy
+. ds th \o'bp'
+. ds Th \o'LP'
+. ds ae ae
+. ds Ae AE
+.\}
+.rm #[ #] #H #V #F C
+.\" ========================================================================
+.\"
+.IX Title "FPING 8"
+.TH FPING 8 "2025-08-19" "fping" ""
+.\" For nroff, turn off justification. Always turn off hyphenation; it makes
+.\" way too many mistakes in technical documents.
+.if n .ad l
+.nh
+.SH "NAME"
+fping \- send ICMP ECHO_REQUEST packets to network hosts
+.SH "SYNOPSIS"
+.IX Header "SYNOPSIS"
+\&\fBfping\fR [ \fIoptions\fR ] [ \fIsystems...\fR ]
+.SH "DESCRIPTION"
+.IX Header "DESCRIPTION"
+\&\fBfping\fR is a program like \fBping\fR which uses the Internet Control Message
+Protocol (\s-1ICMP\s0) echo request to determine if a target host is responding.
+\&\fBfping\fR differs from \fBping\fR in that you can specify any number of targets on the
+command line, or specify a file containing the lists of targets to ping.
+Instead of sending to one target until it times out or replies, \fBfping\fR will
+send out a ping packet and move on to the next target in a round-robin fashion.
+In the default mode, if a target replies, it is noted and removed from the list
+of targets to check; if a target does not respond within a certain time limit
+and/or retry limit it is designated as unreachable. \fBfping\fR also supports
+sending a specified number of pings to a target, or looping indefinitely (as in
+\&\fBping\fR ). Unlike \fBping\fR, \fBfping\fR is meant to be used in scripts, so its
+output is designed to be easy to parse. Current statistics can be obtained without
+termination of process with signal \s-1SIGQUIT\s0 (^\e from the keyboard on most systems).
+.SH "OPTIONS"
+.IX Header "OPTIONS"
+.IP "\fB\-4\fR, \fB\-\-ipv4\fR" 5
+.IX Item "-4, --ipv4"
+Restrict name resolution and IPs to IPv4 addresses.
+.IP "\fB\-6\fR, \fB\-\-ipv6\fR" 5
+.IX Item "-6, --ipv6"
+Restrict name resolution and IPs to IPv6 addresses.
+.IP "\fB\-a\fR, \fB\-\-alive\fR" 5
+.IX Item "-a, --alive"
+Show systems that are alive. (Options \fB\-c\fR and \fB\-C\fR override \fB\-a\fR.)
+.IP "\fB\-A\fR, \fB\-\-addr\fR" 5
+.IX Item "-A, --addr"
+Display targets by address rather than \s-1DNS\s0 name. Combined with \-d, the output
+will be both the ip and (if available) the hostname.
+.IP "\fB\-b\fR, \fB\-\-size\fR=\fI\s-1BYTES\s0\fR" 5
+.IX Item "-b, --size=BYTES"
+Number of bytes of ping data to send. The minimum size (normally 12) allows
+room for the data that \fBfping\fR needs to do its work (sequence number,
+timestamp). The reported received data size includes the \s-1IP\s0 header (normally
+20 bytes) and \s-1ICMP\s0 header (8 bytes), so the minimum total size is 40 bytes.
+Default is 56, as in \fBping\fR. Maximum is the theoretical maximum \s-1IP\s0 datagram
+size (64K), though most systems limit this to a smaller, system-dependent
+number. Cannot be used together with \fB\-\-icmp\-timestamp\fR.
+.IP "\fB\-B\fR, \fB\-\-backoff\fR=\fIN\fR" 5
+.IX Item "-B, --backoff=N"
+Backoff factor. In the default mode, \fBfping\fR sends several requests to a
+target before giving up, waiting longer for a reply on each successive request.
+This parameter is the value by which the wait time (\fB\-t\fR) is multiplied on each
+successive request; it must be entered as a floating-point number (x.y). The
+default is 1.5.
+.IP "\fB\-c\fR, \fB\-\-count\fR=\fIN\fR" 5
+.IX Item "-c, --count=N"
+Number of request packets to send to each target. In this mode, a line is
+displayed for each received response (this can suppressed with \fB\-q\fR or \fB\-Q\fR).
+Also, statistics about responses for each target are displayed when all
+requests have been sent (or when interrupted). This option overrides \fB\-a\fR
+or \fB\-u\fR.
+.IP "\fB\-C\fR, \fB\-\-vcount\fR=\fIN\fR" 5
+.IX Item "-C, --vcount=N"
+Similar to \fB\-c\fR, but the per-target statistics are displayed in a format
+designed for automated response-time statistics gathering. For example:
+.Sp
+.Vb 2
+\& $ fping \-C 5 \-q somehost
+\& somehost : 91.7 37.0 29.2 \- 36.8
+.Ve
+.Sp
+shows the response time in milliseconds for each of the five requests, with the
+\&\f(CW\*(C`\-\*(C'\fR indicating that no response was received to the fourth request. This
+option overrides \fB\-a\fR or \fB\-u\fR.
+.IP "\fB\-\-check\-source\fR" 5
+.IX Item "--check-source"
+Discard Echo replies that are sourced from a different address than the target
+address. This avoids spurious reachability results on busy monitoring systems
+where two \fBfping\fR instances with the same lower 16 bits of the process \s-1ID\s0 may
+be running at the same time.
+.IP "\fB\-d\fR, \fB\-\-rdns\fR" 5
+.IX Item "-d, --rdns"
+Use \s-1DNS\s0 to lookup address of ping target. This allows you to give fping
+a list of \s-1IP\s0 addresses as input and print hostnames in the output. This is similar
+to option \fB\-n\fR/\fB\-\-name\fR, but will force a reverse-DNS lookup even if you give
+hostnames as target (\s-1NAME\-\s0>\s-1IP\-\s0>\s-1NAME\s0).
+.IP "\fB\-D\fR, \fB\-\-timestamp\fR" 5
+.IX Item "-D, --timestamp"
+Add Unix timestamps in front of output lines generated with in looping or counting
+modes (\fB\-l\fR, \fB\-c\fR, or \fB\-C\fR).
+.Sp
+Subcommand: \fB\-\-timestamp\-format\fR=\fIctime|iso|rfc3339\fR
+.Sp
+Allow to change the timestamp format of the \fB\-D\fR option to the following format types.
+.Sp
+\&\fIctime\fR = \*(L"%c\*(R" (Example: Mon Jun 10 07:50:00 2024)
+.Sp
+\&\fIiso\fR = \*(L"%Y\-%m\-%dT%T%z\*(R" (Example: 2024\-06\-10T07:50:00+0200)
+.Sp
+\&\fIrfc3339\fR = \*(L"%Y\-%m\-%d \f(CW%H:\fR%M:%S\*(R" (Example: 2024\-06\-10 07:50:00)
+.IP "\fB\-e\fR, \fB\-\-elapsed\fR" 5
+.IX Item "-e, --elapsed"
+Show elapsed (round-trip) time of packets.
+.IP "\fB\-f\fR, \fB\-\-file\fR" 5
+.IX Item "-f, --file"
+Read list of targets from a file.
+.IP "\fB\-g\fR, \fB\-\-generate\fR \fIaddr/mask\fR" 5
+.IX Item "-g, --generate addr/mask"
+Generate a target list from a supplied \s-1IP\s0 netmask, or a starting and ending \s-1IP.\s0
+Specify the netmask or start/end in the targets portion of the command line. If
+a network with netmask is given, the network and broadcast addresses will be
+excluded. ex. To ping the network 192.168.1.0/24, the specified command line
+could look like either:
+.Sp
+.Vb 1
+\& $ fping \-g 192.168.1.0/24
+.Ve
+.Sp
+or
+.Sp
+.Vb 1
+\& $ fping \-g 192.168.1.1 192.168.1.254
+.Ve
+.IP "\fB\-h\fR, \fB\-\-help\fR" 5
+.IX Item "-h, --help"
+Print usage message.
+.IP "\fB\-H\fR, \fB\-\-ttl\fR=\fIN\fR" 5
+.IX Item "-H, --ttl=N"
+Set the \s-1IP TTL\s0 field (time to live hops).
+.IP "\fB\-\-print\-ttl\fR" 5
+.IX Item "--print-ttl"
+Displays the IPv4 \s-1TTL\s0 value from the \s-1IP\s0 Header in the output.
+If \fBfping\fR cannot read the \s-1TTL\s0 value, \*(L"(\s-1TTL\s0 unknown)\*(R" is returned.
+IPv4 only, requires root privileges or cap_net_raw.
+.IP "\fB\-i\fR, \fB\-\-interval\fR=\fI\s-1MSEC\s0\fR" 5
+.IX Item "-i, --interval=MSEC"
+The minimum amount of time (in milliseconds) between sending a ping packet
+to any target (default is 10, minimum is 1).
+.IP "\fB\-I\fR, \fB\-\-iface\fR=\fI\s-1IFACE\s0\fR" 5
+.IX Item "-I, --iface=IFACE"
+Set the interface (requires \s-1SO_BINDTODEVICE\s0 support).
+.IP "\fB\-\-icmp\-timestamp\fR" 5
+.IX Item "--icmp-timestamp"
+Send \s-1ICMP\s0 timestamp requests (\s-1ICMP\s0 type 13) instead of \s-1ICMP\s0 Echo requests.
+Print \s-1ICMP\s0 timestamps for originate, receive, and transmit, together with
+the local receive time in the same format, in addition to normal output.
+Cannot be used together with \fB\-b\fR because \s-1ICMP\s0 timestamp messages have a fixed size.
+IPv4 only, requires root privileges or cap_net_raw.
+.IP "\fB\-k\fR, \fB\-\-fwmark\fR=\fI\s-1FWMARK\s0\fR" 5
+.IX Item "-k, --fwmark=FWMARK"
+Set \s-1FWMARK\s0 on ping packets for policy-based routing. Requires Linux kernel
+2.6.25<=, and root privileges or cap_net_admin.
+.IP "\fB\-l\fR, \fB\-\-loop\fR" 5
+.IX Item "-l, --loop"
+Loop sending packets to each target indefinitely. Can be interrupted with
+Ctrl-C; statistics about responses for each target are then displayed.
+.IP "\fB\-m\fR, \fB\-\-all\fR" 5
+.IX Item "-m, --all"
+Send pings to each of a target host's multiple \s-1IP\s0 addresses (use of option '\-A'
+is recommended).
+.IP "\fB\-M\fR, \fB\-\-dontfrag\fR" 5
+.IX Item "-M, --dontfrag"
+Set the \*(L"Don't Fragment\*(R" bit in the \s-1IP\s0 header (used to determine/test the \s-1MTU\s0).
+.IP "\fB\-n\fR, \fB\-\-name\fR" 5
+.IX Item "-n, --name"
+If targets are specified as \s-1IP\s0 addresses, do a reverse-DNS lookup on them
+to print hostnames in the output.
+.IP "\fB\-N\fR, \fB\-\-netdata\fR" 5
+.IX Item "-N, --netdata"
+Format output for netdata (\-l \-Q are required). See: <https://netdata.cloud/>
+.IP "\fB\-o\fR, \fB\-\-outage\fR" 5
+.IX Item "-o, --outage"
+Calculate \*(L"outage time\*(R" based on the number of lost pings and the interval used (useful for network convergence tests).
+.IP "\fB\-O\fR, \fB\-\-tos\fR=\fIN\fR" 5
+.IX Item "-O, --tos=N"
+Set the typ of service flag (\s-1TOS\s0). \fIN\fR can be either decimal or hexadecimal
+(0xh) format.
+.IP "\fB\-\-print\-tos\fR" 5
+.IX Item "--print-tos"
+Displays the \s-1TOS\s0 value in the output. If \fBfping\fR cannot read the \s-1TOS\s0 value,
+\&\*(L"(\s-1TOS\s0 unknown)\*(R" is returned.
+IPv4 only, requires root privileges or cap_net_raw.
+.IP "\fB\-p\fR, \fB\-\-period\fR=\fI\s-1MSEC\s0\fR" 5
+.IX Item "-p, --period=MSEC"
+In looping or counting modes (\fB\-l\fR, \fB\-c\fR, or \fB\-C\fR), this parameter sets
+the time in milliseconds that \fBfping\fR waits between successive packets to
+an individual target. Default is 1000 and minimum is 10.
+.IP "\fB\-q\fR, \fB\-\-quiet\fR" 5
+.IX Item "-q, --quiet"
+Quiet. Don't show per-probe results, but only the final summary. Also don't
+show \s-1ICMP\s0 error messages.
+.IP "\fB\-Q\fR, \fB\-\-squiet\fR=\fISECS[,cumulative]\fR" 5
+.IX Item "-Q, --squiet=SECS[,cumulative]"
+Like \fB\-q\fR, but additionally show interval summary results every \fI\s-1SECS\s0\fR
+seconds. With \fIcumulative\fR, show summary results since start instead of
+for the last interval, unless option \fB\-N\fR is used, too.
+.IP "\fB\-r\fR, \fB\-\-retry\fR=\fIN\fR" 5
+.IX Item "-r, --retry=N"
+Retry limit (default 3). This is the number of times an attempt at pinging
+a target will be made, not including the first try.
+.IP "\fB\-R\fR, \fB\-\-random\fR" 5
+.IX Item "-R, --random"
+Instead of using all-zeros as the packet data, generate random bytes.
+Use to defeat, e.g., link data compression.
+.IP "\fB\-s\fR, \fB\-\-stats\fR" 5
+.IX Item "-s, --stats"
+Print cumulative statistics upon exit.
+.IP "\fB\-S\fR, \fB\-\-src\fR=\fIaddr\fR" 5
+.IX Item "-S, --src=addr"
+Set source address.
+.IP "\fB\-t\fR, \fB\-\-timeout\fR=\fI\s-1MSEC\s0\fR" 5
+.IX Item "-t, --timeout=MSEC"
+Initial target timeout in milliseconds. In the default, non-loop mode, the
+default timeout is 500ms, and it represents the amount of time that \fBfping\fR
+waits for a response to its first request. Successive timeouts are multiplied
+by the backoff factor specified with \fB\-B\fR.
+.Sp
+In loop/count mode, the default timeout is automatically adjusted to match
+the \*(L"period\*(R" value (but not more than 2000ms). You can still adjust the timeout
+value with this option, if you wish to, but note that setting a value larger
+than \*(L"period\*(R" produces inconsistent results, because the timeout value can
+be respected only for the last ping.
+.Sp
+Also note that any received replies that are larger than the timeout value, will
+be discarded.
+.IP "\fB\-T\fR \fIn\fR" 5
+.IX Item "-T n"
+Ignored (for compatibility with fping 2.4).
+.IP "\fB\-u\fR, \fB\-\-unreach\fR" 5
+.IX Item "-u, --unreach"
+Show targets that are unreachable. (Options \fB\-c\fR and \fB\-C\fR override \fB\-u\fR.)
+.IP "\fB\-v\fR, \fB\-\-version\fR" 5
+.IX Item "-v, --version"
+Print \fBfping\fR version information.
+.IP "\fB\-x\fR, \fB\-\-reachable\fR=\fIN\fR" 5
+.IX Item "-x, --reachable=N"
+Given a list of hosts, this mode checks if number of reachable hosts is >= N
+and exits true in that case.
+.IP "\fB\-X\fR, \fB\-\-fast\-reachable\fR=\fIN\fR" 5
+.IX Item "-X, --fast-reachable=N"
+Given a list of hosts, this mode immediately exits true once N alive hosts
+have been found.
+.SH "EXAMPLES"
+.IX Header "EXAMPLES"
+Generate 20 pings to two hosts in ca. 1 second (i.e. one ping every 50 ms to
+each host), and report every ping \s-1RTT\s0 at the end:
+.PP
+.Vb 1
+\& $ fping \-\-quiet \-\-interval=1 \-\-vcount=20 \-\-period=50 127.0.0.1 127.0.0.2
+.Ve
+.SH "AUTHORS"
+.IX Header "AUTHORS"
+.IP "\(bu" 4
+Roland J. Schemers \s-1III,\s0 Stanford University, concept and versions 1.x
+.IP "\(bu" 4
+\&\s-1RL\s0 \*(L"Bob\*(R" Morgan, Stanford University, versions 2.x
+.IP "\(bu" 4
+David Papp, versions 2.3x and up
+.IP "\(bu" 4
+David Schweikert, versions 3.0 and up
+.PP
+\&\fBfping website: <http://www.fping.org>\fR
+.SH "DIAGNOSTICS"
+.IX Header "DIAGNOSTICS"
+Exit status is 0 if all the hosts (or the number of hosts specified with \fB\-x\fR
+or \fB\-X\fR) are reachable, 1 if some (or too many with \fB\-x\fR or \fB\-X\fR) hosts
+were unreachable, 2 if any \s-1IP\s0 addresses were not found, 3 for invalid command
+line arguments, and 4 for a system call failure.
+.SH "RESTRICTIONS"
+.IX Header "RESTRICTIONS"
+The number of addresses that can be generated using the \f(CW\*(C`\-g\*(C'\fR, \f(CW\*(C`\-\-generate\*(C'\fR
+option is limited to 131070 (the number of host addresses in one 15\-bit IPv4
+prefix).
+.PP
+If fping was configured with \f(CW\*(C`\-\-enable\-safe\-limits\*(C'\fR, the following values are
+not allowed for non-root users:
+.IP "\(bu" 4
+\&\fB\-i\fR \fIn\fR, where \fIn\fR < 1 msec
+.IP "\(bu" 4
+\&\fB\-p\fR \fIn\fR, where \fIn\fR < 10 msec
+.SH "SEE ALSO"
+.IX Header "SEE ALSO"
+\&\f(CWping(8)\fR
--- /dev/null
+=head1 NAME
+
+fping - send ICMP ECHO_REQUEST packets to network hosts
+
+=head1 SYNOPSIS
+
+B<fping> [ I<options> ] [ I<systems...> ]
+
+=head1 DESCRIPTION
+
+B<fping> is a program like B<ping> which uses the Internet Control Message
+Protocol (ICMP) echo request to determine if a target host is responding.
+B<fping> differs from B<ping> in that you can specify any number of targets on the
+command line, or specify a file containing the lists of targets to ping.
+Instead of sending to one target until it times out or replies, B<fping> will
+send out a ping packet and move on to the next target in a round-robin fashion.
+In the default mode, if a target replies, it is noted and removed from the list
+of targets to check; if a target does not respond within a certain time limit
+and/or retry limit it is designated as unreachable. B<fping> also supports
+sending a specified number of pings to a target, or looping indefinitely (as in
+B<ping> ). Unlike B<ping>, B<fping> is meant to be used in scripts, so its
+output is designed to be easy to parse. Current statistics can be obtained without
+termination of process with signal SIGQUIT (^\ from the keyboard on most systems).
+
+=head1 OPTIONS
+
+=over 5
+
+=item B<-4>, B<--ipv4>
+
+Restrict name resolution and IPs to IPv4 addresses.
+
+=item B<-6>, B<--ipv6>
+
+Restrict name resolution and IPs to IPv6 addresses.
+
+=item B<-a>, B<--alive>
+
+Show systems that are alive. (Options B<-c> and B<-C> override B<-a>.)
+
+=item B<-A>, B<--addr>
+
+Display targets by address rather than DNS name. Combined with -d, the output
+will be both the ip and (if available) the hostname.
+
+=item B<-b>, B<--size>=I<BYTES>
+
+Number of bytes of ping data to send. The minimum size (normally 12) allows
+room for the data that B<fping> needs to do its work (sequence number,
+timestamp). The reported received data size includes the IP header (normally
+20 bytes) and ICMP header (8 bytes), so the minimum total size is 40 bytes.
+Default is 56, as in B<ping>. Maximum is the theoretical maximum IP datagram
+size (64K), though most systems limit this to a smaller, system-dependent
+number. Cannot be used together with B<--icmp-timestamp>.
+
+=item B<-B>, B<--backoff>=I<N>
+
+Backoff factor. In the default mode, B<fping> sends several requests to a
+target before giving up, waiting longer for a reply on each successive request.
+This parameter is the value by which the wait time (B<-t>) is multiplied on each
+successive request; it must be entered as a floating-point number (x.y). The
+default is 1.5.
+
+=item B<-c>, B<--count>=I<N>
+
+Number of request packets to send to each target. In this mode, a line is
+displayed for each received response (this can suppressed with B<-q> or B<-Q>).
+Also, statistics about responses for each target are displayed when all
+requests have been sent (or when interrupted). This option overrides B<-a>
+or B<-u>.
+
+=item B<-C>, B<--vcount>=I<N>
+
+Similar to B<-c>, but the per-target statistics are displayed in a format
+designed for automated response-time statistics gathering. For example:
+
+ $ fping -C 5 -q somehost
+ somehost : 91.7 37.0 29.2 - 36.8
+
+shows the response time in milliseconds for each of the five requests, with the
+C<-> indicating that no response was received to the fourth request. This
+option overrides B<-a> or B<-u>.
+
+=item B<--check-source>
+
+Discard Echo replies that are sourced from a different address than the target
+address. This avoids spurious reachability results on busy monitoring systems
+where two B<fping> instances with the same lower 16 bits of the process ID may
+be running at the same time.
+
+=item B<-d>, B<--rdns>
+
+Use DNS to lookup address of ping target. This allows you to give fping
+a list of IP addresses as input and print hostnames in the output. This is similar
+to option B<-n>/B<--name>, but will force a reverse-DNS lookup even if you give
+hostnames as target (NAME->IP->NAME).
+
+=item B<-D>, B<--timestamp>
+
+Add Unix timestamps in front of output lines generated with in looping or counting
+modes (B<-l>, B<-c>, or B<-C>).
+
+Subcommand: B<--timestamp-format>=I<ctime|iso|rfc3339>
+
+Allow to change the timestamp format of the B<-D> option to the following format types.
+
+I<ctime> = "%c" (Example: Mon Jun 10 07:50:00 2024)
+
+I<iso> = "%Y-%m-%dT%T%z" (Example: 2024-06-10T07:50:00+0200)
+
+I<rfc3339> = "%Y-%m-%d %H:%M:%S" (Example: 2024-06-10 07:50:00)
+
+=item B<-e>, B<--elapsed>
+
+Show elapsed (round-trip) time of packets.
+
+=item B<-f>, B<--file>
+
+Read list of targets from a file.
+
+=item B<-g>, B<--generate> I<addr/mask>
+
+Generate a target list from a supplied IP netmask, or a starting and ending IP.
+Specify the netmask or start/end in the targets portion of the command line. If
+a network with netmask is given, the network and broadcast addresses will be
+excluded. ex. To ping the network 192.168.1.0/24, the specified command line
+could look like either:
+
+ $ fping -g 192.168.1.0/24
+
+or
+
+ $ fping -g 192.168.1.1 192.168.1.254
+
+=item B<-h>, B<--help>
+
+Print usage message.
+
+=item B<-H>, B<--ttl>=I<N>
+
+Set the IP TTL field (time to live hops).
+
+=item B<--print-ttl>
+
+Displays the IPv4 TTL value from the IP Header in the output.
+If B<fping> cannot read the TTL value, "(TTL unknown)" is returned.
+IPv4 only, requires root privileges or cap_net_raw.
+
+=item B<-i>, B<--interval>=I<MSEC>
+
+The minimum amount of time (in milliseconds) between sending a ping packet
+to any target (default is 10, minimum is 1).
+
+=item B<-I>, B<--iface>=I<IFACE>
+
+Set the interface (requires SO_BINDTODEVICE support).
+
+=item B<--icmp-timestamp>
+
+Send ICMP timestamp requests (ICMP type 13) instead of ICMP Echo requests.
+Print ICMP timestamps for originate, receive, and transmit, together with
+the local receive time in the same format, in addition to normal output.
+Cannot be used together with B<-b> because ICMP timestamp messages have a fixed size.
+IPv4 only, requires root privileges or cap_net_raw.
+
+=item B<-k>, B<--fwmark>=I<FWMARK>
+
+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
+Ctrl-C; statistics about responses for each target are then displayed.
+
+=item B<-m>, B<--all>
+
+Send pings to each of a target host's multiple IP addresses (use of option '-A'
+is recommended).
+
+=item B<-M>, B<--dontfrag>
+
+Set the "Don't Fragment" bit in the IP header (used to determine/test the MTU).
+
+=item B<-n>, B<--name>
+
+If targets are specified as IP addresses, do a reverse-DNS lookup on them
+to print hostnames in the output.
+
+=item B<-N>, B<--netdata>
+
+Format output for netdata (-l -Q are required). See: L<https://netdata.cloud/>
+
+=item B<-o>, B<--outage>
+
+Calculate "outage time" based on the number of lost pings and the interval used (useful for network convergence tests).
+
+=item B<-O>, B<--tos>=I<N>
+
+Set the typ of service flag (TOS). I<N> can be either decimal or hexadecimal
+(0xh) format.
+
+=item B<--print-tos>
+
+Displays the TOS value in the output. If B<fping> cannot read the TOS value,
+"(TOS unknown)" is returned.
+IPv4 only, requires root privileges or cap_net_raw.
+
+=item B<-p>, B<--period>=I<MSEC>
+
+In looping or counting modes (B<-l>, B<-c>, or B<-C>), this parameter sets
+the time in milliseconds that B<fping> waits between successive packets to
+an individual target. Default is 1000 and minimum is 10.
+
+=item B<-q>, B<--quiet>
+
+Quiet. Don't show per-probe results, but only the final summary. Also don't
+show ICMP error messages.
+
+=item B<-Q>, B<--squiet>=I<SECS[,cumulative]>
+
+Like B<-q>, but additionally show interval summary results every I<SECS>
+seconds. With I<cumulative>, show summary results since start instead of
+for the last interval, unless option B<-N> is used, too.
+
+=item B<-r>, B<--retry>=I<N>
+
+Retry limit (default 3). This is the number of times an attempt at pinging
+a target will be made, not including the first try.
+
+=item B<-R>, B<--random>
+
+Instead of using all-zeros as the packet data, generate random bytes.
+Use to defeat, e.g., link data compression.
+
+=item B<-s>, B<--stats>
+
+Print cumulative statistics upon exit.
+
+=item B<-S>, B<--src>=I<addr>
+
+Set source address.
+
+=item B<-t>, B<--timeout>=I<MSEC>
+
+Initial target timeout in milliseconds. In the default, non-loop mode, the
+default timeout is 500ms, and it represents the amount of time that B<fping>
+waits for a response to its first request. Successive timeouts are multiplied
+by the backoff factor specified with B<-B>.
+
+In loop/count mode, the default timeout is automatically adjusted to match
+the "period" value (but not more than 2000ms). You can still adjust the timeout
+value with this option, if you wish to, but note that setting a value larger
+than "period" produces inconsistent results, because the timeout value can
+be respected only for the last ping.
+
+Also note that any received replies that are larger than the timeout value, will
+be discarded.
+
+=item B<-T> I<n>
+
+Ignored (for compatibility with fping 2.4).
+
+=item B<-u>, B<--unreach>
+
+Show targets that are unreachable. (Options B<-c> and B<-C> override B<-u>.)
+
+=item B<-v>, B<--version>
+
+Print B<fping> version information.
+
+=item B<-x>, B<--reachable>=I<N>
+
+Given a list of hosts, this mode checks if number of reachable hosts is >= N
+and exits true in that case.
+
+=item B<-X>, B<--fast-reachable>=I<N>
+
+Given a list of hosts, this mode immediately exits true once N alive hosts
+have been found.
+
+=back
+
+=head1 EXAMPLES
+
+Generate 20 pings to two hosts in ca. 1 second (i.e. one ping every 50 ms to
+each host), and report every ping RTT at the end:
+
+ $ fping --quiet --interval=1 --vcount=20 --period=50 127.0.0.1 127.0.0.2
+
+=head1 AUTHORS
+
+=over 4
+
+=item *
+
+Roland J. Schemers III, Stanford University, concept and versions 1.x
+
+=item *
+
+RL "Bob" Morgan, Stanford University, versions 2.x
+
+=item *
+
+David Papp, versions 2.3x and up
+
+=item *
+
+David Schweikert, versions 3.0 and up
+
+=back
+
+B<fping website: L<http://www.fping.org>>
+
+=head1 DIAGNOSTICS
+
+Exit status is 0 if all the hosts (or the number of hosts specified with B<-x>
+or B<-X>) are reachable, 1 if some (or too many with B<-x> or B<-X>) hosts
+were unreachable, 2 if any IP addresses were not found, 3 for invalid command
+line arguments, and 4 for a system call failure.
+
+=head1 RESTRICTIONS
+
+The number of addresses that can be generated using the C<-g>, C<--generate>
+option is limited to 131070 (the number of host addresses in one 15-bit IPv4
+prefix).
+
+If fping was configured with C<--enable-safe-limits>, the following values are
+not allowed for non-root users:
+
+=over 4
+
+=item *
+
+B<-i> I<n>, where I<n> < 1 msec
+
+=item *
+
+B<-p> I<n>, where I<n> < 10 msec
+
+=back
+
+=head1 SEE ALSO
+
+C<ping(8)>
--- /dev/null
+AM_CFLAGS = -Wall -Wextra -Wno-sign-compare
+
+sbin_PROGRAMS = fping
+
+fping_SOURCES = fping.c seqmap.c socket4.c fping.h options.h seqmap.h optparse.c optparse.h
+fping_DEPENDENCIES = ../config.h
+
+if IPV6
+fping_SOURCES += socket6.c
+fping_CFLAGS = $(AM_CFLAGS) -DIPV6
+endif
--- /dev/null
+/*
+ * fping: fast-ping, file-ping, favorite-ping, funky-ping
+ *
+ * Ping a list of target hosts in a round robin fashion.
+ * A better ping overall.
+ *
+ * fping website: http://www.fping.org
+ *
+ * Current maintainer of fping: David Schweikert
+ * Please send suggestions and patches to: david@schweikert.ch
+ *
+ *
+ * Original author: Roland Schemers <schemers@stanford.edu>
+ * IPv6 Support: Jeroen Massar <jeroen@unfix.org / jeroen@ipng.nl>
+ * Improved main loop: David Schweikert <david@schweikert.ch>
+ * Debian Merge, TOS settings: Tobi Oetiker <tobi@oetiker.ch>
+ * Bugfixes, byte order & senseful seq.-numbers: Stephan Fuhrmann (stephan.fuhrmann AT 1und1.de)
+ *
+ *
+ * Redistribution and use in source and binary forms are permitted
+ * provided that the above copyright notice and this paragraph are
+ * duplicated in all such forms and that any documentation,
+ * advertising materials, and other materials related to such
+ * distribution and use acknowledge that the software was developed
+ * by Stanford University. The name of the University may not be used
+ * to endorse or promote products derived from this software without
+ * specific prior written permission.
+ * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED
+ * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE.
+ */
+
+#ifdef __cplusplus
+extern "C" {
+#endif /* __cplusplus */
+
+#include "fping.h"
+#include "config.h"
+#include "options.h"
+#include "optparse.h"
+
+#include <errno.h>
+#include <inttypes.h>
+#include <signal.h>
+#include <stdarg.h>
+#include <stdint.h>
+#include <stdio.h>
+#include <time.h>
+
+#include "seqmap.h"
+
+#ifdef HAVE_UNISTD_H
+#include <unistd.h>
+#endif /* HAVE_UNISTD_H */
+
+#ifdef HAVE_STDLIB_H
+#include <stdlib.h>
+#endif /* HAVE_STDLIB_H */
+
+#include <stddef.h>
+#include <string.h>
+
+#include <sys/socket.h>
+#include <sys/time.h>
+#include <sys/types.h>
+
+#if HAVE_SYS_FILE_H
+#include <sys/file.h>
+#endif /* HAVE_SYS_FILE_H */
+
+#ifdef IPV6
+#include <netinet/icmp6.h>
+#endif
+#include <netinet/in_systm.h>
+
+#include <netinet/ip.h>
+#include <netinet/ip_icmp.h>
+
+#include <arpa/inet.h>
+#include <ctype.h>
+#include <netdb.h>
+
+#include <sys/select.h>
+
+/*** compatibility ***/
+
+/* Mac OS X's getaddrinfo() does not fail if we use an invalid combination,
+ * e.g. AF_INET6 with "127.0.0.1". If we pass AI_UNUSABLE to flags, it behaves
+ * like other platforms. But AI_UNUSABLE isn't available on other platforms,
+ * and we can safely use 0 for flags instead.
+ */
+#ifndef AI_UNUSABLE
+#define AI_UNUSABLE 0
+#endif
+
+/* MSG_TRUNC available on Linux kernel 2.2+, makes recvmsg return the full
+ * length of the raw packet received, even if the buffer is smaller */
+#ifndef MSG_TRUNC
+#define MSG_TRUNC 0
+#define RECV_BUFSIZE 4096
+#else
+#define RECV_BUFSIZE 128
+#endif
+
+/*** externals ***/
+
+extern char *optarg;
+extern int optind, opterr;
+#ifndef h_errno
+extern int h_errno;
+#endif
+
+#ifdef __cplusplus
+}
+#endif /* __cplusplus */
+
+/*** Constants ***/
+
+/* CLOCK_MONTONIC starts under macOS, OpenBSD and FreeBSD with undefined positive point and can not be use
+ * see github PR #217
+ * The configure script detect the predefined operating systems an set CLOCK_REALTIME using over ONLY_CLOCK_REALTIME variable
+ */
+#if HAVE_SO_TIMESTAMPNS || ONLY_CLOCK_REALTIME
+#define CLOCKID CLOCK_REALTIME
+#endif
+
+#if !defined(CLOCKID)
+#if defined(CLOCK_MONOTONIC)
+#define CLOCKID CLOCK_MONOTONIC
+#else
+#define CLOCKID CLOCK_REALTIME
+#endif
+#endif
+
+/*** Ping packet defines ***/
+
+#define MAX_IP_PACKET 65535 /* (theoretical) max IPv4 packet size */
+#define SIZE_IP_HDR 20 /* min IPv4 header size */
+#define SIZE_ICMP_HDR 8 /* from ip_icmp.h */
+#define MAX_PING_DATA (MAX_IP_PACKET - SIZE_IP_HDR - SIZE_ICMP_HDR)
+
+#define MAX_GENERATE 131070 /* maximum number of hosts that -g can generate */
+
+/* sized so as to be like traditional ping */
+#define DEFAULT_PING_DATA_SIZE 56
+
+/* ICMP Timestamp has a fixed payload size of 12 bytes */
+#define ICMP_TIMESTAMP_DATA_SIZE 12
+
+#ifdef FPING_SAFE_LIMITS
+#define MIN_INTERVAL_MS 1 /* in millisec */
+#define MIN_PERHOST_INTERVAL_MS 10 /* in millisec */
+#else
+#define MIN_INTERVAL_MS 0
+/* Set a very low limit for the per-host interval, even if safe limits are
+ * disabled, so that the memory allocation of the event storage is not
+ * unreasonably high. 0.001 ms would mean in theory at least 592 mbps of data
+ * sent to a single host, which probably doesn't make sense in any scenario. */
+#define MIN_PERHOST_INTERVAL_MS 0.001
+#endif
+
+/* response time array flags */
+#define RESP_WAITING -1
+#define RESP_UNUSED -2
+#define RESP_ERROR -3
+#define RESP_TIMEOUT -4
+
+/* debugging flags */
+#if defined(DEBUG) || defined(_DEBUG)
+#define DBG_TRACE 1
+#define DBG_SENT_TIMES 2
+#define DBG_RANDOM_LOSE_FEW 4
+#define DBG_RANDOM_LOSE_MANY 8
+#define DBG_PRINT_PER_SYSTEM 16
+#define DBG_REPORT_ALL_RTTS 32
+#endif /* DEBUG || _DEBUG */
+
+/* Long names for ICMP packet types */
+#define ICMP_TYPE_STR_MAX 18
+char *icmp_type_str[19] = {
+ "ICMP Echo Reply", /* 0 */
+ "",
+ "",
+ "ICMP Unreachable", /* 3 */
+ "ICMP Source Quench", /* 4 */
+ "ICMP Redirect", /* 5 */
+ "",
+ "",
+ "ICMP Echo", /* 8 */
+ "",
+ "",
+ "ICMP Time Exceeded", /* 11 */
+ "ICMP Parameter Problem", /* 12 */
+ "ICMP Timestamp Request", /* 13 */
+ "ICMP Timestamp Reply", /* 14 */
+ "ICMP Information Request", /* 15 */
+ "ICMP Information Reply", /* 16 */
+ "ICMP Mask Request", /* 17 */
+ "ICMP Mask Reply" /* 18 */
+};
+
+char *icmp_unreach_str[16] = {
+ "ICMP Network Unreachable", /* 0 */
+ "ICMP Host Unreachable", /* 1 */
+ "ICMP Protocol Unreachable", /* 2 */
+ "ICMP Port Unreachable", /* 3 */
+ "ICMP Unreachable (Fragmentation Needed)", /* 4 */
+ "ICMP Unreachable (Source Route Failed)", /* 5 */
+ "ICMP Unreachable (Destination Network Unknown)", /* 6 */
+ "ICMP Unreachable (Destination Host Unknown)", /* 7 */
+ "ICMP Unreachable (Source Host Isolated)", /* 8 */
+ "ICMP Unreachable (Communication with Network Prohibited)", /* 9 */
+ "ICMP Unreachable (Communication with Host Prohibited)", /* 10 */
+ "ICMP Unreachable (Network Unreachable For Type Of Service)", /* 11 */
+ "ICMP Unreachable (Host Unreachable For Type Of Service)", /* 12 */
+ "ICMP Unreachable (Communication Administratively Prohibited)", /* 13 */
+ "ICMP Unreachable (Host Precedence Violation)", /* 14 */
+ "ICMP Unreachable (Precedence cutoff in effect)" /* 15 */
+};
+
+#define ICMP_UNREACH_MAXTYPE 15
+
+struct event;
+typedef struct host_entry {
+ int i; /* index into array */
+ char *name; /* name as given by user */
+ char *host; /* text description of host */
+ struct sockaddr_storage saddr; /* internet address */
+ socklen_t saddr_len;
+ int64_t timeout; /* time to wait for response */
+ int64_t last_send_time; /* time of last packet sent */
+ int num_sent; /* number of ping packets sent (for statistics) */
+ int num_recv; /* number of pings received (duplicates ignored) */
+ int num_recv_total; /* number of pings received, including duplicates */
+ int64_t max_reply; /* longest response time */
+ int64_t min_reply; /* shortest response time */
+ int64_t total_time; /* sum of response times */
+ /* _i -> splits (reset on every report interval) */
+ int num_sent_i; /* number of ping packets sent */
+ int num_recv_i; /* number of pings received */
+ int64_t max_reply_i; /* longest response time */
+ int64_t min_reply_i; /* shortest response time */
+ int64_t total_time_i; /* sum of response times */
+ int64_t *resp_times; /* individual response times */
+
+ /* to avoid allocating two struct events each time that we send a ping, we
+ * preallocate here two struct events for each ping that we might send for
+ * this host. */
+ struct event *event_storage_ping;
+ struct event *event_storage_timeout;
+} HOST_ENTRY;
+
+int event_storage_count; /* how many events can be stored in host_entry->event_storage_xxx */
+
+/* basic algorithm to ensure that we have correct data at all times:
+ *
+ * 1. when a ping is sent:
+ * - two events get added into event_queue:
+ * - t+PERIOD: ping event
+ * - t+TIMEOUT: timeout event
+ *
+ * 2. when a ping is received:
+ * - record statistics (increase num_sent and num_received)
+ * - remove timeout event (we store the event in seqmap, so that we can retrieve it when the response is received)
+ *
+ * 3. when a timeout happens:
+ * - record statistics (increase num_sent only)
+ */
+
+#define EV_TYPE_PING 1
+#define EV_TYPE_TIMEOUT 2
+
+struct event {
+ struct event *ev_prev;
+ struct event *ev_next;
+ int64_t ev_time;
+ struct host_entry *host;
+ int ping_index;
+};
+
+struct event_queue {
+ struct event *first;
+ struct event *last;
+};
+
+/*** globals ***/
+
+HOST_ENTRY **table = NULL; /* array of pointers to items in the list */
+
+/* we keep two separate queues: a ping queue, for when the next ping should be
+ * sent, and a timeout queue. the reason for having two separate queues is that
+ * the ping period and the timeout value are different, so if we put them in
+ * the same event queue, we would need to scan many more entries when inserting
+ * into the sorted list.
+ */
+struct event_queue event_queue_ping;
+struct event_queue event_queue_timeout;
+
+char *prog;
+int ident4 = 0; /* our icmp identity field */
+int ident6 = 0;
+int socket4 = -1;
+int socktype4 = -1;
+int using_sock_dgram4 = 0;
+#ifndef IPV6
+int hints_ai_family = AF_INET;
+#else
+int socket6 = -1;
+int socktype6 = -1;
+int hints_ai_family = AF_UNSPEC;
+#endif
+
+volatile sig_atomic_t status_snapshot = 0;
+volatile sig_atomic_t finish_requested = 0;
+
+unsigned int debugging = 0;
+
+/* all time-related values are int64_t nanoseconds */
+unsigned int retry = DEFAULT_RETRY;
+int64_t timeout = (int64_t)DEFAULT_TIMEOUT * 1000000;
+int64_t interval = (int64_t)DEFAULT_INTERVAL * 1000000;
+int64_t perhost_interval = (int64_t)DEFAULT_PERHOST_INTERVAL * 1000000;
+float backoff = DEFAULT_BACKOFF_FACTOR;
+unsigned int ping_data_size = DEFAULT_PING_DATA_SIZE;
+unsigned int count = 1, min_reachable = 0;
+unsigned int trials;
+int64_t report_interval = 0;
+unsigned int ttl = 0;
+int src_addr_set = 0;
+struct in_addr src_addr;
+#ifdef IPV6
+int src_addr6_set = 0;
+struct in6_addr src_addr6;
+#endif
+
+/* global stats */
+int64_t max_reply = 0;
+int64_t min_reply = 0;
+int64_t total_replies = 0;
+int64_t sum_replies = 0;
+int max_hostname_len = 0;
+int num_hosts = 0; /* total number of hosts */
+int num_alive = 0, /* total number alive */
+ num_unreachable = 0, /* total number unreachable */
+ num_noaddress = 0; /* total number of addresses not found */
+int num_timeout = 0, /* number of times select timed out */
+ num_pingsent = 0, /* total pings sent */
+ num_pingreceived = 0, /* total pings received */
+ num_othericmprcvd = 0; /* total non-echo-reply ICMP received */
+
+struct timespec current_time; /* current time (pseudo) */
+int64_t current_time_ns;
+int64_t start_time;
+int64_t end_time;
+int64_t last_send_time; /* time last ping was sent */
+int64_t next_report_time; /* time next -Q report is expected */
+
+/* switches */
+int generate_flag = 0; /* flag for IP list generation */
+int verbose_flag, quiet_flag, stats_flag, unreachable_flag, alive_flag;
+int elapsed_flag, version_flag, count_flag, loop_flag, netdata_flag;
+int per_recv_flag, report_all_rtts_flag, name_flag, addr_flag, backoff_flag, rdns_flag;
+int multif_flag, timeout_flag, fast_reachable;
+int outage_flag = 0;
+int timestamp_flag = 0;
+int timestamp_format_flag = 0;
+int random_data_flag = 0;
+int cumulative_stats_flag = 0;
+int check_source_flag = 0;
+int icmp_request_typ = 0;
+int print_tos_flag = 0;
+int print_ttl_flag = 0;
+int size_flag = 0;
+#if defined(DEBUG) || defined(_DEBUG)
+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 ***/
+
+void add_name(char *name);
+void add_addr(char *name, char *host, struct sockaddr *ipaddr, socklen_t ipaddr_len);
+char *na_cat(char *name, struct in_addr ipaddr);
+void crash_and_burn(char *message);
+void errno_crash_and_burn(char *message);
+char *get_host_by_address(struct in_addr in);
+int send_ping(HOST_ENTRY *h, int index);
+void usage(int);
+int wait_for_reply(int64_t);
+void print_per_system_stats(void);
+void print_per_system_splits(void);
+void stats_reset_interval(HOST_ENTRY *h);
+void print_netdata(void);
+void print_global_stats(void);
+void main_loop();
+void signal_handler(int);
+void finish();
+const char *sprint_tm(int64_t t);
+void ev_enqueue(struct event_queue *queue, struct event *event);
+struct event *ev_dequeue(struct event_queue *queue);
+void ev_remove(struct event_queue *queue, struct event *event);
+void add_cidr(char *);
+void add_range(char *, char *);
+void add_addr_range_ipv4(unsigned long, unsigned long);
+void print_warning(char *fmt, ...);
+int addr_cmp(struct sockaddr *a, struct sockaddr *b);
+void host_add_ping_event(HOST_ENTRY *h, int index, int64_t ev_time);
+void host_add_timeout_event(HOST_ENTRY *h, int index, int64_t ev_time);
+struct event *host_get_timeout_event(HOST_ENTRY *h, int index);
+void stats_add(HOST_ENTRY *h, int index, int success, int64_t latency);
+void update_current_time();
+void print_timestamp_format(int64_t current_time_ns, int timestamp_format);
+static uint32_t ms_since_midnight_utc(int64_t time_val);
+
+/************************************************************
+
+ Function: p_setsockopt
+
+*************************************************************
+
+ Inputs: p_uid: privileged uid. Others as per setsockopt(2)
+
+ Description:
+
+ Elevates privileges to p_uid when required, calls
+ setsockopt, and drops privileges back.
+
+************************************************************/
+
+int p_setsockopt(uid_t p_uid, int sockfd, int level, int optname,
+ const void *optval, socklen_t optlen)
+{
+ const uid_t saved_uid = geteuid();
+ int res;
+
+ if (p_uid != saved_uid && seteuid(p_uid)) {
+ perror("cannot elevate privileges for setsockopt");
+ }
+
+ res = setsockopt(sockfd, level, optname, optval, optlen);
+
+ if (p_uid != saved_uid && seteuid(saved_uid)) {
+ perror("fatal error: could not drop privileges after setsockopt");
+ /* continuing would be a security hole */
+ exit(4);
+ }
+
+ return res;
+}
+
+/************************************************************
+
+ Function: main
+
+*************************************************************
+
+ Inputs: int argc, char** argv
+
+ Description:
+
+ Main program entry point
+
+************************************************************/
+
+int main(int argc, char **argv)
+{
+/* Debug: CPU Performance */
+#if defined(DEBUG) || defined(_DEBUG)
+ clock_t perf_cpu_start, perf_cpu_end;
+ double perf_cpu_time_used;
+ perf_cpu_start = clock();
+#endif /* DEBUG || _DEBUG */
+
+ int c;
+ const uid_t suid = geteuid();
+ int tos = 0;
+ struct optparse optparse_state;
+#ifdef USE_SIGACTION
+ struct sigaction act;
+#endif
+
+ /* pre-parse -h/--help, so that we also can output help information
+ * without trying to open the socket, which might fail */
+ prog = argv[0];
+ if (argc == 2 && (strcmp(argv[1], "-h") == 0 || strcmp(argv[1], "--help") == 0)) {
+ usage(0);
+ }
+
+ socket4 = open_ping_socket_ipv4(&socktype4);
+#ifdef __linux__
+ /* We only treat SOCK_DGRAM differently on Linux, where the IPv4 header
+ * structure is missing in the message.
+ */
+ using_sock_dgram4 = (socktype4 == SOCK_DGRAM);
+#endif
+
+#ifdef IPV6
+ socket6 = open_ping_socket_ipv6(&socktype6);
+ /* if called (sym-linked) via 'fping6', imply '-6'
+ * for backward compatibility */
+ if (strstr(prog, "fping6")) {
+ hints_ai_family = AF_INET6;
+ }
+#endif
+
+ memset(&src_addr, 0, sizeof(src_addr));
+#ifdef IPV6
+ memset(&src_addr6, 0, sizeof(src_addr6));
+#endif
+
+ if (!suid && suid != getuid()) {
+ /* *temporarily* drop privileges */
+ if (seteuid(getuid()) == -1)
+ perror("cannot setuid");
+ }
+
+ optparse_init(&optparse_state, argv);
+ ident4 = ident6 = htons(getpid() & 0xFFFF);
+ verbose_flag = 1;
+ backoff_flag = 1;
+ opterr = 1;
+
+ /* get command line options */
+
+ struct optparse_long longopts[] = {
+ { "ipv4", '4', OPTPARSE_NONE },
+ { "ipv6", '6', OPTPARSE_NONE },
+ { "alive", 'a', OPTPARSE_NONE },
+ { "addr", 'A', OPTPARSE_NONE },
+ { "size", 'b', OPTPARSE_REQUIRED },
+ { "backoff", 'B', OPTPARSE_REQUIRED },
+ { "count", 'c', OPTPARSE_REQUIRED },
+ { "vcount", 'C', OPTPARSE_REQUIRED },
+ { "rdns", 'd', OPTPARSE_NONE },
+ { "timestamp", 'D', OPTPARSE_NONE },
+ { "timestamp-format", '0', OPTPARSE_REQUIRED },
+ { "elapsed", 'e', OPTPARSE_NONE },
+ { "file", 'f', OPTPARSE_REQUIRED },
+ { "generate", 'g', OPTPARSE_NONE },
+ { "help", 'h', OPTPARSE_NONE },
+ { "ttl", 'H', OPTPARSE_REQUIRED },
+ { "interval", 'i', OPTPARSE_REQUIRED },
+ { "iface", 'I', OPTPARSE_REQUIRED },
+ { "icmp-timestamp", '0', OPTPARSE_NONE },
+#ifdef SO_MARK
+ { "fwmark", 'k', OPTPARSE_REQUIRED },
+#endif
+ { "loop", 'l', OPTPARSE_NONE },
+ { "all", 'm', OPTPARSE_NONE },
+ { "dontfrag", 'M', OPTPARSE_NONE },
+ { "name", 'n', OPTPARSE_NONE },
+ { "netdata", 'N', OPTPARSE_NONE },
+ { "outage", 'o', OPTPARSE_NONE },
+ { "tos", 'O', OPTPARSE_REQUIRED },
+ { "period", 'p', OPTPARSE_REQUIRED },
+ { "quiet", 'q', OPTPARSE_NONE },
+ { "squiet", 'Q', OPTPARSE_REQUIRED },
+ { "retry", 'r', OPTPARSE_REQUIRED },
+ { "random", 'R', OPTPARSE_NONE },
+ { "stats", 's', OPTPARSE_NONE },
+ { "src", 'S', OPTPARSE_REQUIRED },
+ { "timeout", 't', OPTPARSE_REQUIRED },
+ { NULL, 'T', OPTPARSE_REQUIRED },
+ { "unreach", 'u', OPTPARSE_NONE },
+ { "version", 'v', OPTPARSE_NONE },
+ { "reachable", 'x', OPTPARSE_REQUIRED },
+ { "fast-reachable", 'X', OPTPARSE_REQUIRED },
+ { "check-source", '0', OPTPARSE_NONE },
+ { "print-tos", '0', OPTPARSE_NONE },
+ { "print-ttl", '0', OPTPARSE_NONE },
+#if defined(DEBUG) || defined(_DEBUG)
+ { NULL, 'z', OPTPARSE_REQUIRED },
+#endif
+ { 0, 0, 0 }
+ };
+
+ float opt_value_float;
+ while ((c = optparse_long(&optparse_state, longopts, NULL)) != EOF) {
+ switch (c) {
+ case '0':
+ if(strstr(optparse_state.optlongname, "timestamp-format") != NULL) {
+ if(strcmp(optparse_state.optarg, "ctime") == 0) {
+ timestamp_format_flag = 1;
+ }else if(strcmp(optparse_state.optarg, "iso") == 0) {
+ timestamp_format_flag = 2;
+ }else if(strcmp(optparse_state.optarg, "rfc3339") == 0) {
+ timestamp_format_flag = 3;
+ }else{
+ usage(1);
+ }
+ } else if (strstr(optparse_state.optlongname, "check-source") != NULL) {
+ check_source_flag = 1;
+ } else if (strstr(optparse_state.optlongname, "icmp-timestamp") != NULL) {
+#ifdef IPV6
+ if (hints_ai_family != AF_UNSPEC && hints_ai_family != AF_INET) {
+ fprintf(stderr, "%s: ICMP Timestamp is IPv4 only\n", prog);
+ exit(1);
+ }
+ hints_ai_family = AF_INET;
+#endif
+ icmp_request_typ = 13;
+ ping_data_size = ICMP_TIMESTAMP_DATA_SIZE;
+ } else if (strstr(optparse_state.optlongname, "print-tos") != NULL) {
+ print_tos_flag = 1;
+ } else if (strstr(optparse_state.optlongname, "print-ttl") != NULL) {
+ print_ttl_flag = 1;
+ } else {
+ usage(1);
+ }
+ break;
+ case '4':
+#ifdef IPV6
+ if (hints_ai_family != AF_UNSPEC && hints_ai_family != AF_INET) {
+ fprintf(stderr, "%s: can't specify both -4 and -6\n", prog);
+ exit(1);
+ }
+ hints_ai_family = AF_INET;
+#endif
+ break;
+ case '6':
+#ifdef IPV6
+ if (hints_ai_family != AF_UNSPEC && hints_ai_family != AF_INET6) {
+ fprintf(stderr, "%s: can't specify both -4 and -6\n", prog);
+ exit(1);
+ }
+ hints_ai_family = AF_INET6;
+#else
+ fprintf(stderr, "%s: IPv6 not supported by this binary\n", prog);
+ exit(1);
+#endif
+ break;
+ case 'M':
+#ifdef IP_MTU_DISCOVER
+ if (socket4 >= 0) {
+ int val = IP_PMTUDISC_DO;
+ if (setsockopt(socket4, IPPROTO_IP, IP_MTU_DISCOVER, &val, sizeof(val))) {
+ perror("setsockopt IP_MTU_DISCOVER");
+ }
+ }
+#ifdef IPV6
+ if (socket6 >= 0) {
+ int val = IPV6_PMTUDISC_DO;
+ if (setsockopt(socket6, IPPROTO_IPV6, IPV6_MTU_DISCOVER, &val, sizeof(val))) {
+ perror("setsockopt IPV6_MTU_DISCOVER");
+ }
+ }
+#endif
+#else
+ fprintf(stderr, "%s, -M option not supported on this platform\n", prog);
+ exit(1);
+#endif
+ break;
+
+ case 't':
+ if (sscanf(optparse_state.optarg, "%f", &opt_value_float) != 1)
+ usage(1);
+ if (opt_value_float < 0) {
+ usage(1);
+ }
+ timeout = opt_value_float * 1000000;
+ timeout_flag = 1;
+ break;
+
+ case 'r':
+ if (sscanf(optparse_state.optarg, "%u", &retry) != 1)
+ usage(1);
+ break;
+
+ case 'i':
+ if (sscanf(optparse_state.optarg, "%f", &opt_value_float) != 1)
+ usage(1);
+ if (opt_value_float < 0) {
+ usage(1);
+ }
+ interval = opt_value_float * 1000000;
+ break;
+
+ case 'p':
+ if (sscanf(optparse_state.optarg, "%f", &opt_value_float) != 1)
+ usage(1);
+ if (opt_value_float < 0) {
+ usage(1);
+ }
+ perhost_interval = opt_value_float * 1000000;
+
+ break;
+
+ case 'c':
+ if (!(count = (unsigned int)atoi(optparse_state.optarg)))
+ usage(1);
+
+ count_flag = 1;
+ break;
+
+ case 'C':
+ if (!(count = (unsigned int)atoi(optparse_state.optarg)))
+ usage(1);
+
+ count_flag = 1;
+ report_all_rtts_flag = 1;
+ break;
+
+ case 'b':
+ if (sscanf(optparse_state.optarg, "%u", &ping_data_size) != 1)
+ usage(1);
+ size_flag = 1;
+ break;
+
+ case 'h':
+ usage(0);
+ break;
+
+ case 'q':
+ verbose_flag = 0;
+ quiet_flag = 1;
+ break;
+
+ case 'Q':
+ verbose_flag = 0;
+ quiet_flag = 1;
+ if (sscanf(optparse_state.optarg, "%f", &opt_value_float) != 1)
+ usage(1);
+ if (opt_value_float < 0) {
+ usage(1);
+ }
+ report_interval = opt_value_float * 1e9;
+
+ /* recognize keyword(s) after number, ignore everything else */
+ {
+ char *comma = strchr(optparse_state.optarg, ',');
+ if ((comma != NULL) && (strcmp(++comma, "cumulative") == 0)) {
+ cumulative_stats_flag = 1;
+ }
+ }
+
+ break;
+
+ case 'e':
+ elapsed_flag = 1;
+ break;
+
+ case 'm':
+ multif_flag = 1;
+ break;
+
+ case 'N':
+ netdata_flag = 1;
+ break;
+
+ case 'n':
+ name_flag = 1;
+ if (rdns_flag) {
+ fprintf(stderr, "%s: use either one of -d or -n\n", prog);
+ exit(1);
+ }
+ break;
+
+ case 'd':
+ rdns_flag = 1;
+ if (name_flag) {
+ fprintf(stderr, "%s: use either one of -d or -n\n", prog);
+ exit(1);
+ }
+ break;
+
+ case 'A':
+ addr_flag = 1;
+ break;
+
+ case 'B':
+ if (!(backoff = atof(optparse_state.optarg)))
+ usage(1);
+
+ break;
+
+ case 's':
+ stats_flag = 1;
+ break;
+
+ case 'D':
+ timestamp_flag = 1;
+ break;
+
+ case 'R':
+ random_data_flag = 1;
+ break;
+
+ case 'l':
+ loop_flag = 1;
+ backoff_flag = 0;
+ break;
+
+ case 'u':
+ unreachable_flag = 1;
+ break;
+
+ case 'a':
+ alive_flag = 1;
+ break;
+
+ case 'H':
+ if (!(ttl = (unsigned int)atoi(optparse_state.optarg)))
+ usage(1);
+ break;
+
+#if defined(DEBUG) || defined(_DEBUG)
+ case 'z':
+ if (sscanf(optparse_state.optarg, "0x%x", &debugging) != 1)
+ if (sscanf(optparse_state.optarg, "%u", &debugging) != 1)
+ usage(1);
+
+ break;
+#endif /* DEBUG || _DEBUG */
+
+ case 'v':
+ printf("%s: Version %s\n", prog, VERSION);
+ exit(0);
+
+ case 'x':
+ if (!(min_reachable = (unsigned int)atoi(optparse_state.optarg)))
+ usage(1);
+ break;
+
+ case 'X':
+ if (!(min_reachable = (unsigned int)atoi(optparse_state.optarg)))
+ usage(1);
+ fast_reachable = 1;
+ break;
+
+ 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 == p_setsockopt(suid, socket4, SOL_SOCKET, SO_MARK, &fwmark, sizeof fwmark))
+ perror("fwmark ipv4");
+
+#ifdef IPV6
+ if (socket6 >= 0)
+ if(-1 == p_setsockopt(suid, socket6, SOL_SOCKET, SO_MARK, &fwmark, sizeof fwmark))
+ perror("fwmark ipv6");
+#endif
+
+ break;
+#endif
+
+ case 'g':
+ /* use IP list generation */
+ /* mutually exclusive with using file input or command line targets */
+ generate_flag = 1;
+ break;
+
+ case 'S':
+ if (inet_pton(AF_INET, optparse_state.optarg, &src_addr)) {
+ src_addr_set = 1;
+ break;
+ }
+#ifdef IPV6
+ if (inet_pton(AF_INET6, optparse_state.optarg, &src_addr6)) {
+ src_addr6_set = 1;
+ break;
+ }
+#endif
+ fprintf(stderr, "%s: can't parse source address: %s\n", prog, optparse_state.optarg);
+ exit(1);
+
+ case 'I':
+#ifdef SO_BINDTODEVICE
+ if (socket4 >= 0) {
+ if (p_setsockopt(suid, socket4, SOL_SOCKET, SO_BINDTODEVICE, optparse_state.optarg, strlen(optparse_state.optarg))) {
+ perror("binding to specific interface (SO_BINTODEVICE)");
+ exit(1);
+ }
+ }
+#ifdef IPV6
+ if (socket6 >= 0) {
+ if (p_setsockopt(suid, socket6, SOL_SOCKET, SO_BINDTODEVICE, optparse_state.optarg, strlen(optparse_state.optarg))) {
+ perror("binding to specific interface (SO_BINTODEVICE), IPV6");
+ exit(1);
+ }
+ }
+#endif
+#else
+ printf("%s: cant bind to a particular net interface since SO_BINDTODEVICE is not supported on your os.\n", prog);
+ exit(3);
+ ;
+#endif
+ break;
+
+ case 'T':
+ /* This option is ignored for compatibility reasons ("select timeout" is not meaningful anymore) */
+ break;
+
+ case 'O':
+ if (sscanf(optparse_state.optarg, "%i", &tos) == 1) {
+ if (socket4 >= 0) {
+ if (setsockopt(socket4, IPPROTO_IP, IP_TOS, &tos, sizeof(tos))) {
+ perror("setting type of service octet IP_TOS");
+ }
+ }
+#if defined(IPV6) && defined(IPV6_TCLASS)
+ if (socket6 >= 0) {
+ if (setsockopt(socket6, IPPROTO_IPV6, IPV6_TCLASS, &tos, sizeof(tos))) {
+ perror("setting type of service octet IPV6_TCLASS");
+ }
+ }
+#endif
+ }
+ else {
+ usage(1);
+ }
+ break;
+
+ case 'o':
+ outage_flag = 1;
+ break;
+
+ case '?':
+ fprintf(stderr, "%s: %s\n", argv[0], optparse_state.errmsg);
+ fprintf(stderr, "see 'fping -h' for usage information\n");
+ exit(1);
+ break;
+ }
+ }
+
+ /* permanently drop privileges */
+ if (suid != getuid() && setuid(getuid())) {
+ perror("fatal: failed to permanently drop privileges");
+ /* continuing would be a security hole */
+ exit(4);
+ }
+
+ /* validate various option settings */
+
+#ifndef IPV6
+ if (socket4 < 0) {
+ crash_and_burn("can't create socket (must run as root?)");
+ }
+#else
+ if ((socket4 < 0 && socket6 < 0) || (hints_ai_family == AF_INET6 && socket6 < 0)) {
+ crash_and_burn("can't create socket (must run as root?)");
+ }
+#endif
+
+ if (ttl > 255) {
+ fprintf(stderr, "%s: ttl %u out of range\n", prog, ttl);
+ exit(1);
+ }
+
+ if (unreachable_flag && alive_flag) {
+ fprintf(stderr, "%s: specify only one of a, u\n", prog);
+ exit(1);
+ }
+
+ if (count_flag && loop_flag) {
+ fprintf(stderr, "%s: specify only one of c, l\n", prog);
+ exit(1);
+ }
+
+ if (interval < (float)MIN_INTERVAL_MS * 1000000 && getuid()) {
+ fprintf(stderr, "%s: -i must be >= %g\n", prog, (float)MIN_INTERVAL_MS);
+ exit(1);
+ }
+
+ if (perhost_interval < (float)MIN_PERHOST_INTERVAL_MS * 1000000 && getuid()) {
+ fprintf(stderr, "%s: -p must be >= %g\n", prog, (float)MIN_PERHOST_INTERVAL_MS);
+ exit(1);
+ }
+
+ if (ping_data_size > MAX_PING_DATA) {
+ fprintf(stderr, "%s: data size %u not valid, must not be larger than %u\n",
+ prog, ping_data_size, (unsigned int)MAX_PING_DATA);
+ exit(1);
+ }
+
+ if ((backoff > MAX_BACKOFF_FACTOR) || (backoff < MIN_BACKOFF_FACTOR)) {
+ fprintf(stderr, "%s: backoff factor %.1f not valid, must be between %.1f and %.1f\n",
+ prog, backoff, MIN_BACKOFF_FACTOR, MAX_BACKOFF_FACTOR);
+ exit(1);
+ }
+
+ if (icmp_request_typ == 13 && size_flag != 0) {
+ fprintf(stderr, "%s: cannot change ICMP Timestamp size\n", prog);
+ exit(1);
+ }
+
+ if (count_flag) {
+ if (verbose_flag)
+ per_recv_flag = 1;
+
+ alive_flag = unreachable_flag = verbose_flag = 0;
+ }
+
+ if (loop_flag) {
+ if (!report_interval)
+ per_recv_flag = 1;
+
+ alive_flag = unreachable_flag = verbose_flag = 0;
+ }
+
+ if (alive_flag || unreachable_flag || min_reachable)
+ verbose_flag = 0;
+
+ trials = (count > retry + 1) ? count : retry + 1;
+
+ /* auto-tune default timeout for count/loop modes
+ * see also github #32 */
+ if (loop_flag || count_flag) {
+ if (!timeout_flag) {
+ timeout = perhost_interval;
+ if (timeout > (int64_t)AUTOTUNE_TIMEOUT_MAX * 1000000) {
+ timeout = (int64_t)AUTOTUNE_TIMEOUT_MAX * 1000000;
+ }
+ }
+ }
+
+#if defined(DEBUG) || defined(_DEBUG)
+ if (debugging & DBG_TRACE)
+ trace_flag = 1;
+
+ if (debugging & DBG_RANDOM_LOSE_FEW) {
+ randomly_lose_flag = 1;
+ lose_factor = 1; /* ie, 1/4 */
+ }
+
+ if (debugging & DBG_RANDOM_LOSE_MANY) {
+ randomly_lose_flag = 1;
+ lose_factor = 5; /* ie, 3/4 */
+ }
+
+ if (debugging & DBG_PRINT_PER_SYSTEM)
+ print_per_system_flag = 1;
+
+ if ((debugging & DBG_REPORT_ALL_RTTS) && !loop_flag)
+ report_all_rtts_flag = 1;
+
+ if (trace_flag) {
+ fprintf(stderr, "%s:\n count: %u, retry: %u, interval: %.0f ms\n",
+ prog, count, retry, interval / 1e6);
+ fprintf(stderr, " perhost_interval: %.0f ms, timeout: %.0f\n",
+ perhost_interval / 1e6, timeout / 1e6);
+ fprintf(stderr, " ping_data_size = %u, trials = %u\n",
+ ping_data_size, trials);
+
+ if (verbose_flag)
+ fprintf(stderr, " verbose_flag set\n");
+ if (multif_flag)
+ fprintf(stderr, " multif_flag set\n");
+ if (name_flag)
+ fprintf(stderr, " name_flag set\n");
+ if (addr_flag)
+ fprintf(stderr, " addr_flag set\n");
+ if (stats_flag)
+ fprintf(stderr, " stats_flag set\n");
+ if (unreachable_flag)
+ fprintf(stderr, " unreachable_flag set\n");
+ if (alive_flag)
+ fprintf(stderr, " alive_flag set\n");
+ if (elapsed_flag)
+ fprintf(stderr, " elapsed_flag set\n");
+ if (version_flag)
+ fprintf(stderr, " version_flag set\n");
+ if (count_flag)
+ fprintf(stderr, " count_flag set\n");
+ if (loop_flag)
+ fprintf(stderr, " loop_flag set\n");
+ if (backoff_flag)
+ fprintf(stderr, " backoff_flag set\n");
+ if (per_recv_flag)
+ fprintf(stderr, " per_recv_flag set\n");
+ if (report_all_rtts_flag)
+ fprintf(stderr, " report_all_rtts_flag set\n");
+ if (randomly_lose_flag)
+ fprintf(stderr, " randomly_lose_flag set\n");
+ if (print_per_system_flag)
+ fprintf(stderr, " print_per_system_flag set\n");
+ if (outage_flag)
+ fprintf(stderr, " outage_flag set\n");
+ if (netdata_flag)
+ fprintf(stderr, " netdata_flag set\n");
+ }
+#endif /* DEBUG || _DEBUG */
+
+ /* set the TTL, if the -H option was set (otherwise ttl will be = 0) */
+ if (ttl > 0) {
+ if (socket4 >= 0) {
+ if (setsockopt(socket4, IPPROTO_IP, IP_TTL, &ttl, sizeof(ttl))) {
+ perror("setting time to live");
+ }
+ }
+#ifdef IPV6
+ if (socket6 >= 0) {
+ if (setsockopt(socket6, IPPROTO_IPV6, IPV6_UNICAST_HOPS, &ttl, sizeof(ttl))) {
+ perror("setting time to live");
+ }
+ }
+#endif
+ }
+
+#if HAVE_SO_TIMESTAMPNS
+ {
+ int opt = 1;
+ if (socket4 >= 0) {
+ if (setsockopt(socket4, SOL_SOCKET, SO_TIMESTAMPNS, &opt, sizeof(opt))) {
+ if (setsockopt(socket4, SOL_SOCKET, SO_TIMESTAMP, &opt, sizeof(opt))) {
+ perror("setting SO_TIMESTAMPNS and SO_TIMESTAMP option");
+ }
+ }
+ }
+#ifdef IPV6
+ if (socket6 >= 0) {
+ if (setsockopt(socket6, SOL_SOCKET, SO_TIMESTAMPNS, &opt, sizeof(opt))) {
+ if (setsockopt(socket6, SOL_SOCKET, SO_TIMESTAMP, &opt, sizeof(opt))) {
+ perror("setting SO_TIMESTAMPNS and SO_TIMESTAMP option (IPv6)");
+ }
+ }
+ }
+#endif
+ }
+#endif
+
+ update_current_time();
+ start_time = current_time_ns;
+
+ /* handle host names supplied on command line or in a file */
+ /* if the generate_flag is on, then generate the IP list */
+
+ argv = &argv[optparse_state.optind];
+ argc -= optparse_state.optind;
+
+ /* calculate how many ping can be in-flight per host */
+ if (count_flag) {
+ event_storage_count = count;
+ }
+ else if (loop_flag) {
+ if (perhost_interval > timeout) {
+ event_storage_count = 1;
+ }
+ else {
+ event_storage_count = 1 + timeout / perhost_interval;
+ }
+ }
+ else {
+ event_storage_count = 1;
+ }
+
+ /* file and generate are mutually exclusive */
+ /* file and command line are mutually exclusive */
+ /* generate requires command line parameters beyond the switches */
+ if ((*argv && filename) || (filename && generate_flag) || (generate_flag && !*argv))
+ usage(1);
+
+ /* if no conditions are specified, then assume input from stdin */
+ if (!*argv && !filename && !generate_flag)
+ filename = "-";
+
+ if (*argv && !generate_flag) {
+ while (*argv) {
+ add_name(*argv);
+ ++argv;
+ }
+ }
+ else if (filename) {
+ FILE *ping_file;
+ char line[132];
+ char host[132];
+
+ if (strcmp(filename, "-") == 0)
+ ping_file = fdopen(0, "r");
+ else
+ ping_file = fopen(filename, "r");
+
+ if (!ping_file)
+ errno_crash_and_burn("fopen");
+
+ while (fgets(line, sizeof(line), ping_file)) {
+ if (sscanf(line, "%s", host) != 1)
+ continue;
+
+ if ((!*host) || (host[0] == '#')) /* magic to avoid comments */
+ continue;
+
+ add_name(host);
+ }
+
+ fclose(ping_file);
+ }
+ else if (*argv && generate_flag) {
+ if (argc == 1) {
+ /* one target: we expect a cidr range (n.n.n.n/m) */
+ add_cidr(argv[0]);
+ }
+ else if (argc == 2) {
+ add_range(argv[0], argv[1]);
+ }
+ else {
+ usage(1);
+ }
+ }
+ else {
+ usage(1);
+ }
+
+ if (!num_hosts) {
+ exit(num_noaddress ? 2 : 1);
+ }
+
+ if (socket4 >= 0 && (src_addr_set || socktype4 == SOCK_DGRAM)) {
+ socket_set_src_addr_ipv4(socket4, &src_addr, (socktype4 == SOCK_DGRAM) ? &ident4 : NULL);
+ }
+#ifdef IPV6
+ if (socket6 >= 0 && (src_addr6_set || socktype6 == SOCK_DGRAM)) {
+ socket_set_src_addr_ipv6(socket6, &src_addr6, (socktype6 == SOCK_DGRAM) ? &ident6 : NULL);
+ }
+#endif
+
+ /* allocate and initialize array to map host nr to host_entry */
+ {
+ struct event *cursor = event_queue_ping.first;
+ int i = 0;
+ table = (HOST_ENTRY **)calloc(num_hosts, sizeof(HOST_ENTRY *));
+ if (!table)
+ crash_and_burn("Can't malloc array of hosts");
+ /* initialize table of hosts. we know that we have ping events scheduled
+ * for each of them */
+ for (cursor = event_queue_ping.first; cursor; cursor = cursor->ev_next) {
+ table[i] = cursor->host;
+ cursor->host->i = i;
+ i++;
+ }
+ }
+
+ init_ping_buffer_ipv4(ping_data_size);
+#ifdef IPV6
+ init_ping_buffer_ipv6(ping_data_size);
+#endif
+
+#ifdef USE_SIGACTION
+ memset(&act, 0, sizeof(act));
+ act.sa_handler = signal_handler;
+ sigemptyset(&act.sa_mask);
+ sigaddset(&act.sa_mask, SIGINT);
+ sigaddset(&act.sa_mask, SIGQUIT);
+ act.sa_flags = SA_RESTART;
+ if (sigaction(SIGQUIT, &act, NULL) || sigaction(SIGINT, &act, NULL)) {
+ crash_and_burn("failure to set signal handler");
+ }
+#else
+ signal(SIGINT, signal_handler);
+ signal(SIGQUIT, signal_handler);
+#endif
+ setlinebuf(stdout);
+
+ if (report_interval) {
+ next_report_time = current_time_ns + report_interval;
+ }
+
+ last_send_time = 0;
+
+ seqmap_init();
+
+ /* main loop */
+ main_loop();
+
+/* Debug: CPU Performance */
+#if defined(DEBUG) || defined(_DEBUG)
+ perf_cpu_end = clock();
+ perf_cpu_time_used = ((double) (perf_cpu_end - perf_cpu_start)) / CLOCKS_PER_SEC;
+ printf("[DEBUG] CPU time used: %f sec", perf_cpu_time_used);
+#endif /* DEBUG || _DEBUG */
+
+ finish();
+
+ return 0;
+}
+
+static inline int64_t timespec_ns(struct timespec *a)
+{
+ return ((int64_t)a->tv_sec * 1000000000) + a->tv_nsec;
+}
+
+void add_cidr(char *addr)
+{
+ char *addr_end;
+ char *mask_str;
+ unsigned long mask;
+ unsigned long bitmask;
+ int ret;
+ struct addrinfo addr_hints;
+ struct addrinfo *addr_res;
+ unsigned long net_addr;
+ unsigned long net_last;
+
+ /* Split address from mask */
+ addr_end = strchr(addr, '/');
+ if (addr_end == NULL) {
+ usage(1);
+ }
+ *addr_end = '\0';
+ mask_str = addr_end + 1;
+ mask = atoi(mask_str);
+
+ /* parse address (IPv4 only) */
+ memset(&addr_hints, 0, sizeof(struct addrinfo));
+ addr_hints.ai_family = AF_UNSPEC;
+ addr_hints.ai_flags = AI_NUMERICHOST;
+ ret = getaddrinfo(addr, NULL, &addr_hints, &addr_res);
+ if (ret) {
+ fprintf(stderr, "%s, can't parse address %s: %s\n", prog, addr, gai_strerror(ret));
+ exit(1);
+ }
+ if (addr_res->ai_family != AF_INET) {
+ fprintf(stderr, "%s: -g works only with IPv4 addresses\n", prog);
+ exit(1);
+ }
+ net_addr = ntohl(((struct sockaddr_in *)addr_res->ai_addr)->sin_addr.s_addr);
+ freeaddrinfo(addr_res);
+
+ /* check mask */
+ if (mask < 1 || mask > 32) {
+ fprintf(stderr, "%s: netmask must be between 1 and 32 (is: %s)\n", prog, mask_str);
+ exit(1);
+ }
+
+ /* convert mask integer from 1 to 32 to a bitmask */
+ bitmask = ((unsigned long)0xFFFFFFFF) << (32 - mask);
+
+ /* calculate network range */
+ net_addr &= bitmask;
+ net_last = net_addr + ((unsigned long)0x1 << (32 - mask)) - 1;
+
+ /* exclude network and broadcast address for regular prefixes */
+ if (mask < 31) {
+ net_last--;
+ net_addr++;
+ }
+
+ /* add all hosts in that network (net_addr and net_last inclusive) */
+ add_addr_range_ipv4(net_addr, net_last);
+}
+
+void add_range(char *start, char *end)
+{
+ struct addrinfo addr_hints;
+ struct addrinfo *addr_res;
+ unsigned long start_long;
+ unsigned long end_long;
+ int ret;
+
+ /* parse start address (IPv4 only) */
+ memset(&addr_hints, 0, sizeof(struct addrinfo));
+ addr_hints.ai_family = AF_UNSPEC;
+ addr_hints.ai_flags = AI_NUMERICHOST;
+ ret = getaddrinfo(start, NULL, &addr_hints, &addr_res);
+ if (ret) {
+ fprintf(stderr, "%s: can't parse address %s: %s\n", prog, start, gai_strerror(ret));
+ exit(1);
+ }
+ if (addr_res->ai_family != AF_INET) {
+ freeaddrinfo(addr_res);
+ fprintf(stderr, "%s: -g works only with IPv4 addresses\n", prog);
+ exit(1);
+ }
+ start_long = ntohl(((struct sockaddr_in *)addr_res->ai_addr)->sin_addr.s_addr);
+
+ /* parse end address (IPv4 only) */
+ memset(&addr_hints, 0, sizeof(struct addrinfo));
+ addr_hints.ai_family = AF_UNSPEC;
+ addr_hints.ai_flags = AI_NUMERICHOST;
+ ret = getaddrinfo(end, NULL, &addr_hints, &addr_res);
+ if (ret) {
+ fprintf(stderr, "%s: can't parse address %s: %s\n", prog, end, gai_strerror(ret));
+ exit(1);
+ }
+ if (addr_res->ai_family != AF_INET) {
+ freeaddrinfo(addr_res);
+ fprintf(stderr, "%s: -g works only with IPv4 addresses\n", prog);
+ exit(1);
+ }
+ end_long = ntohl(((struct sockaddr_in *)addr_res->ai_addr)->sin_addr.s_addr);
+ freeaddrinfo(addr_res);
+
+ /* add IPv4 addresses from closed interval [start_long,end_long] */
+ add_addr_range_ipv4(start_long, end_long);
+}
+
+void add_addr_range_ipv4(unsigned long start_long, unsigned long end_long)
+{
+ /* check if generator limit is exceeded */
+ if (end_long >= start_long + MAX_GENERATE) {
+ fprintf(stderr, "%s: -g parameter generates too many addresses\n", prog);
+ exit(1);
+ }
+
+ /* generate */
+ for (; start_long <= end_long; start_long++) {
+ struct in_addr in_addr_tmp;
+ char buffer[20];
+ in_addr_tmp.s_addr = htonl(start_long);
+ inet_ntop(AF_INET, &in_addr_tmp, buffer, sizeof(buffer));
+ add_name(buffer);
+ }
+}
+
+void main_loop()
+{
+ int64_t lt;
+ int64_t wait_time_ns;
+ struct event *event;
+ struct host_entry *h;
+
+ while (event_queue_ping.first || event_queue_timeout.first) {
+ dbg_printf("%s", "# main_loop\n");
+
+ /* timeout event ? */
+ if (event_queue_timeout.first && event_queue_timeout.first->ev_time - current_time_ns <= 0) {
+ event = ev_dequeue(&event_queue_timeout);
+ h = event->host;
+
+ dbg_printf("%s [%d]: timeout event\n", h->host, event->ping_index);
+
+ stats_add(h, event->ping_index, 0, -1);
+
+ if (per_recv_flag) {
+ if (timestamp_flag) {
+ print_timestamp_format(current_time_ns, timestamp_format_flag);
+ }
+ printf("%-*s : [%d], timed out",
+ max_hostname_len, h->host, event->ping_index);
+ if (h->num_recv > 0) {
+ printf(" (%s avg, ", sprint_tm(h->total_time / h->num_recv));
+ }
+ else {
+ printf(" (NaN avg, ");
+ }
+ if (h->num_recv <= h->num_sent) {
+ printf("%d%% loss)",
+ ((h->num_sent - h->num_recv) * 100) / h->num_sent);
+ }
+ else {
+ printf("%d%% return)",
+ (h->num_recv_total * 100) / h->num_sent);
+ }
+ printf("\n");
+ }
+
+ /* do we need to send a retry? */
+ if (!loop_flag && !count_flag) {
+ if (h->num_sent < retry + 1) {
+ if (backoff_flag) {
+ h->timeout *= backoff;
+ }
+ send_ping(h, event->ping_index);
+ }
+ }
+
+ /* note: we process first timeout events, because we might need to
+ * wait to process ping events, while we for sure never need to
+ * wait for timeout events.
+ */
+ continue;
+ }
+
+ /* ping event ? */
+ if (event_queue_ping.first && event_queue_ping.first->ev_time - current_time_ns <= 0) {
+ /* Make sure that we don't ping more than once every "interval" */
+ lt = current_time_ns - last_send_time;
+ if (lt < interval)
+ goto wait_for_reply;
+
+ /* Dequeue the event */
+ event = ev_dequeue(&event_queue_ping);
+ h = event->host;
+
+ dbg_printf("%s [%d]: ping event\n", h->host, event->ping_index);
+
+ /* Send the ping */
+ send_ping(h, event->ping_index);
+
+ /* Loop and count mode: schedule next ping */
+ if (loop_flag || (count_flag && event->ping_index + 1 < count)) {
+ host_add_ping_event(h, event->ping_index + 1, event->ev_time + perhost_interval);
+ }
+ }
+
+ wait_for_reply:
+
+ /* When is the next ping next event? */
+ wait_time_ns = -1;
+ if (event_queue_ping.first) {
+ wait_time_ns = event_queue_ping.first->ev_time - current_time_ns;
+ if (wait_time_ns < 0)
+ wait_time_ns = 0;
+ /* make sure that we wait enough, so that the inter-ping delay is
+ * bigger than 'interval' */
+ if (wait_time_ns < interval) {
+ lt = current_time_ns - last_send_time;
+ if (lt < interval) {
+ wait_time_ns = interval - lt;
+ }
+ }
+
+ dbg_printf("next ping event in %.0f ms (%s)\n", wait_time_ns / 1e6, event_queue_ping.first->host->host);
+ }
+
+ /* When is the next timeout event? */
+ if (event_queue_timeout.first) {
+ int64_t wait_time_timeout = event_queue_timeout.first->ev_time - current_time_ns;
+ if (wait_time_ns < 0 || wait_time_timeout < wait_time_ns) {
+ wait_time_ns = wait_time_timeout;
+ if (wait_time_ns < 0) {
+ wait_time_ns = 0;
+ }
+ }
+
+ dbg_printf("next timeout event in %.0f ms (%s)\n", wait_time_timeout / 1e6, event_queue_timeout.first->host->host);
+ }
+
+ /* When is the next report due? */
+ if (report_interval && (loop_flag || count_flag)) {
+ int64_t wait_time_next_report = next_report_time - current_time_ns;
+ if (wait_time_next_report < wait_time_ns) {
+ wait_time_ns = wait_time_next_report;
+ if (wait_time_ns < 0) {
+ wait_time_ns = 0;
+ }
+ }
+
+ dbg_printf("next report event in %0.f ms\n", wait_time_next_report / 1e6);
+ }
+
+ /* if wait_time is still -1, it means that we are waiting for nothing... */
+ if (wait_time_ns == -1) {
+ break;
+ }
+
+ /* end of loop was requested by interrupt signal handler */
+ if (finish_requested) {
+ break;
+ }
+
+ /* Receive replies */
+ /* (this is what sleeps during each loop iteration) */
+ dbg_printf("waiting up to %.0f ms\n", wait_time_ns / 1e6);
+ if (wait_for_reply(wait_time_ns)) {
+ while (wait_for_reply(0))
+ ; /* process other replies in the queue */
+ }
+
+ update_current_time();
+
+ if (status_snapshot) {
+ status_snapshot = 0;
+ print_per_system_splits();
+ }
+
+ /* Print report */
+ if (report_interval && (loop_flag || count_flag) && (current_time_ns >= next_report_time)) {
+ if (netdata_flag)
+ print_netdata();
+ else
+ print_per_system_splits();
+
+ while (current_time_ns >= next_report_time) {
+ next_report_time += report_interval;
+ }
+ }
+ }
+}
+
+/************************************************************
+
+ Function: signal_handler
+
+*************************************************************
+
+ Inputs: int signum
+
+ Description:
+
+ SIGQUIT signal handler - set flag and return
+ SIGINT signal handler - set flag and return
+
+************************************************************/
+
+void signal_handler(int signum)
+{
+ switch (signum) {
+ case SIGINT:
+ finish_requested = 1;
+ break;
+
+ case SIGQUIT:
+ status_snapshot = 1;
+ break;
+ }
+}
+
+/************************************************************
+
+ Function: update_current_time
+
+*************************************************************/
+
+void update_current_time()
+{
+ clock_gettime(CLOCKID, ¤t_time);
+ current_time_ns = timespec_ns(¤t_time);
+}
+
+/************************************************************
+
+ Function: finish
+
+*************************************************************
+
+ Inputs: void (none)
+
+ Description:
+
+ Main program clean up and exit point
+
+************************************************************/
+
+void finish()
+{
+ int i;
+ HOST_ENTRY *h;
+
+ update_current_time();
+ end_time = current_time_ns;
+
+ /* tot up unreachables */
+ for (i = 0; i < num_hosts; i++) {
+ h = table[i];
+
+ if (!h->num_recv) {
+ num_unreachable++;
+
+ if (verbose_flag || unreachable_flag) {
+ printf("%s", h->host);
+
+ if (verbose_flag)
+ printf(" is unreachable");
+
+ printf("\n");
+ }
+ }
+ }
+
+ if (count_flag || loop_flag)
+ print_per_system_stats();
+#if defined(DEBUG) || defined(_DEBUG)
+ else if (print_per_system_flag)
+ print_per_system_stats();
+#endif /* DEBUG || _DEBUG */
+
+ if (stats_flag)
+ print_global_stats();
+
+ if (min_reachable) {
+ if ((num_hosts - num_unreachable) >= min_reachable) {
+ printf("Enough hosts reachable (required: %d, reachable: %d)\n", min_reachable, num_hosts - num_unreachable);
+ exit(0);
+ }
+ else {
+ printf("Not enough hosts reachable (required: %d, reachable: %d)\n", min_reachable, num_hosts - num_unreachable);
+ exit(1);
+ }
+ }
+
+ if (num_noaddress)
+ exit(2);
+ else if (num_alive != num_hosts)
+ exit(1);
+
+ exit(0);
+}
+
+/************************************************************
+
+ Function: print_per_system_stats
+
+*************************************************************
+
+ Inputs: void (none)
+
+ Description:
+
+
+************************************************************/
+
+void print_per_system_stats(void)
+{
+ int i, j, avg, outage_ms;
+ HOST_ENTRY *h;
+ int64_t resp;
+
+ if (verbose_flag || per_recv_flag)
+ fprintf(stderr, "\n");
+
+ for (i = 0; i < num_hosts; i++) {
+ h = table[i];
+ fprintf(stderr, "%-*s :", max_hostname_len, h->host);
+
+ if (report_all_rtts_flag) {
+ for (j = 0; j < h->num_sent; j++) {
+ if ((resp = h->resp_times[j]) >= 0)
+ fprintf(stderr, " %s", sprint_tm(resp));
+ else
+ fprintf(stderr, " -");
+ }
+
+ fprintf(stderr, "\n");
+ }
+ else {
+ if (h->num_recv <= h->num_sent) {
+ fprintf(stderr, " xmt/rcv/%%loss = %d/%d/%d%%",
+ h->num_sent, h->num_recv, h->num_sent > 0 ? ((h->num_sent - h->num_recv) * 100) / h->num_sent : 0);
+
+ if (outage_flag) {
+ /* Time outage total */
+ outage_ms = (h->num_sent - h->num_recv) * perhost_interval / 1e6;
+ fprintf(stderr, ", outage(ms) = %d", outage_ms);
+ }
+ }
+ else {
+ fprintf(stderr, " xmt/rcv/%%return = %d/%d/%d%%",
+ h->num_sent, h->num_recv,
+ h->num_sent > 0 ? ((h->num_recv * 100) / h->num_sent) : 0);
+ }
+
+ if (h->num_recv) {
+ avg = h->total_time / h->num_recv;
+ fprintf(stderr, ", min/avg/max = %s", sprint_tm(h->min_reply));
+ fprintf(stderr, "/%s", sprint_tm(avg));
+ fprintf(stderr, "/%s", sprint_tm(h->max_reply));
+ }
+
+ fprintf(stderr, "\n");
+ }
+ }
+}
+
+/************************************************************
+
+ Function: print_netdata
+
+*************************************************************
+
+ Inputs: void (none)
+
+ Description:
+
+
+************************************************************/
+
+void print_netdata(void)
+{
+ static int sent_charts = 0;
+
+ int i;
+ int64_t avg;
+ HOST_ENTRY *h;
+
+ for (i = 0; i < num_hosts; i++) {
+ h = table[i];
+
+ if (!sent_charts) {
+ printf("CHART fping.%s_packets '' 'FPing Packets' packets '%s' fping.packets line 110020 %.0f\n", h->name, h->host, report_interval / 1e9);
+ printf("DIMENSION xmt sent absolute 1 1\n");
+ printf("DIMENSION rcv received absolute 1 1\n");
+ }
+
+ printf("BEGIN fping.%s_packets\n", h->name);
+ printf("SET xmt = %d\n", h->num_sent_i);
+ printf("SET rcv = %d\n", h->num_recv_i);
+ printf("END\n");
+
+ if (!sent_charts) {
+ printf("CHART fping.%s_quality '' 'FPing Quality' percentage '%s' fping.quality area 110010 %.0f\n", h->name, h->host, report_interval / 1e9);
+ printf("DIMENSION returned '' absolute 1 1\n");
+ /* printf("DIMENSION lost '' absolute 1 1\n"); */
+ }
+
+ printf("BEGIN fping.%s_quality\n", h->name);
+ /*
+ if( h->num_recv_i <= h->num_sent_i )
+ printf("SET lost = %d\n", h->num_sent_i > 0 ? ( ( h->num_sent_i - h->num_recv_i ) * 100 ) / h->num_sent_i : 0 );
+ else
+ printf("SET lost = 0\n");
+*/
+
+ printf("SET returned = %d\n", h->num_sent_i > 0 ? ((h->num_recv_i * 100) / h->num_sent_i) : 0);
+ printf("END\n");
+
+ if (!sent_charts) {
+ printf("CHART fping.%s_latency '' 'FPing Latency' ms '%s' fping.latency area 110000 %.0f\n", h->name, h->host, report_interval / 1e9);
+ printf("DIMENSION min minimum absolute 1 1000000\n");
+ printf("DIMENSION max maximum absolute 1 1000000\n");
+ printf("DIMENSION avg average absolute 1 1000000\n");
+ }
+
+ printf("BEGIN fping.%s_latency\n", h->name);
+ if (h->num_recv_i) {
+ avg = h->total_time_i / h->num_recv_i;
+ printf("SET min = %" PRId64 "\n", h->min_reply_i);
+ printf("SET avg = %" PRId64 "\n", avg);
+ printf("SET max = %" PRId64 "\n", h->max_reply_i);
+ }
+ printf("END\n");
+
+ stats_reset_interval(h);
+ }
+
+ sent_charts = 1;
+}
+
+/************************************************************
+
+ Function: print_per_system_splits
+
+*************************************************************
+
+ Inputs: void (none)
+
+ Description:
+
+
+************************************************************/
+
+void print_per_system_splits(void)
+{
+ int i, avg, outage_ms_i;
+ HOST_ENTRY *h;
+ struct tm *curr_tm;
+
+ if (verbose_flag || per_recv_flag)
+ fprintf(stderr, "\n");
+
+ update_current_time();
+ curr_tm = localtime((time_t *)¤t_time.tv_sec);
+ fprintf(stderr, "[%2.2d:%2.2d:%2.2d]\n", curr_tm->tm_hour,
+ curr_tm->tm_min, curr_tm->tm_sec);
+
+ for (i = 0; i < num_hosts; i++) {
+ h = table[i];
+ fprintf(stderr, "%-*s :", max_hostname_len, h->host);
+
+ if (h->num_recv_i <= h->num_sent_i) {
+ fprintf(stderr, " xmt/rcv/%%loss = %d/%d/%d%%",
+ h->num_sent_i, h->num_recv_i, h->num_sent_i > 0 ? ((h->num_sent_i - h->num_recv_i) * 100) / h->num_sent_i : 0);
+
+ if (outage_flag) {
+ /* Time outage */
+ outage_ms_i = (h->num_sent_i - h->num_recv_i) * perhost_interval / 1e6;
+ fprintf(stderr, ", outage(ms) = %d", outage_ms_i);
+ }
+ }
+ else {
+ fprintf(stderr, " xmt/rcv/%%return = %d/%d/%d%%",
+ h->num_sent_i, h->num_recv_i, h->num_sent_i > 0 ? ((h->num_recv_i * 100) / h->num_sent_i) : 0);
+ }
+
+ if (h->num_recv_i) {
+ avg = h->total_time_i / h->num_recv_i;
+ fprintf(stderr, ", min/avg/max = %s", sprint_tm(h->min_reply_i));
+ fprintf(stderr, "/%s", sprint_tm(avg));
+ fprintf(stderr, "/%s", sprint_tm(h->max_reply_i));
+ }
+
+ fprintf(stderr, "\n");
+ if (!cumulative_stats_flag) {
+ stats_reset_interval(h);
+ }
+ }
+}
+
+/************************************************************
+
+ Function: print_global_stats
+
+*************************************************************
+
+ Inputs: void (none)
+
+ Description:
+
+
+************************************************************/
+
+void print_global_stats(void)
+{
+ fprintf(stderr, "\n");
+ fprintf(stderr, " %7d targets\n", num_hosts);
+ fprintf(stderr, " %7d alive\n", num_alive);
+ fprintf(stderr, " %7d unreachable\n", num_unreachable);
+ fprintf(stderr, " %7d unknown addresses\n", num_noaddress);
+ fprintf(stderr, "\n");
+ fprintf(stderr, " %7d timeouts (waiting for response)\n", num_timeout);
+ fprintf(stderr, " %7d ICMP Echos sent\n", num_pingsent);
+ fprintf(stderr, " %7d ICMP Echo Replies received\n", num_pingreceived);
+ fprintf(stderr, " %7d other ICMP received\n", num_othericmprcvd);
+ fprintf(stderr, "\n");
+
+ if (total_replies == 0) {
+ min_reply = 0;
+ max_reply = 0;
+ total_replies = 1;
+ sum_replies = 0;
+ }
+
+ fprintf(stderr, " %s ms (min round trip time)\n", sprint_tm(min_reply));
+ fprintf(stderr, " %s ms (avg round trip time)\n",
+ sprint_tm(sum_replies / total_replies));
+ fprintf(stderr, " %s ms (max round trip time)\n", sprint_tm(max_reply));
+ fprintf(stderr, " %12.3f sec (elapsed real time)\n",
+ (end_time - start_time) / 1e9);
+ fprintf(stderr, "\n");
+}
+
+/************************************************************
+
+ Function: send_ping
+
+*************************************************************
+
+ Inputs: int s, HOST_ENTRY *h
+
+ Description:
+
+ Compose and transmit an ICMP_ECHO REQUEST packet. The IP packet
+ will be added on by the kernel. The ID field is our UNIX process ID,
+ and the sequence number is an index into an array of outstanding
+ ping requests. The sequence number will later be used to quickly
+ figure out who the ping reply came from.
+
+************************************************************/
+
+int send_ping(HOST_ENTRY *h, int index)
+{
+ int n;
+ int myseq;
+ int ret = 1;
+ uint8_t proto = ICMP_ECHO;
+
+ update_current_time();
+ h->last_send_time = current_time_ns;
+ myseq = seqmap_add(h->i, index, current_time_ns);
+
+ dbg_printf("%s [%d]: send ping\n", h->host, index);
+
+ if (h->saddr.ss_family == AF_INET && socket4 >= 0) {
+ if(icmp_request_typ == 13)
+ proto = ICMP_TSTAMP;
+ n = socket_sendto_ping_ipv4(socket4, (struct sockaddr *)&h->saddr, h->saddr_len, myseq, ident4, proto);
+ }
+#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, ident6);
+ }
+#endif
+ else {
+ return 0;
+ }
+
+ /* error sending? */
+ if (
+ (n < 0)
+#if defined(EHOSTDOWN)
+ && errno != EHOSTDOWN
+#endif
+ ) {
+ if (verbose_flag) {
+ print_warning("%s: error while sending ping: %s\n", h->host, strerror(errno));
+ }
+ else {
+ dbg_printf("%s: error while sending ping: %s\n", h->host, strerror(errno));
+ }
+
+ h->num_sent++;
+ h->num_sent_i++;
+ if (!loop_flag)
+ h->resp_times[index] = RESP_ERROR;
+
+ ret = 0;
+ }
+ else {
+ /* schedule timeout */
+ host_add_timeout_event(h, index, current_time_ns + h->timeout);
+
+ /* mark this trial as outstanding */
+ if (!loop_flag) {
+ h->resp_times[index] = RESP_WAITING;
+ }
+ }
+
+ num_pingsent++;
+ last_send_time = h->last_send_time;
+
+ return (ret);
+}
+
+int socket_can_read(struct timeval *timeout)
+{
+ int nfound;
+ fd_set readset;
+ int socketmax;
+
+#ifndef IPV6
+ socketmax = socket4;
+#else
+ socketmax = socket4 > socket6 ? socket4 : socket6;
+#endif
+
+select_again:
+ FD_ZERO(&readset);
+ if (socket4 >= 0)
+ FD_SET(socket4, &readset);
+#ifdef IPV6
+ if (socket6 >= 0)
+ FD_SET(socket6, &readset);
+#endif
+
+ nfound = select(socketmax + 1, &readset, NULL, NULL, timeout);
+ if (nfound < 0) {
+ if (errno == EINTR) {
+ /* interrupted system call: redo the select */
+ goto select_again;
+ }
+ else {
+ perror("select");
+ }
+ }
+
+ if (nfound > 0) {
+ if (socket4 >= 0 && FD_ISSET(socket4, &readset)) {
+ return socket4;
+ }
+#ifdef IPV6
+ if (socket6 >= 0 && FD_ISSET(socket6, &readset)) {
+ return socket6;
+ }
+#endif
+ }
+
+ return -1;
+}
+
+int receive_packet(int64_t wait_time,
+#if HAVE_SO_TIMESTAMPNS
+ int64_t *reply_timestamp,
+#else
+ int64_t *reply_timestamp __attribute__((unused)),
+#endif
+ struct sockaddr *reply_src_addr,
+ size_t reply_src_addr_len,
+ char *reply_buf,
+ size_t reply_buf_len)
+{
+ struct timeval to;
+ int s = 0;
+ int recv_len;
+ static unsigned char msg_control[40];
+ struct iovec msg_iov = {
+ reply_buf,
+ reply_buf_len
+ };
+ struct msghdr recv_msghdr = {0};
+ recv_msghdr.msg_name = reply_src_addr;
+ recv_msghdr.msg_namelen = reply_src_addr_len;
+ recv_msghdr.msg_iov = &msg_iov;
+ recv_msghdr.msg_iovlen = 1;
+ recv_msghdr.msg_control = &msg_control;
+ recv_msghdr.msg_controllen = sizeof(msg_control);
+#if HAVE_SO_TIMESTAMPNS
+ struct cmsghdr *cmsg;
+#endif
+
+ /* Wait for a socket to become ready */
+ if (wait_time) {
+ to.tv_sec = wait_time / UINT64_C(1000000000);
+ to.tv_usec = (wait_time % UINT64_C(1000000000)) / 1000 + 1;
+ }
+ else {
+ to.tv_sec = 0;
+ to.tv_usec = 0;
+ }
+ s = socket_can_read(&to);
+ if (s == -1) {
+ return 0; /* timeout */
+ }
+
+ recv_len = recvmsg(s, &recv_msghdr, MSG_TRUNC);
+ if (recv_len <= 0) {
+ return 0;
+ }
+
+#if HAVE_SO_TIMESTAMPNS
+ /* ancilliary data */
+ {
+ struct timespec reply_timestamp_ts;
+ for (cmsg = CMSG_FIRSTHDR(&recv_msghdr);
+ cmsg != NULL;
+ cmsg = CMSG_NXTHDR(&recv_msghdr, cmsg)) {
+ if (cmsg->cmsg_level == SOL_SOCKET && cmsg->cmsg_type == SCM_TIMESTAMPNS) {
+ memcpy(&reply_timestamp_ts, CMSG_DATA(cmsg), sizeof(reply_timestamp_ts));
+ *reply_timestamp = timespec_ns(&reply_timestamp_ts);
+ }
+ }
+ }
+#endif
+
+#if defined(DEBUG) || defined(_DEBUG)
+ if (randomly_lose_flag) {
+ if ((random() & 0x07) <= lose_factor)
+ return 0;
+ }
+#endif
+
+ return recv_len;
+}
+
+/* stats_add: update host statistics for a single packet that was received (or timed out)
+ * h: host entry to update
+ * index: if in count mode: index number for this ping packet (-1 otherwise)
+ * success: 1 if response received, 0 otherwise
+ * latency: response time, in ns
+ */
+void stats_add(HOST_ENTRY *h, int index, int success, int64_t latency)
+{
+ /* sent count - we update only on receive/timeout, so that we don't get
+ * weird loss percentage, just because a packet was note recived yet.
+ */
+ h->num_sent++;
+ h->num_sent_i++;
+
+ if (!success) {
+ if (!loop_flag && index >= 0) {
+ h->resp_times[index] = RESP_TIMEOUT;
+ }
+ num_timeout++;
+ return;
+ }
+
+ /* received count */
+ h->num_recv++;
+ h->num_recv_i++;
+
+ /* maximum */
+ if (!h->max_reply || latency > h->max_reply) {
+ h->max_reply = latency;
+ }
+ if (!h->max_reply_i || latency > h->max_reply_i) {
+ h->max_reply_i = latency;
+ }
+
+ /* minimum */
+ if (!h->min_reply || latency < h->min_reply) {
+ h->min_reply = latency;
+ }
+ if (!h->min_reply_i || latency < h->min_reply_i) {
+ h->min_reply_i = latency;
+ }
+
+ /* total time (for average) */
+ h->total_time += latency;
+ h->total_time_i += latency;
+
+ /* response time per-packet (count mode) */
+ if (!loop_flag && index >= 0) {
+ h->resp_times[index] = latency;
+ }
+}
+
+/* stats_reset_interval: reset interval statistics
+ * h: host entry to update
+ */
+void stats_reset_interval(HOST_ENTRY *h)
+{
+ h->num_sent_i = 0;
+ h->num_recv_i = 0;
+ h->max_reply_i = 0;
+ h->min_reply_i = 0;
+ h->total_time_i = 0;
+}
+
+int decode_icmp_ipv4(
+ struct sockaddr *response_addr,
+ size_t response_addr_len,
+ char *reply_buf,
+ size_t reply_buf_len,
+ unsigned short *id,
+ unsigned short *seq,
+ int *ip_header_tos,
+ int *ip_header_ttl,
+ uint32_t *ip_header_otime_ms,
+ uint32_t *ip_header_rtime_ms,
+ uint32_t *ip_header_ttime_ms)
+{
+ struct icmp *icp;
+ int hlen = 0;
+
+ if (!using_sock_dgram4) {
+ struct ip *ip = (struct ip *)reply_buf;
+ *ip_header_tos = ip->ip_tos;
+ *ip_header_ttl = ip->ip_ttl;
+
+#if defined(__alpha__) && __STDC__ && !defined(__GLIBC__) && !defined(__NetBSD__) && !defined(__OpenBSD__)
+ /* 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;
+#endif
+ }
+
+ if (reply_buf_len < hlen + ICMP_MINLEN) {
+ /* too short */
+ if (verbose_flag) {
+ char buf[INET6_ADDRSTRLEN];
+ getnameinfo(response_addr, response_addr_len, buf, INET6_ADDRSTRLEN, NULL, 0, NI_NUMERICHOST);
+ printf("received packet too short for ICMP (%d bytes from %s)\n", (int)reply_buf_len, buf);
+ }
+ return -1;
+ }
+
+ icp = (struct icmp *)(reply_buf + hlen);
+
+ if ((icmp_request_typ == 0 && icp->icmp_type != ICMP_ECHOREPLY) ||
+ (icmp_request_typ == 13 && icp->icmp_type != ICMP_TSTAMPREPLY)) {
+ /* Handle other ICMP packets */
+ struct icmp *sent_icmp;
+ SEQMAP_VALUE *seqmap_value;
+ char addr_ascii[INET6_ADDRSTRLEN];
+ HOST_ENTRY *h;
+
+ /* reply icmp packet (hlen + ICMP_MINLEN) followed by "sent packet" (ip + icmp headers) */
+ if (reply_buf_len < hlen + ICMP_MINLEN + sizeof(struct ip) + ICMP_MINLEN) {
+ /* discard ICMP message if we can't tell that it was caused by us (i.e. if the "sent packet" is not included). */
+ return -1;
+ }
+
+ sent_icmp = (struct icmp *)(reply_buf + hlen + ICMP_MINLEN + sizeof(struct ip));
+
+ if ((icmp_request_typ == 0 && sent_icmp->icmp_type != ICMP_ECHO) ||
+ (icmp_request_typ == 13 && sent_icmp->icmp_type != ICMP_TSTAMP) ||
+ sent_icmp->icmp_id != ident4) {
+ /* not caused by us */
+ return -1;
+ }
+
+ seqmap_value = seqmap_fetch(ntohs(sent_icmp->icmp_seq), current_time_ns);
+ if (seqmap_value == NULL) {
+ return -1;
+ }
+
+ getnameinfo(response_addr, response_addr_len, addr_ascii, INET6_ADDRSTRLEN, NULL, 0, NI_NUMERICHOST);
+
+ switch (icp->icmp_type) {
+ case ICMP_UNREACH:
+ h = table[seqmap_value->host_nr];
+ if (icp->icmp_code > ICMP_UNREACH_MAXTYPE) {
+ print_warning("ICMP Unreachable (Invalid Code) from %s for ICMP Echo sent to %s",
+ addr_ascii, h->host);
+ }
+ else {
+ print_warning("%s from %s for ICMP Echo sent to %s",
+ icmp_unreach_str[icp->icmp_code], addr_ascii, h->host);
+ }
+
+ print_warning("\n");
+ num_othericmprcvd++;
+ break;
+
+ case ICMP_SOURCEQUENCH:
+ case ICMP_REDIRECT:
+ case ICMP_TIMXCEED:
+ case ICMP_PARAMPROB:
+ h = table[seqmap_value->host_nr];
+ if (icp->icmp_type <= ICMP_TYPE_STR_MAX) {
+ print_warning("%s from %s for ICMP Echo sent to %s",
+ icmp_type_str[icp->icmp_type], addr_ascii, h->host);
+ }
+ else {
+ print_warning("ICMP %d from %s for ICMP Echo sent to %s",
+ icp->icmp_type, addr_ascii, h->host);
+ }
+ print_warning("\n");
+ num_othericmprcvd++;
+ break;
+ }
+
+ return -1;
+ }
+
+ *id = icp->icmp_id;
+ *seq = ntohs(icp->icmp_seq);
+ if(icp->icmp_type == ICMP_TSTAMPREPLY) {
+
+ /* Check that reply_buf_len is sufficiently big to contain the timestamps */
+ if (reply_buf_len < hlen + ICMP_MINLEN + ICMP_TIMESTAMP_DATA_SIZE) {
+ if (verbose_flag) {
+ char buf[INET6_ADDRSTRLEN];
+ getnameinfo(response_addr, response_addr_len, buf, INET6_ADDRSTRLEN, NULL, 0, NI_NUMERICHOST);
+ printf("received packet too short for ICMP Timestamp Reply (%d bytes from %s)\n", (int)reply_buf_len, buf);
+ }
+ return -1;
+ }
+
+ *ip_header_otime_ms = ntohl(icp->icmp_dun.id_ts.its_otime);
+ *ip_header_rtime_ms = ntohl(icp->icmp_dun.id_ts.its_rtime);
+ *ip_header_ttime_ms = ntohl(icp->icmp_dun.id_ts.its_ttime);
+ }
+
+ return hlen;
+}
+
+#ifdef IPV6
+int decode_icmp_ipv6(
+ struct sockaddr *response_addr,
+ size_t response_addr_len,
+ char *reply_buf,
+ size_t reply_buf_len,
+ unsigned short *id,
+ unsigned short *seq)
+{
+ struct icmp6_hdr *icp;
+
+ if (reply_buf_len < sizeof(struct icmp6_hdr)) {
+ if (verbose_flag) {
+ char buf[INET6_ADDRSTRLEN];
+ getnameinfo(response_addr, response_addr_len, buf, INET6_ADDRSTRLEN, NULL, 0, NI_NUMERICHOST);
+ printf("received packet too short for ICMP (%d bytes from %s)\n", (int)reply_buf_len, buf);
+ }
+ return 0; /* too short */
+ }
+
+ icp = (struct icmp6_hdr *)reply_buf;
+
+ if (icp->icmp6_type != ICMP6_ECHO_REPLY) {
+ /* Handle other ICMP packets */
+ struct icmp6_hdr *sent_icmp;
+ SEQMAP_VALUE *seqmap_value;
+ char addr_ascii[INET6_ADDRSTRLEN];
+ HOST_ENTRY *h;
+
+ /* reply icmp packet (ICMP_MINLEN) followed by "sent packet" (ip + icmp headers) */
+ if (reply_buf_len < ICMP_MINLEN + sizeof(struct ip) + ICMP_MINLEN) {
+ /* discard ICMP message if we can't tell that it was caused by us (i.e. if the "sent packet" is not included). */
+ return 0;
+ }
+
+ sent_icmp = (struct icmp6_hdr *)(reply_buf + sizeof(struct icmp6_hdr) + sizeof(struct ip));
+
+ if (sent_icmp->icmp6_type != ICMP_ECHO || sent_icmp->icmp6_id != ident6) {
+ /* not caused by us */
+ return 0;
+ }
+
+ seqmap_value = seqmap_fetch(ntohs(sent_icmp->icmp6_seq), current_time_ns);
+ if (seqmap_value == NULL) {
+ return 0;
+ }
+
+ getnameinfo(response_addr, response_addr_len, addr_ascii, INET6_ADDRSTRLEN, NULL, 0, NI_NUMERICHOST);
+
+ switch (icp->icmp6_type) {
+ case ICMP_UNREACH:
+ h = table[seqmap_value->host_nr];
+ if (icp->icmp6_code > ICMP_UNREACH_MAXTYPE) {
+ print_warning("ICMP Unreachable (Invalid Code) from %s for ICMP Echo sent to %s",
+ addr_ascii, h->host);
+ }
+ else {
+ print_warning("%s from %s for ICMP Echo sent to %s",
+ icmp_unreach_str[icp->icmp6_code], addr_ascii, h->host);
+ }
+
+ print_warning("\n");
+ num_othericmprcvd++;
+ break;
+
+ case ICMP_SOURCEQUENCH:
+ case ICMP_REDIRECT:
+ case ICMP_TIMXCEED:
+ case ICMP_PARAMPROB:
+ h = table[seqmap_value->host_nr];
+ if (icp->icmp6_type <= ICMP_TYPE_STR_MAX) {
+ print_warning("%s from %s for ICMP Echo sent to %s",
+ icmp_type_str[icp->icmp6_type], addr_ascii, h->host);
+ }
+ else {
+ print_warning("ICMP %d from %s for ICMP Echo sent to %s",
+ icp->icmp6_type, addr_ascii, h->host);
+ }
+ print_warning("\n");
+ num_othericmprcvd++;
+ break;
+ }
+
+ return 0;
+ }
+
+ *id = icp->icmp6_id;
+ *seq = ntohs(icp->icmp6_seq);
+
+ return 1;
+}
+#endif
+
+int wait_for_reply(int64_t wait_time)
+{
+ int result;
+ static char buffer[RECV_BUFSIZE];
+ struct sockaddr_storage response_addr;
+ int n, avg;
+ HOST_ENTRY *h;
+ int64_t this_reply;
+ int this_count;
+ int64_t recv_time = 0;
+ SEQMAP_VALUE *seqmap_value;
+ unsigned short id;
+ unsigned short seq;
+ int ip_header_tos = -1;
+ int ip_header_ttl = -1;
+ // ICMP Timestamp
+ uint32_t ip_header_otime_ms = 0x80000000U;
+ uint32_t ip_header_rtime_ms = 0x80000000U;
+ uint32_t ip_header_ttime_ms = 0x80000000U;
+
+ /* Receive packet */
+ result = receive_packet(wait_time, /* max. wait time, in ns */
+ &recv_time, /* reply_timestamp */
+ (struct sockaddr *)&response_addr, /* reply_src_addr */
+ sizeof(response_addr), /* reply_src_addr_len */
+ buffer, /* reply_buf */
+ sizeof(buffer) /* reply_buf_len */
+ );
+
+ if (result <= 0) {
+ return 0;
+ }
+
+ update_current_time();
+ if (recv_time == 0)
+ recv_time = current_time_ns;
+
+ /* Process ICMP packet and retrieve id/seq */
+ if (response_addr.ss_family == AF_INET) {
+ int ip_hlen = decode_icmp_ipv4(
+ (struct sockaddr *)&response_addr,
+ sizeof(response_addr),
+ buffer,
+ sizeof(buffer),
+ &id,
+ &seq,
+ &ip_header_tos,
+ &ip_header_ttl,
+ &ip_header_otime_ms,
+ &ip_header_rtime_ms,
+ &ip_header_ttime_ms);
+ if (ip_hlen < 0) {
+ return 1;
+ }
+ if (id != ident4) {
+ return 1; /* packet received, but not the one we are looking for! */
+ }
+ if (!using_sock_dgram4) {
+ /* do not include IP header in returned size, to be consistent with ping(8) and also
+ * with fping with IPv6 hosts */
+ result -= ip_hlen;
+ }
+ }
+#ifdef IPV6
+ else if (response_addr.ss_family == AF_INET6) {
+ if (!decode_icmp_ipv6(
+ (struct sockaddr *)&response_addr,
+ sizeof(response_addr),
+ buffer,
+ sizeof(buffer),
+ &id,
+ &seq)) {
+ return 1;
+ }
+ if (id != ident6) {
+ return 1; /* packet received, but not the one we are looking for! */
+ }
+ }
+#endif
+ else {
+ return 1;
+ }
+
+ seqmap_value = seqmap_fetch(seq, current_time_ns);
+ if (seqmap_value == NULL) {
+ return 1;
+ }
+
+ /* find corresponding host_entry */
+ n = seqmap_value->host_nr;
+ h = table[n];
+ this_count = seqmap_value->ping_count;
+ this_reply = recv_time - seqmap_value->ping_ts;
+
+ /* update stats that include invalid replies */
+ h->num_recv_total++;
+ num_pingreceived++;
+
+ dbg_printf("received [%d] from %s\n", this_count, h->host);
+
+ /* optionally require reply source equal to target address */
+ if (check_source_flag && addr_cmp((struct sockaddr *)&response_addr, (struct sockaddr *)&h->saddr)) {
+ dbg_printf("discarding reply from wrong source address\n");
+ return 1;
+ }
+
+ /* discard duplicates */
+ if (!loop_flag && h->resp_times[this_count] >= 0) {
+ if (!per_recv_flag) {
+ fprintf(stderr, "%s : duplicate for [%d], %d bytes, %s ms",
+ h->host, this_count, result, sprint_tm(this_reply));
+
+ if (addr_cmp((struct sockaddr *)&response_addr, (struct sockaddr *)&h->saddr)) {
+ char buf[INET6_ADDRSTRLEN];
+ getnameinfo((struct sockaddr *)&response_addr, sizeof(response_addr), buf, INET6_ADDRSTRLEN, NULL, 0, NI_NUMERICHOST);
+ fprintf(stderr, " [<- %s]", buf);
+ }
+ fprintf(stderr, "\n");
+ }
+ return 1;
+ }
+
+ /* discard reply if delay is larger than timeout
+ * (see also: github #32) */
+ if (this_reply > h->timeout) {
+ return 1;
+ }
+
+ /* update stats */
+ stats_add(h, this_count, 1, this_reply);
+ // TODO: move to stats_add?
+ if (!max_reply || this_reply > max_reply)
+ max_reply = this_reply;
+ if (!min_reply || this_reply < min_reply)
+ min_reply = this_reply;
+ sum_replies += this_reply;
+ total_replies++;
+
+ /* initialize timeout to initial timeout (without backoff) */
+ h->timeout = timeout;
+
+ /* remove timeout event */
+ struct event *timeout_event = host_get_timeout_event(h, this_count);
+ if (timeout_event) {
+ ev_remove(&event_queue_timeout, timeout_event);
+ }
+
+ /* print "is alive" */
+ if (h->num_recv == 1) {
+ num_alive++;
+ if (fast_reachable && num_alive >= min_reachable)
+ finish_requested = 1;
+
+ if (verbose_flag || alive_flag) {
+ printf("%s", h->host);
+
+ if (verbose_flag)
+ printf(" is alive");
+ }
+ }
+
+ /* print received ping (unless --quiet) */
+ if (per_recv_flag) {
+ if (timestamp_flag) {
+ print_timestamp_format(recv_time, timestamp_format_flag);
+ }
+ avg = h->total_time / h->num_recv;
+ printf("%-*s : [%d], %d bytes, %s ms",
+ max_hostname_len, h->host, this_count, result, sprint_tm(this_reply));
+ printf(" (%s avg, ", sprint_tm(avg));
+
+ if (h->num_recv <= h->num_sent) {
+ printf("%d%% loss)",
+ ((h->num_sent - h->num_recv) * 100) / h->num_sent);
+ }
+ else {
+ printf("%d%% return)",
+ (h->num_recv_total * 100) / h->num_sent);
+ }
+ }
+
+ if (verbose_flag || alive_flag || per_recv_flag) {
+
+ if (addr_cmp((struct sockaddr *)&response_addr, (struct sockaddr *)&h->saddr)) {
+ char buf[INET6_ADDRSTRLEN];
+ getnameinfo((struct sockaddr *)&response_addr, sizeof(response_addr), buf, INET6_ADDRSTRLEN, NULL, 0, NI_NUMERICHOST);
+ fprintf(stderr, " [<- %s]", buf);
+ }
+
+ if (icmp_request_typ == 13) {
+ printf("%s timestamps: Originate=%u Receive=%u Transmit=%u Localreceive=%u",
+ alive_flag ? "" : ",",
+ ip_header_otime_ms, ip_header_rtime_ms, ip_header_ttime_ms,
+ ms_since_midnight_utc(recv_time));
+ }
+
+ if(print_tos_flag) {
+ if(ip_header_tos != -1) {
+ printf(" (TOS %d)", ip_header_tos);
+ }
+ else {
+ printf(" (TOS unknown)");
+ }
+ }
+
+ if (print_ttl_flag) {
+ if(ip_header_ttl != -1) {
+ printf(" (TTL %d)", ip_header_ttl);
+ }
+ else {
+ printf(" (TTL unknown)");
+ }
+ }
+
+ if (elapsed_flag && !per_recv_flag)
+ printf(" (%s ms)", sprint_tm(this_reply));
+
+ printf("\n");
+ }
+
+ return 1;
+}
+
+/************************************************************
+
+ Function: add_name
+
+*************************************************************
+
+ Inputs: char* name
+
+ Description:
+
+ process input name for addition to target list
+ name can turn into multiple targets via multiple interfaces (-m)
+ or via NIS groups
+
+************************************************************/
+
+void add_name(char *name)
+{
+ struct addrinfo *res0, *res, hints;
+ int ret_ga;
+ char *printname;
+ char namebuf[256];
+ char addrbuf[256];
+
+ /* getaddrinfo */
+ memset(&hints, 0, sizeof(struct addrinfo));
+ hints.ai_flags = AI_UNUSABLE;
+ hints.ai_socktype = SOCK_RAW;
+ hints.ai_family = hints_ai_family;
+ if (hints_ai_family == AF_INET) {
+ hints.ai_protocol = IPPROTO_ICMP;
+ }
+#ifdef IPV6
+ else if (hints_ai_family == AF_INET6) {
+ hints.ai_protocol = IPPROTO_ICMPV6;
+ }
+#endif
+ else {
+ hints.ai_socktype = SOCK_STREAM;
+ hints.ai_protocol = 0;
+ }
+ ret_ga = getaddrinfo(name, NULL, &hints, &res0);
+ if (ret_ga) {
+ if (!quiet_flag)
+ print_warning("%s: %s\n", name, gai_strerror(ret_ga));
+ num_noaddress++;
+ return;
+ }
+
+ /* NOTE: we could/should loop with res on all addresses like this:
+ * for (res = res0; res; res = res->ai_next) {
+ * We don't do it yet, however, because is is an incompatible change
+ * (need to implement a separate option for this)
+ */
+ for (res = res0; res; res = res->ai_next) {
+ /* name_flag: addr -> name lookup requested) */
+ if (name_flag || rdns_flag) {
+ int do_rdns = rdns_flag ? 1 : 0;
+ if (name_flag) {
+ /* Was it a numerical address? Only then do a rdns-query */
+ struct addrinfo *nres;
+ hints.ai_flags = AI_NUMERICHOST;
+ if (getaddrinfo(name, NULL, &hints, &nres) == 0) {
+ do_rdns = 1;
+ freeaddrinfo(nres);
+ }
+ }
+
+ if (do_rdns && getnameinfo(res->ai_addr, res->ai_addrlen, namebuf, sizeof(namebuf) / sizeof(char), NULL, 0, 0) == 0) {
+ printname = namebuf;
+ }
+ else {
+ printname = name;
+ }
+ }
+ else {
+ printname = name;
+ }
+
+ /* addr_flag: name -> addr lookup requested */
+ if (addr_flag) {
+ int ret;
+ ret = getnameinfo(res->ai_addr, res->ai_addrlen, addrbuf,
+ sizeof(addrbuf) / sizeof(char), NULL, 0, NI_NUMERICHOST);
+ if (ret) {
+ if (!quiet_flag) {
+ print_warning("%s: can't forward-lookup address (%s)\n", name, gai_strerror(ret));
+ }
+ continue;
+ }
+
+ if (name_flag || rdns_flag) {
+ char nameaddrbuf[512 + 3];
+ snprintf(nameaddrbuf, sizeof(nameaddrbuf) / sizeof(char), "%s (%s)", printname, addrbuf);
+ add_addr(name, nameaddrbuf, res->ai_addr, res->ai_addrlen);
+ }
+ else {
+ add_addr(name, addrbuf, res->ai_addr, res->ai_addrlen);
+ }
+ }
+ else {
+ add_addr(name, printname, res->ai_addr, res->ai_addrlen);
+ }
+
+ if (!multif_flag) {
+ break;
+ }
+ }
+
+ freeaddrinfo(res0);
+}
+
+/************************************************************
+
+ Function: add_addr
+
+*************************************************************
+
+ Description:
+
+ add single address to list of hosts to be pinged
+
+************************************************************/
+
+void add_addr(char *name, char *host, struct sockaddr *ipaddr, socklen_t ipaddr_len)
+{
+ HOST_ENTRY *p;
+ int n;
+ int64_t *i;
+
+ p = (HOST_ENTRY *)calloc(1, sizeof(HOST_ENTRY));
+ if (!p)
+ crash_and_burn("can't allocate HOST_ENTRY");
+
+ p->name = strdup(name);
+ p->host = strdup(host);
+ memcpy(&p->saddr, ipaddr, ipaddr_len);
+ p->saddr_len = ipaddr_len;
+ p->timeout = timeout;
+ p->min_reply = 0;
+
+ if (netdata_flag) {
+ char *s = p->name;
+ while (*s) {
+ if (!isalnum(*s))
+ *s = '_';
+ s++;
+ }
+ }
+
+ if (strlen(p->host) > max_hostname_len)
+ max_hostname_len = strlen(p->host);
+
+ /* array for response time results */
+ if (!loop_flag) {
+ i = (int64_t *)malloc(trials * sizeof(int64_t));
+ if (!i)
+ crash_and_burn("can't allocate resp_times array");
+
+ for (n = 1; n < trials; n++)
+ i[n] = RESP_UNUSED;
+
+ p->resp_times = i;
+ }
+
+ /* allocate event storage */
+ p->event_storage_ping = (struct event *)calloc(event_storage_count, sizeof(struct event));
+ if (!p->event_storage_ping) {
+ errno_crash_and_burn("can't allocate event_storage_ping");
+ }
+ p->event_storage_timeout = (struct event *)calloc(event_storage_count, sizeof(struct event));
+ if (!p->event_storage_timeout) {
+ errno_crash_and_burn("can't allocate event_storage_timeout");
+ }
+
+ /* schedule first ping */
+ host_add_ping_event(p, 0, current_time_ns);
+
+ num_hosts++;
+}
+
+/************************************************************
+
+ Function: crash_and_burn
+
+*************************************************************
+
+ Inputs: char* message
+
+ Description:
+
+************************************************************/
+
+void crash_and_burn(char *message)
+{
+ fprintf(stderr, "%s: %s\n", prog, message);
+ exit(4);
+}
+
+/************************************************************
+
+ Function: errno_crash_and_burn
+
+*************************************************************
+
+ Inputs: char* message
+
+ Description:
+
+************************************************************/
+
+void errno_crash_and_burn(char *message)
+{
+ fprintf(stderr, "%s: %s : %s\n", prog, message, strerror(errno));
+ exit(4);
+}
+
+/************************************************************
+
+ Function: print_warning
+
+ Description: fprintf(stderr, ...), unless running with -q
+
+*************************************************************/
+
+void print_warning(char *format, ...)
+{
+ va_list args;
+ if (!quiet_flag) {
+ va_start(args, format);
+ vfprintf(stderr, format, args);
+ va_end(args);
+ }
+}
+
+/************************************************************
+
+ Function: sprint_tm
+
+*************************************************************
+
+ render nanosecond int64_t value into milliseconds string with three digits of
+ precision.
+
+************************************************************/
+
+const char *sprint_tm(int64_t ns)
+{
+ static char buf[10];
+ double t = (double)ns / 1e6;
+
+ if (t < 0.0) {
+ /* negative (unexpected) */
+ sprintf(buf, "%.2g", t);
+ }
+ else if (t < 1.0) {
+ /* <= 0.99 ms */
+ sprintf(buf, "%.3f", t);
+ }
+ else if (t < 10.0) {
+ /* 1.00 - 9.99 ms */
+ sprintf(buf, "%.2f", t);
+ }
+ else if (t < 100.0) {
+ /* 10.0 - 99.9 ms */
+ sprintf(buf, "%.1f", t);
+ }
+ else if (t < 1000000.0) {
+ /* 100 - 1'000'000 ms */
+ sprintf(buf, "%.0f", t);
+ }
+ else {
+ sprintf(buf, "%.3e", t);
+ }
+
+ return (buf);
+}
+
+/************************************************************
+
+ Function: addr_cmp
+
+*************************************************************/
+int addr_cmp(struct sockaddr *a, struct sockaddr *b)
+{
+ if (a->sa_family != b->sa_family) {
+ return a->sa_family - b->sa_family;
+ }
+ else {
+ if (a->sa_family == AF_INET) {
+ return ((struct sockaddr_in *)a)->sin_addr.s_addr - ((struct sockaddr_in *)b)->sin_addr.s_addr;
+ }
+ else if (a->sa_family == AF_INET6) {
+ return memcmp(&((struct sockaddr_in6 *)a)->sin6_addr,
+ &((struct sockaddr_in6 *)b)->sin6_addr,
+ sizeof(((struct sockaddr_in6 *)a)->sin6_addr));
+ }
+ }
+
+ return 0;
+}
+
+void host_add_ping_event(HOST_ENTRY *h, int index, int64_t ev_time)
+{
+ struct event *event = &h->event_storage_ping[index % event_storage_count];
+ event->host = h;
+ event->ping_index = index;
+ event->ev_time = ev_time;
+ ev_enqueue(&event_queue_ping, event);
+
+ dbg_printf("%s [%d]: add ping event in %.0f ms\n",
+ event->host->host, index, (ev_time - current_time_ns) / 1e6);
+}
+
+void host_add_timeout_event(HOST_ENTRY *h, int index, int64_t ev_time)
+{
+ struct event *event = &h->event_storage_timeout[index % event_storage_count];
+ event->host = h;
+ event->ping_index = index;
+ event->ev_time = ev_time;
+ ev_enqueue(&event_queue_timeout, event);
+
+ dbg_printf("%s [%d]: add timeout event in %.0f ms\n",
+ event->host->host, index, (ev_time - current_time_ns) / 1e6);
+}
+
+struct event *host_get_timeout_event(HOST_ENTRY *h, int index)
+{
+ return &h->event_storage_timeout[index % event_storage_count];
+}
+
+/************************************************************
+
+ Function: ev_enqueue
+
+ Enqueue an event
+
+ The queue is sorted by event->ev_time, so that queue->first always points to
+ the earliest event.
+
+ We start scanning the queue from the tail, because we assume
+ that new events mostly get inserted with a event time higher
+ than the others.
+
+*************************************************************/
+void ev_enqueue(struct event_queue *queue, struct event *event)
+{
+ struct event *i;
+ struct event *i_prev;
+
+ /* Empty list */
+ if (queue->last == NULL) {
+ event->ev_next = NULL;
+ event->ev_prev = NULL;
+ queue->first = event;
+ queue->last = event;
+ return;
+ }
+
+ /* Insert on tail? */
+ if (event->ev_time - queue->last->ev_time >= 0) {
+ event->ev_next = NULL;
+ event->ev_prev = queue->last;
+ queue->last->ev_next = event;
+ queue->last = event;
+ return;
+ }
+
+ /* Find insertion point */
+ i = queue->last;
+ while (1) {
+ i_prev = i->ev_prev;
+ if (i_prev == NULL || event->ev_time - i_prev->ev_time >= 0) {
+ event->ev_prev = i_prev;
+ event->ev_next = i;
+ i->ev_prev = event;
+ if (i_prev != NULL) {
+ i_prev->ev_next = event;
+ }
+ else {
+ queue->first = event;
+ }
+ return;
+ }
+ i = i_prev;
+ }
+}
+
+/************************************************************
+
+ Function: ev_dequeue
+
+*************************************************************/
+struct event *ev_dequeue(struct event_queue *queue)
+{
+ struct event *dequeued;
+
+ if (queue->first == NULL) {
+ return NULL;
+ }
+ dequeued = queue->first;
+ ev_remove(queue, dequeued);
+
+ return dequeued;
+}
+
+/************************************************************
+
+ Function: ev_remove
+
+*************************************************************/
+void ev_remove(struct event_queue *queue, struct event *event)
+{
+ if (queue->first == event) {
+ queue->first = event->ev_next;
+ }
+ if (queue->last == event) {
+ queue->last = event->ev_prev;
+ }
+ if (event->ev_prev) {
+ event->ev_prev->ev_next = event->ev_next;
+ }
+ if (event->ev_next) {
+ event->ev_next->ev_prev = event->ev_prev;
+ }
+ event->ev_prev = NULL;
+ event->ev_next = NULL;
+}
+
+/************************************************************
+
+ Function: print_human_readable_time from current_time_ns
+
+*************************************************************/
+void print_timestamp_format(int64_t current_time_ns, int timestamp_format)
+{
+ char time_buffer[100];
+ time_t current_time_s;
+ struct tm *local_time;
+
+ current_time_s = current_time_ns / 1000000000;
+ local_time = localtime(¤t_time_s);
+ switch(timestamp_format) {
+ case 1:
+ // timestamp-format ctime
+ strftime(time_buffer, sizeof(time_buffer), "%c", local_time);
+ printf("[%s] ", time_buffer);
+ break;
+ case 2:
+ // timestamp-format iso
+ strftime(time_buffer, sizeof(time_buffer), "%Y-%m-%dT%T%z", local_time);
+ printf("[%s] ", time_buffer);
+ break;
+ case 3:
+ // timestamp-format rfc3339
+ strftime(time_buffer, sizeof(time_buffer), "%Y-%m-%d %H:%M:%S", local_time);
+ printf("[%s] ", time_buffer);
+ break;
+ default:
+ printf("[%.5f] ", (double)current_time_ns / 1e9);
+ }
+}
+
+/************************************************************
+
+ Function: ms_since_midnight_utc
+
+*************************************************************
+
+ Input: int64_t: current UTC time in ns
+
+ Output: uint32_t: current time in ms since midnight UTC
+
+ Description:
+
+ Return ICMP Timestamp value corresponding to the given time value.
+ The given time value must be in UTC.
+
+*************************************************************/
+static uint32_t ms_since_midnight_utc(int64_t time_val)
+{
+ return (uint32_t)((time_val / 1000000) % (24 * 60 * 60 * 1000));
+}
+
+/************************************************************
+
+ Function: usage
+
+*************************************************************
+
+ Inputs: int: 0 if output on request, 1 if output because of wrong argument
+
+ Description:
+
+************************************************************/
+
+void usage(int is_error)
+{
+ FILE *out = is_error ? stderr : stdout;
+ fprintf(out, "Usage: %s [options] [targets...]\n", prog);
+ fprintf(out, "\n");
+ fprintf(out, "Probing options:\n");
+ fprintf(out, " -4, --ipv4 only ping IPv4 addresses\n");
+ fprintf(out, " -6, --ipv6 only ping IPv6 addresses\n");
+ fprintf(out, " -b, --size=BYTES amount of ping data to send, in bytes (default: %d)\n", DEFAULT_PING_DATA_SIZE);
+ fprintf(out, " -B, --backoff=N set exponential backoff factor to N (default: 1.5)\n");
+ fprintf(out, " -c, --count=N count mode: send N pings to each target and report stats\n");
+ fprintf(out, " -f, --file=FILE read list of targets from a file ( - means stdin)\n");
+ fprintf(out, " -g, --generate generate target list (only if no -f specified),\n");
+ fprintf(out, " limited to at most %d targets\n", MAX_GENERATE);
+ fprintf(out, " (give start and end IP in the target list, or a CIDR address)\n");
+ fprintf(out, " (ex. %s -g 192.168.1.0 192.168.1.255 or %s -g 192.168.1.0/24)\n", prog, prog);
+ fprintf(out, " -H, --ttl=N set the IP TTL value (Time To Live hops)\n");
+ fprintf(out, " -i, --interval=MSEC interval between sending ping packets (default: %.0f ms)\n", interval / 1e6);
+#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");
+ fprintf(out, " -M, --dontfrag set the Don't Fragment flag\n");
+ fprintf(out, " -O, --tos=N set the type of service (tos) flag on the ICMP packets\n");
+ fprintf(out, " -p, --period=MSEC interval between ping packets to one target (in ms)\n");
+ fprintf(out, " (in loop and count modes, default: %.0f ms)\n", perhost_interval / 1e6);
+ fprintf(out, " -r, --retry=N number of retries (default: %d)\n", DEFAULT_RETRY);
+ fprintf(out, " -R, --random random packet data (to foil link data compression)\n");
+ fprintf(out, " -S, --src=IP set source address\n");
+ fprintf(out, " -t, --timeout=MSEC individual target initial timeout (default: %.0f ms,\n", timeout / 1e6);
+ fprintf(out, " except with -l/-c/-C, where it's the -p period up to 2000 ms)\n");
+ fprintf(out, " --check-source discard replies not from target address\n");
+ fprintf(out, " --icmp-timestamp use ICMP Timestamp instead of ICMP Echo\n");
+ fprintf(out, "\n");
+ fprintf(out, "Output options:\n");
+ fprintf(out, " -a, --alive show targets that are alive\n");
+ fprintf(out, " -A, --addr show targets by address\n");
+ fprintf(out, " -C, --vcount=N same as -c, report results (not stats) in verbose format\n");
+ fprintf(out, " -d, --rdns show targets by name (force reverse-DNS lookup)\n");
+ fprintf(out, " -D, --timestamp print timestamp before each output line\n");
+ fprintf(out, " --timestamp-format=FORMAT show timestamp in the given format (-D required): ctime|iso|rfc3339\n");
+ fprintf(out, " -e, --elapsed show elapsed time on return packets\n");
+ fprintf(out, " -n, --name show targets by name (reverse-DNS lookup for target IPs)\n");
+ fprintf(out, " -N, --netdata output compatible for netdata (-l -Q are required)\n");
+ fprintf(out, " -o, --outage show the accumulated outage time (lost packets * packet interval)\n");
+ fprintf(out, " -q, --quiet quiet (don't show per-target/per-ping results)\n");
+ fprintf(out, " -Q, --squiet=SECS[,cumulative] same as -q, but add interval summary every SECS seconds,\n");
+ fprintf(out, " with 'cumulative', print stats since beginning\n");
+ fprintf(out, " -s, --stats print final stats\n");
+ fprintf(out, " -u, --unreach show targets that are unreachable\n");
+ fprintf(out, " -v, --version show version\n");
+ fprintf(out, " -x, --reachable=N shows if >=N hosts are reachable or not\n");
+ fprintf(out, " -X, --fast-reachable=N exits true immediately when N hosts are found\n");
+ fprintf(out, " --print-tos show received TOS value\n");
+ fprintf(out, " --print-ttl show IP TTL value\n");
+ exit(is_error);
+}
--- /dev/null
+#ifndef _FPING_H
+#define _FPING_H
+
+#define __APPLE_USE_RFC_3542 1
+
+#include <sys/socket.h>
+#include <sys/types.h>
+#include <netinet/in.h>
+
+/* this requires variadic macros, part of C99 */
+#if (defined(DEBUG) || defined(_DEBUG))
+extern int64_t current_time_ns;
+extern int trace_flag;
+#define dbg_printf(fmt, ...) do { if (trace_flag) { fprintf(stderr, "[%10.5f] ", (double)(current_time_ns / 1000)/1000000); fprintf(stderr, fmt, __VA_ARGS__); } } while (0)
+
+#else
+#define dbg_printf(fmt, ...)
+#endif
+
+
+/* fping.c */
+void crash_and_burn( char *message );
+void errno_crash_and_burn( char *message );
+int in_cksum( unsigned short *p, int n );
+extern int random_data_flag;
+
+/* socket.c */
+int open_ping_socket_ipv4(int *socktype);
+void init_ping_buffer_ipv4(size_t ping_data_size);
+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, uint8_t icmp_proto);
+#ifdef IPV6
+int open_ping_socket_ipv6(int *socktype);
+void init_ping_buffer_ipv6(size_t ping_data_size);
+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
+
+#endif
--- /dev/null
+
+/*
+ * Interval is the minimum amount of time between sending a ping packet to
+ * any host.
+ *
+ * Perhost_interval is the minimum amount of time between sending a ping
+ * packet to a particular responding host (when count is > 1)
+ *
+ * Timeout is the initial amount of time between sending a ping packet to
+ * a particular non-responding host.
+ *
+ * Retry is the number of ping packets to send to a non-responding host
+ * before giving up (in is-it-alive mode).
+ *
+ * Backoff factor is how much longer to wait on successive retries.
+ *
+ *
+ */
+
+/* constants */
+
+#ifndef DEFAULT_INTERVAL
+#define DEFAULT_INTERVAL 10 /* default time between packets (msec) */
+#endif
+
+#ifndef DEFAULT_PERHOST_INTERVAL /* default time between packets */
+#define DEFAULT_PERHOST_INTERVAL 1000 /* to a particular destination */
+#endif /* in counting/looping mode */
+
+#ifndef DEFAULT_TIMEOUT
+#define DEFAULT_TIMEOUT 500 /* individual host timeouts */
+#define AUTOTUNE_TIMEOUT_MAX 2000
+#endif
+
+
+#ifndef DEFAULT_RETRY
+#define DEFAULT_RETRY 3 /* number of times to retry a host */
+#endif
+
+#ifndef DEFAULT_SELECT_TIME
+#define DEFAULT_SELECT_TIME 10 /* default time to wait during select() */
+#endif
+
+#ifndef DEFAULT_BACKOFF_FACTOR
+#define DEFAULT_BACKOFF_FACTOR 1.5 /* exponential timeout factor */
+#endif
+#define MIN_BACKOFF_FACTOR 1.0 /* exponential timeout factor */
+#define MAX_BACKOFF_FACTOR 5.0 /* exponential timeout factor */
+
+#ifndef DNS_TIMEOUT
+#define DNS_TIMEOUT 1000 /* time in micro_sec for dns retry */
+#endif
--- /dev/null
+#include "optparse.h"
+
+#define MSG_INVALID "invalid option"
+#define MSG_MISSING "option requires an argument"
+#define MSG_TOOMANY "option takes no arguments"
+
+static int
+opterror(struct optparse *options, const char *message, const char *data)
+{
+ unsigned p = 0;
+ while (*message)
+ options->errmsg[p++] = *message++;
+ const char *sep = " -- '";
+ while (*sep)
+ options->errmsg[p++] = *sep++;
+ while (p < sizeof(options->errmsg) - 2 && *data)
+ options->errmsg[p++] = *data++;
+ options->errmsg[p++] = '\'';
+ options->errmsg[p++] = '\0';
+ return '?';
+}
+
+void optparse_init(struct optparse *options, char **argv)
+{
+ options->argv = argv;
+ options->permute = 1;
+ options->optind = 1;
+ options->subopt = 0;
+ options->optarg = 0;
+ options->errmsg[0] = '\0';
+}
+
+static inline int
+is_dashdash(const char *arg)
+{
+ return arg != 0 && arg[0] == '-' && arg[1] == '-' && arg[2] == '\0';
+}
+
+static inline int
+is_shortopt(const char *arg)
+{
+ return arg != 0 && arg[0] == '-' && arg[1] != '-' && arg[1] != '\0';
+}
+
+static inline int
+is_longopt(const char *arg)
+{
+ return arg != 0 && arg[0] == '-' && arg[1] == '-' && arg[2] != '\0';
+}
+
+static void
+permute(struct optparse *options, int index)
+{
+ char *nonoption = options->argv[index];
+ for (int i = index; i < options->optind - 1; i++)
+ options->argv[i] = options->argv[i + 1];
+ options->argv[options->optind - 1] = nonoption;
+}
+
+static int
+argtype(const char *optstring, char c)
+{
+ if (c == ':')
+ return -1;
+ for (; *optstring && c != *optstring; optstring++);
+ if (!*optstring)
+ return -1;
+ int count = OPTPARSE_NONE;
+ if (optstring[1] == ':')
+ count += optstring[2] == ':' ? 2 : 1;
+ return count;
+}
+
+int optparse(struct optparse *options, const char *optstring)
+{
+ options->errmsg[0] = '\0';
+ options->optopt = 0;
+ options->optarg = 0;
+ char *option = options->argv[options->optind];
+ if (option == 0) {
+ return -1;
+ } else if (is_dashdash(option)) {
+ options->optind++; /* consume "--" */
+ return -1;
+ } else if (!is_shortopt(option)) {
+ if (options->permute) {
+ int index = options->optind;
+ options->optind++;
+ int r = optparse(options, optstring);
+ permute(options, index);
+ options->optind--;
+ return r;
+ } else {
+ return -1;
+ }
+ }
+ option += options->subopt + 1;
+ options->optopt = option[0];
+ int type = argtype(optstring, option[0]);
+ char *next = options->argv[options->optind + 1];
+ switch (type) {
+ case -1: {
+ options->optind++;
+ char str[2] = {option[0]};
+ return opterror(options, MSG_INVALID, str);
+ }
+ case OPTPARSE_NONE:
+ if (option[1]) {
+ options->subopt++;
+ } else {
+ options->subopt = 0;
+ options->optind++;
+ }
+ return option[0];
+ case OPTPARSE_REQUIRED:
+ options->subopt = 0;
+ options->optind++;
+ if (option[1]) {
+ options->optarg = option + 1;
+ } else if (next != 0) {
+ options->optarg = next;
+ options->optind++;
+ } else {
+ options->optarg = 0;
+ char str[2] = {option[0]};
+ return opterror(options, MSG_MISSING, str);
+ }
+ return option[0];
+ case OPTPARSE_OPTIONAL:
+ options->subopt = 0;
+ options->optind++;
+ if (option[1])
+ options->optarg = option + 1;
+ else
+ options->optarg = 0;
+ return option[0];
+ }
+ return 0;
+}
+
+char *optparse_arg(struct optparse *options)
+{
+ options->subopt = 0;
+ char *option = options->argv[options->optind];
+ if (option != 0)
+ options->optind++;
+ return option;
+}
+
+static inline int
+longopts_end(const struct optparse_long *longopts, int i)
+{
+ return !longopts[i].longname && !longopts[i].shortname;
+}
+
+static void
+optstring_from_long(const struct optparse_long *longopts, char *optstring)
+{
+ char *p = optstring;
+ for (int i = 0; !longopts_end(longopts, i); i++) {
+ if (longopts[i].shortname) {
+ *p++ = longopts[i].shortname;
+ for (int a = 0; a < (int)longopts[i].argtype; a++)
+ *p++ = ':';
+ }
+ }
+ *p = '\0';
+}
+
+/* Unlike strcmp(), handles options containing "=". */
+static int
+longopts_match(const char *longname, const char *option)
+{
+ if (longname == 0)
+ return 0;
+ const char *a = option, *n = longname;
+ for (; *a && *n && *a != '='; a++, n++)
+ if (*a != *n)
+ return 0;
+ return *n == '\0' && (*a == '\0' || *a == '=');
+}
+
+/* Return the part after "=", or NULL. */
+static char *
+longopts_arg(char *option)
+{
+ for (; *option && *option != '='; option++);
+ if (*option == '=')
+ return option + 1;
+ else
+ return 0;
+}
+
+static int
+long_fallback(struct optparse *options,
+ const struct optparse_long *longopts,
+ int *longindex)
+{
+ char optstring[96 * 3 + 1]; /* 96 ASCII printable characters */
+ optstring_from_long(longopts, optstring);
+ int result = optparse(options, optstring);
+ if (longindex != 0) {
+ *longindex = -1;
+ if (result != -1)
+ for (int i = 0; !longopts_end(longopts, i); i++)
+ if (longopts[i].shortname == options->optopt)
+ *longindex = i;
+ }
+ return result;
+}
+
+int
+optparse_long(struct optparse *options,
+ const struct optparse_long *longopts,
+ int *longindex)
+{
+ char *option = options->argv[options->optind];
+ if (option == 0) {
+ return -1;
+ } else if (is_dashdash(option)) {
+ options->optind++; /* consume "--" */
+ return -1;
+ } else if (is_shortopt(option)) {
+ return long_fallback(options, longopts, longindex);
+ } else if (!is_longopt(option)) {
+ if (options->permute) {
+ int index = options->optind;
+ options->optind++;
+ int r = optparse_long(options, longopts, longindex);
+ permute(options, index);
+ options->optind--;
+ return r;
+ } else {
+ return -1;
+ }
+ }
+
+ /* Parse as long option. */
+ options->errmsg[0] = '\0';
+ options->optopt = 0;
+ options->optlongname = 0;
+ options->optarg = 0;
+ option += 2; /* skip "--" */
+ options->optind++;
+ for (int i = 0; !longopts_end(longopts, i); i++) {
+ const char *name = longopts[i].longname;
+ if (longopts_match(name, option)) {
+ options->optlongname = option;
+ if (longindex)
+ *longindex = i;
+ options->optopt = longopts[i].shortname;
+ char *arg = longopts_arg(option);
+ if (longopts[i].argtype == OPTPARSE_NONE && arg != 0) {
+ return opterror(options, MSG_TOOMANY, name);
+ } if (arg != 0) {
+ options->optarg = arg;
+ } else if (longopts[i].argtype == OPTPARSE_REQUIRED) {
+ options->optarg = options->argv[options->optind++];
+ if (options->optarg == 0)
+ return opterror(options, MSG_MISSING, name);
+ }
+ return options->optopt;
+ }
+ }
+ return opterror(options, MSG_INVALID, option);
+}
--- /dev/null
+#ifndef OPTPARSE_H
+#define OPTPARSE_H
+
+/**
+ * Optparse -- portable, reentrant, embeddable, getopt-like option parser
+ *
+ * The POSIX getopt() option parser has three fatal flaws. These flaws
+ * are solved by Optparse.
+ *
+ * 1) Parser state is stored entirely in global variables, some of
+ * which are static and inaccessible. This means only one thread can
+ * use getopt(). It also means it's not possible to recursively parse
+ * nested sub-arguments while in the middle of argument parsing.
+ * Optparse fixes this by storing all state on a local struct.
+ *
+ * 2) The POSIX standard provides no way to properly reset the parser.
+ * This means for portable code that getopt() is only good for one
+ * run, over one argv with one optstring. It also means subcommand
+ * options cannot be processed with getopt(). Most implementations
+ * provide a method to reset the parser, but it's not portable.
+ * Optparse provides an optparse_arg() function for stepping over
+ * subcommands and continuing parsing of options with another
+ * optstring. The Optparse struct itself can be passed around to
+ * subcommand handlers for additional subcommand option parsing. A
+ * full reset can be achieved by with an additional optparse_init().
+ *
+ * 3) Error messages are printed to stderr. This can be disabled with
+ * opterr, but the messages themselves are still inaccessible.
+ * Optparse solves this by writing an error message in its errmsg
+ * field. The downside to Optparse is that this error message will
+ * always be in English rather than the current locale.
+ *
+ * Optparse should be familiar with anyone accustomed to getopt(), and
+ * it could be a nearly drop-in replacement. The optstring is the same
+ * and the fields have the same names as the getopt() global variables
+ * (optarg, optind, optopt).
+ *
+ * Optparse also supports GNU-style long options with optparse_long().
+ * The interface is slightly different and simpler than getopt_long().
+ *
+ * By default, argv is permuted as it is parsed, moving non-option
+ * arguments to the end. This can be disabled by setting the `permute`
+ * field to 0 after initialization.
+ */
+
+struct optparse {
+ char **argv;
+ int permute;
+ int optind;
+ int optopt;
+ char *optlongname;
+ char *optarg;
+ char errmsg[64];
+ int subopt;
+};
+
+enum optparse_argtype { OPTPARSE_NONE, OPTPARSE_REQUIRED, OPTPARSE_OPTIONAL };
+
+struct optparse_long {
+ const char *longname;
+ int shortname;
+ enum optparse_argtype argtype;
+};
+
+/**
+ * Initializes the parser state.
+ */
+void optparse_init(struct optparse *options, char **argv);
+
+/**
+ * Read the next option in the argv array.
+ * @param optstring a getopt()-formatted option string.
+ * @return the next option character, -1 for done, or '?' for error
+ *
+ * Just like getopt(), a character followed by no colons means no
+ * argument. One colon means the option has a required argument. Two
+ * colons means the option takes an optional argument.
+ */
+int optparse(struct optparse *options, const char *optstring);
+
+/**
+ * Handles GNU-style long options in addition to getopt() options.
+ * This works a lot like GNU's getopt_long(). The last option in
+ * longopts must be all zeros, marking the end of the array. The
+ * longindex argument may be NULL.
+ */
+int
+optparse_long(struct optparse *options,
+ const struct optparse_long *longopts,
+ int *longindex);
+
+/**
+ * Used for stepping over non-option arguments.
+ * @return the next non-option argument, or NULL for no more arguments
+ *
+ * Argument parsing can continue with optparse() after using this
+ * function. That would be used to parse the options for the
+ * subcommand returned by optparse_arg(). This function allows you to
+ * ignore the value of optind.
+ */
+char *optparse_arg(struct optparse *options);
+
+#endif
--- /dev/null
+/*
+ * fping: fast-ping, file-ping, favorite-ping, funky-ping
+ *
+ * Ping a list of target hosts in a round robin fashion.
+ * A better ping overall.
+ *
+ * fping website: http://www.fping.org
+ *
+ * Current maintainer of fping: David Schweikert
+ * Please send suggestions and patches to: david@schweikert.ch
+ *
+ *
+ * Original author: Roland Schemers <schemers@stanford.edu>
+ * IPv6 Support: Jeroen Massar <jeroen@unfix.org / jeroen@ipng.nl>
+ * Improved main loop: David Schweikert <david@schweikert.ch>
+ * Debian Merge, TOS settings: Tobi Oetiker <tobi@oetiker.ch>
+ * Bugfixes, byte order & senseful seq.-numbers: Stephan Fuhrmann (stephan.fuhrmann AT 1und1.de)
+ *
+ *
+ * Redistribution and use in source and binary forms are permitted
+ * provided that the above copyright notice and this paragraph are
+ * duplicated in all such forms and that any documentation,
+ * advertising materials, and other materials related to such
+ * distribution and use acknowledge that the software was developed
+ * by Stanford University. The name of the University may not be used
+ * to endorse or promote products derived from this software without
+ * specific prior written permission.
+ * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED
+ * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE.
+ *
+ * seqmap.c: implementation of a mapping between sequence number and (host, ping_nr)
+ * we can't just use ping_nr*host_count + host_nr, because it can
+ * overflow the 16 bit of the icmp header field. See also:
+ * https://github.com/schweikert/fping/issues/48
+ */
+
+#include "config.h"
+#include "seqmap.h"
+#include "limits.h"
+#include "options.h"
+#include "fping.h"
+
+#include <inttypes.h>
+#include <stdio.h>
+#include <stdlib.h>
+
+/* description of the data structure used:
+ *
+ * - we assume that no more than SEQMAP_MAXSEQ (65535) pings are sent in
+ * the timeout interval (SEQMAP_TIMEOUT_IN_NS)
+ * - we store the values in an array with SEQMAP_MAXSEQ elements
+ * - current sequence number % SEQMAP_MAXSEQ gives the current index
+ * - when entering a value, we check that the current entry is expired
+ */
+
+static SEQMAP_VALUE* seqmap_map = NULL;
+static unsigned int seqmap_next_id = 0;
+
+#define SEQMAP_TIMEOUT_IN_NS INT64_C(10000000000)
+#define SEQMAP_UNASSIGNED_HOST_NR UINT_MAX
+
+void seqmap_init()
+{
+ seqmap_map = calloc(SEQMAP_MAXSEQ, sizeof(SEQMAP_VALUE));
+ if (seqmap_map == NULL) {
+ perror("malloc error (can't allocate seqmap_map)");
+ }
+}
+
+unsigned int seqmap_add(unsigned int host_nr, unsigned int ping_count, int64_t timestamp)
+{
+ unsigned int current_id;
+ SEQMAP_VALUE* next_value;
+
+ if (!seqmap_map) {
+ fprintf(stderr, "fping internal error: seqmap not initialized.\n");
+ exit(4);
+ }
+
+ /* check if expired (note that unused seqmap values will have fields set to
+ * 0, so will be seen as expired */
+ next_value = &seqmap_map[seqmap_next_id];
+ if (next_value->ping_ts != 0 && timestamp - next_value->ping_ts < SEQMAP_TIMEOUT_IN_NS) {
+ fprintf(stderr, "fping error: not enough sequence numbers available! (expire_timeout=%" PRId64 ", host_nr=%d, ping_count=%d, seqmap_next_id=%d)\n",
+ SEQMAP_TIMEOUT_IN_NS, host_nr, ping_count, seqmap_next_id);
+ exit(4);
+ }
+
+ /* store the value */
+ next_value->host_nr = host_nr;
+ next_value->ping_count = ping_count;
+ next_value->ping_ts = timestamp;
+
+ /* increase next id */
+ current_id = seqmap_next_id;
+ seqmap_next_id = (seqmap_next_id + 1) % SEQMAP_MAXSEQ;
+
+ dbg_printf("seqmap_add(host: %d, index: %d) -> %d\n", host_nr, ping_count, current_id);
+
+ return current_id;
+}
+
+SEQMAP_VALUE* seqmap_fetch(unsigned int id, int64_t now)
+{
+ SEQMAP_VALUE* value;
+
+ if (id >= SEQMAP_MAXSEQ) {
+ return NULL;
+ }
+
+ value = &seqmap_map[id];
+
+ /* verify that value is not expired */
+ if (now - value->ping_ts >= SEQMAP_TIMEOUT_IN_NS) {
+ dbg_printf("seqmap_fetch(%d) -> host: %d, index: %d -> DISCARDED %ld\n", id, value->host_nr, value->ping_count,
+ now - value->ping_ts);
+ return NULL;
+ }
+
+ dbg_printf("seqmap_fetch(%d) -> host: %d, index: %d\n", id, value->host_nr, value->ping_count);
+
+ return value;
+}
--- /dev/null
+#ifndef SEQMAP_H
+#define SEQMAP_H
+
+#include <sys/time.h>
+#include <stdint.h>
+
+typedef struct seqmap_value
+{
+ unsigned int host_nr;
+ unsigned int ping_count;
+ int64_t ping_ts;
+
+} SEQMAP_VALUE;
+
+#define SEQMAP_MAXSEQ 65535
+
+void seqmap_init();
+unsigned int seqmap_add(unsigned int host_nr, unsigned int ping_count, int64_t now);
+SEQMAP_VALUE *seqmap_fetch(unsigned int id, int64_t now);
+
+#endif
--- /dev/null
+/*
+ * fping: fast-ping, file-ping, favorite-ping, funky-ping
+ *
+ * Ping a list of target hosts in a round robin fashion.
+ * A better ping overall.
+ *
+ * fping website: http://www.fping.org
+ *
+ * Current maintainer of fping: David Schweikert
+ * Please send suggestions and patches to: david@schweikert.ch
+ *
+ *
+ * Original author: Roland Schemers <schemers@stanford.edu>
+ * IPv6 Support: Jeroen Massar <jeroen@unfix.org / jeroen@ipng.nl>
+ * Improved main loop: David Schweikert <david@schweikert.ch>
+ * Debian Merge, TOS settings: Tobi Oetiker <tobi@oetiker.ch>
+ * Bugfixes, byte order & senseful seq.-numbers: Stephan Fuhrmann (stephan.fuhrmann AT 1und1.de)
+ *
+ *
+ * Redistribution and use in source and binary forms are permitted
+ * provided that the above copyright notice and this paragraph are
+ * duplicated in all such forms and that any documentation,
+ * advertising materials, and other materials related to such
+ * distribution and use acknowledge that the software was developed
+ * by Stanford University. The name of the University may not be used
+ * to endorse or promote products derived from this software without
+ * specific prior written permission.
+ * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED
+ * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE.
+ */
+
+#include "config.h"
+#include "fping.h"
+
+#include <fcntl.h>
+#include <netdb.h>
+#include <netinet/in.h>
+#include <netinet/in_systm.h>
+#include <netinet/ip.h>
+#include <netinet/ip_icmp.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/socket.h>
+#include <time.h>
+
+char* ping_buffer_ipv4 = 0;
+size_t ping_pkt_size_ipv4;
+
+int open_ping_socket_ipv4(int *socktype)
+{
+ struct protoent* proto;
+ int s;
+
+ /* confirm that ICMP is available on this machine */
+ if ((proto = getprotobyname("icmp")) == NULL)
+ crash_and_burn("icmp: unknown protocol");
+
+ /* create raw socket for ICMP calls (ping) */
+ *socktype = SOCK_RAW;
+ s = socket(AF_INET, *socktype, proto->p_proto);
+ if (s < 0) {
+ /* try non-privileged icmp (works on Mac OSX without privileges, for example) */
+ *socktype = SOCK_DGRAM;
+ s = socket(AF_INET, *socktype, proto->p_proto);
+ if (s < 0) {
+ return -1;
+ }
+ }
+
+ /* Make sure that we use non-blocking IO */
+ {
+ int flags;
+
+ if ((flags = fcntl(s, F_GETFL, 0)) < 0)
+ perror("fcntl");
+
+ if (fcntl(s, F_SETFL, flags | O_NONBLOCK) < 0)
+ perror("fcntl");
+ }
+
+ return s;
+}
+
+void init_ping_buffer_ipv4(size_t ping_data_size)
+{
+ /* allocate ping buffer */
+ ping_pkt_size_ipv4 = ping_data_size + ICMP_MINLEN;
+ ping_buffer_ipv4 = (char*)calloc(1, ping_pkt_size_ipv4);
+ if (!ping_buffer_ipv4)
+ crash_and_burn("can't malloc ping packet");
+}
+
+void socket_set_src_addr_ipv4(int s, struct in_addr* src_addr, int *ident)
+{
+ struct sockaddr_in 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, len) < 0)
+ errno_crash_and_burn("cannot bind source address");
+
+ if (ident) {
+ 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)
+{
+ unsigned long sum;
+
+ /* initialize sum to zero and loop until length (in words) is 0 */
+ for (sum = 0; length > 1; length -= 2) /* sizeof() returns number of bytes, we're interested in number of words */
+ sum += *buffer++; /* add 1 word of buffer to sum and proceed to the next */
+
+ /* we may have an extra byte */
+ if (length == 1)
+ sum += (char)*buffer;
+
+ sum = (sum >> 16) + (sum & 0xFFFF); /* add high 16 to low 16 */
+ sum += (sum >> 16); /* add carry */
+ return ~sum;
+}
+
+int socket_sendto_ping_ipv4(int s, struct sockaddr* saddr, socklen_t saddr_len, uint16_t icmp_seq_nr, uint16_t icmp_id_nr, uint8_t icmp_proto)
+{
+ struct icmp* icp;
+ struct timespec tsorig;
+ long tsorig_ms;
+ int n;
+
+ icp = (struct icmp*)ping_buffer_ipv4;
+
+ icp->icmp_type = icmp_proto;
+ if(icmp_proto == ICMP_TSTAMP) {
+ clock_gettime(CLOCK_REALTIME, &tsorig);
+ tsorig_ms = (tsorig.tv_sec % (24*60*60)) * 1000 + tsorig.tv_nsec / 1000000;
+ icp->icmp_otime = htonl(tsorig_ms);
+ icp->icmp_rtime = 0;
+ icp->icmp_ttime = 0;
+ }
+ icp->icmp_code = 0;
+ icp->icmp_cksum = 0;
+ icp->icmp_seq = htons(icmp_seq_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) {
+ ping_buffer_ipv4[n] = random() & 0xFF;
+ }
+ }
+
+ icp->icmp_cksum = calcsum((unsigned short*)icp, ping_pkt_size_ipv4);
+
+ n = sendto(s, icp, ping_pkt_size_ipv4, 0, saddr, saddr_len);
+
+ return n;
+}
--- /dev/null
+/*
+ * fping: fast-ping, file-ping, favorite-ping, funky-ping
+ *
+ * Ping a list of target hosts in a round robin fashion.
+ * A better ping overall.
+ *
+ * fping website: http://www.fping.org
+ *
+ * Current maintainer of fping: David Schweikert
+ * Please send suggestions and patches to: david@schweikert.ch
+ *
+ *
+ * Original author: Roland Schemers <schemers@stanford.edu>
+ * IPv6 Support: Jeroen Massar <jeroen@unfix.org / jeroen@ipng.nl>
+ * Improved main loop: David Schweikert <david@schweikert.ch>
+ * Debian Merge, TOS settings: Tobi Oetiker <tobi@oetiker.ch>
+ * Bugfixes, byte order & senseful seq.-numbers: Stephan Fuhrmann (stephan.fuhrmann AT 1und1.de)
+ *
+ *
+ * Redistribution and use in source and binary forms are permitted
+ * provided that the above copyright notice and this paragraph are
+ * duplicated in all such forms and that any documentation,
+ * advertising materials, and other materials related to such
+ * distribution and use acknowledge that the software was developed
+ * by Stanford University. The name of the University may not be used
+ * to endorse or promote products derived from this software without
+ * specific prior written permission.
+ * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED
+ * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE.
+ */
+
+#include "config.h"
+#include "fping.h"
+
+#include <fcntl.h>
+#include <netdb.h>
+#include <netinet/in.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/socket.h>
+
+#include <netinet/icmp6.h>
+
+char* ping_buffer_ipv6 = 0;
+size_t ping_pkt_size_ipv6;
+
+int open_ping_socket_ipv6(int *socktype)
+{
+ struct protoent* proto;
+ int s;
+
+ /* confirm that ICMP6 is available on this machine */
+ if ((proto = getprotobyname("ipv6-icmp")) == NULL)
+ crash_and_burn("ipv6-icmp: unknown protocol");
+
+ /* create raw socket for ICMP6 calls (ping) */
+ *socktype = SOCK_RAW;
+ s = socket(AF_INET6, *socktype, proto->p_proto);
+ if (s < 0) {
+ /* try non-privileged icmp6 (works on Mac OSX without privileges, for example) */
+ *socktype = SOCK_DGRAM;
+ s = socket(AF_INET6, *socktype, proto->p_proto);
+ if (s < 0) {
+ return -1;
+ }
+ }
+
+ /* Make sure that we use non-blocking IO */
+ {
+ int flags;
+
+ if ((flags = fcntl(s, F_GETFL, 0)) < 0)
+ perror("fcntl");
+
+ if (fcntl(s, F_SETFL, flags | O_NONBLOCK) < 0)
+ perror("fcntl");
+ }
+
+ return s;
+}
+
+void init_ping_buffer_ipv6(size_t ping_data_size)
+{
+ /* allocate ping buffer */
+ ping_pkt_size_ipv6 = ping_data_size + sizeof(struct icmp6_hdr);
+ ping_buffer_ipv6 = (char*)calloc(1, ping_pkt_size_ipv6);
+ if (!ping_buffer_ipv6)
+ crash_and_burn("can't malloc ping packet");
+}
+
+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");
+
+ if (ident) {
+ memset(&sa, 0, len);
+ if (getsockname(s, (struct sockaddr *)&sa, &len) < 0)
+ errno_crash_and_burn("can't get ICMP6 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)
+{
+ struct icmp6_hdr* icp;
+ int n;
+
+ icp = (struct icmp6_hdr*)ping_buffer_ipv6;
+ icp->icmp6_type = ICMP6_ECHO_REQUEST;
+ icp->icmp6_code = 0;
+ icp->icmp6_seq = htons(icmp_seq_nr);
+ icp->icmp6_id = icmp_id_nr;
+
+ if (random_data_flag) {
+ for (n = sizeof(struct icmp6_hdr); n < ping_pkt_size_ipv6; ++n) {
+ ping_buffer_ipv6[n] = random() & 0xFF;
+ }
+ }
+
+ icp->icmp6_cksum = 0; /* The IPv6 stack calculates the checksum for us... */
+
+ n = sendto(s, icp, ping_pkt_size_ipv6, 0, saddr, saddr_len);
+
+ return n;
+}
--- /dev/null
+fping 5.4 (2025-08-19)
+======================
+
+## Bugfixes
+
+- Memory allocation safety checks for event storage (thanks David.A for bug report)
+- Fix off-by-one boundary check in seqmap code (thanks David.A for bug report)
+- The minimum value for the per-host interval (-i flag) is now 0.001 (milliseconds),
+ since it probably never makes sense to use a smaller value, and to avoid trying
+ to do a too large memory allocation.
+
+fping 5.3 (2025-01-02)
+======================
+
+## New features
+
+- New option --icmp-timestamp to send ICMP timestamp requests (ICMP type 13)
+ instead of ICMP Echo requests (#353 #363, thanks @auerswal and @gsnw-sebast)
+- New option --print-ttl to print returned TTL value (#354, thanks @nalves599)
+- New option --print-tos to print returned TOS value (#335 #346 #347, thanks
+ @auerswal and @gsnw-sebast)
+- New option --check-source (#334, thanks @auerswal)
+- Predefined various timestamp formats (#321, thanks @auerswal and @gsnw-sebast)
+- Print cumulative stats with -Q SECS,cumulative (#315, thanks @auerswal)
+
+## Bugfixes and other changes
+
+- ci: Upgrade actions/upload-artifact to v4 (#360, thanks @gsnw-sebast)
+- ci: Azure Pipeline only trigger when changes are made in the development branch
+ (#359, thanks @gsnw-sebast)
+- ci: Upgrade actions/upload-artifact to v3 (#355, thanks @pevik)
+- ci: Azure Pipeline YAML add docker build (#354, thanks @gsnw-sebast)
+- Dockerfile: change distribution from ubuntu to debian (#350, thanks
+ @gsnw-sebast)
+- Fix warning unused parameter 'reply_timestamp' under macOS (#348, thanks
+ @gsnw-sebast)
+- Fix increase maximum -s value to 65507 (#344, thanks @pevik)
+- ci: use File::Temp to create temporary directory (#343, thanks @auerswal)
+- Fix -k, --fwmark with setuid fping executable (#342, thanks @auerswal)
+- Another batch of additional tests (take 2) (#341, thanks @auerswal)
+- Document that -a and -u are overridden by -c and -C (#338, thanks @auerswal)
+- Fix macOS build warning sets SEQMAP_TIMEOUT_IN_NSSEQMAP_TIMEOUT_IN_NS as INT64_C
+ (#336, thanks @gsnw-sebast)
+- Fix inconsistent limits for address generation via -g, --generator using either
+ range or CIDR (#331, thanks @auerswal)
+- Some additional tests (#329, thanks @auerswal)
+- ci: skip an unreliable test on macOS (#328, thanks @auerswal)
+- Fix incorrect return-value check for a scanf like function (CWE-253) (#323,
+ thanks @gsnw-sebast)
+- A few more tests to increase code coverage a little bit (#320, thanks @auerswal)
+- Github fix: Change to codeql-action-v2 (#319, thanks @gsnw-sebast)
+- Developer function: Debug with Visual Studio Code (#318, thanks @gsnw-sebast)
+
+fping 5.2 (2024-04-21)
+======================
+
+## New features
+
+- New option -X / --fast-reachable to exit immediately once N hosts have been
+ found (#260, thanks @chriscray and @gsnw)
+
+- New option -k / -fwmark to set Linux fwmark mask (#289, thanks @tomangert and
+ @deepkv)
+
+## Bugfixes and other changes
+
+- Always output fatal error messages (#303, thanks @auerswal)
+- Fallback to SO\_TIMESTAMP if SO\_TIMESTAMPNS is not available (#279, thanks
+ @gsnw)
+- Fix "not enough sequence numbers available" error on BSD-like systems (#307,
+ thanks @cagney, @gsnw)
+- Fix running in unprivileged mode (#248, thanks @sfan5)
+- Fix build issue for NetBSD/alpha (#255, thanks @0-wiz-0)
+- Fix build issue for OpenBSD/alpha (#275, thanks @gsnw)
+- Fix build warning for long int usage (#258, thanks @gsnw)
+- Fix build error with musl libc (#263, thanks @kraj)
+- Fix to guard against division by zero (#293, thanks @auerswal)
+- Decouple -a/-u effects from -c (#298, thanks @auerswal)
+- Added contrib/Dockerfile (#224, thanks @darless)
+- Remove host from Netdata chart titles (#253, thanks @ilyam8)
+- Add additional tests (#292, #297, thanks @auerswal)
+- Update github action os images (#282, thanks @gsnw)
+- Fix Azure pipeline tests (#308, thanks @gsnw)
+- Various autoconf fixes (#286, #283, thanks @gsnw)
+- Extended configure script with --enable-debug and output cpu usage (#311,
+ thanks @gsnw)
+- Documentation: Update Netdata website link (#257, thanks @ilyam8)
+- Documentation: fix description of --file option (#268, thanks @MohGeek)
+- Documentation: improve exit status description (#294, thanks @auerswal)
+- Documentation: move description of -i MSEC (#298, thanks @auerswal)
+- Documentation: improve help output for options -c and -C (#302, #auerswal)
+
+
+fping 5.1 (2022-02-06)
+======================
+
+## Bugfixes and other changes
+
+- Use setcap to specify specific files in fping.spec (#232, thanks @zdyxry)
+- Netdata: use host instead name as family label (#226, thanks @k0ste)
+- Netdata: use formatstring macro PRId64 (#229, thanks @gsnw)
+- Allow -4 option to be given multiple times (#215, thanks @normanr)
+- Documentation fix (#208, thanks @timgates42)
+- Retain privileges until after privileged setsockopt (#200, thanks @simetnicbr)
+- Set bind to source only when option is set (#198, thanks @dinoex)
+- Update Azure test pipeline (#197, thanks @gsnw)
+- Fix getnameinfo not called properly for IPv4 (#227, thanks @aafbsd)
+- Fixed wrong timestamp under Free- and OpenBSD and macOS (#217, thanks @gsnw)
+- Documentation updates (#240, thanks @auerswal)
+- Updated autotools (autoconf 2.71, automake 1.16.5, libtool 2.4.6)
+
+
+fping 5.0 (2020-08-05)
+======================
+
+## Incompatible Changes
+
+- In non-quiet loop and count mode, a line is printed for every lost packet
+ (#175, thanks @kbucheli):
+
+ ```
+ $ fping -D -c2 8.8.8.8 8.8.8.7
+ [1596092373.18423] 8.8.8.8 : [0], 64 bytes, 12.8 ms (12.8 avg, 0% loss)
+ [1596092374.18223] 8.8.8.7 : [0], timed out (NaN avg, 100% loss)
+ [1596092374.18424] 8.8.8.8 : [1], 64 bytes, 12.3 ms (12.5 avg, 0% loss)
+ [1596092375.18344] 8.8.8.7 : [1], timed out (NaN avg, 100% loss)
+
+ 8.8.8.8 : xmt/rcv/%loss = 2/2/0%, min/avg/max = 12.3/12.5/12.8
+ 8.8.8.7 : xmt/rcv/%loss = 2/0/100%
+ ```
+
+- The returned size in bytes now always excludes the IP header, so if before it
+ reported '84 bytes' e.g. when using 'fping -l', now it reports '64 bytes'.
+ This is to make the reported size consistent with ping(8) from iputils and
+ also with fping when pinging a IPv6 host (which never included the IPv6
+ header size).
+
+## New features
+
+- The number of sent pings is only counted when the pings are received or have
+ timed out, ensuring that the loss ratio will be always correct. This makes it
+ possible, for example, to use loop mode (-l) with interval statistics (-Q)
+ and a timeout larger than period, without having the issue that initially
+ some pings would be reported as missing (#193)
+
+- Improved precision of measurements from 10us to 1us (#136, thanks @tycho)
+
+## Bugfixes and other changes
+
+- The reported size of received packets is now always correct on Linux even for
+ packets > 4096 bytes (#180)
+
+- Travis CI automated testing now also macos testing and additional ubuntu
+ distributions (#196)
+
+fping 4.4 (2020-07-24)
+======================
+## Bugfixes and other changes
+
+- Fix wrong ident used for normal (non-unprivileged) pings (#191, thanks @tycho)
+- Fix build with --disable-ipv6 (#187, thanks Polynomial-C)
+
+fping 4.3 (2020-07-11)
+======================
+
+## New features
+
+- Linux unprivileged ping support (#173, thanks @tycho)
+- Add SIGQUIT summary support similar to ping (#185, thanks @laddp)
+
+## Bugfixes and other changes
+
+- Corrected long option name of -s to --stats (#148, thanks @wopfel)
+- Do not fail if using fping6 with -6 flag (#149, thanks @stromnet)
+- Fail if interface binding (-I) does not work (#162, thanks @kbucheli)
+- Fix using option -4 when fping is compiled IPv4-only (#154, thanks @pbhenson)
+- Add Azure pipeline test build (#153 and #170, thanks @gsnw)
+- GCC 10 compatibility fixes (#167 and #168, thanks @cranderson)
+- Macos build fix (#174, thanks @tycho)
+- Fix xmt stats in Netdata output (#172, thanks @vlvkobal)
+- Only increase num_alive if response is not a duplicate (#151, thanks @brownowski)
+- Use line buffering for stdout (#179, thanks @bg6cq)
+
+fping 4.2 (2019-02-19)
+======================
+
+## New features
+
+- New option -x / --reachable to check if the number of reachable hosts is >= a certain
+ number. Useful for example to implement connectivity-checks (#138, thanks @deepak0004)
+
+## Bugfixes and other changes
+
+- Allow decimal numbers for '-t', '-i', '-p', and '-Q'
+- Fix build with --disable-ipv6 (#134, thanks @Polynomial-C)
+- Fix hang with '-6', with ipv6 kernel module, but not loaded (#140, thanks @abelbeck)
+- Assume '-6' if the binary is named 'fping6' (this is mostly for special
+ embedded-distro use cases, and not meant to be used generally in place of
+ compiling IPv6-only binary or using '-6', see also the notes in #139, thanks
+ abelbeck)
+- Get rid of warning "timeout (-t) value larger than period (-p) produces unexpected results"
+ (#142, thanks @MrDragon1122)
+
+
+fping 4.1 (2018-09-17)
+======================
+
+## Bugfixes and other changes
+
+- Fix problem when socket fd is 0 (#125, thanks Ramón Novoa!)
+- Fix running on servers with disabled IPv6 (#118, thanks Simon Matter)
+- Allow running "fping -h" or "--help" even when raw socket can't be opened (#131, thanks @teto)
+- Fix build issue with FreeBSD and IPv6 (#132, thanks @gsnw)
+
+fping 4.0 (2017-04-23)
+======================
+
+## Incompatible Changes
+
+##### fping and fping6 unification
+
+fping and fping6 are now unified into one binary. It means that, for example,
+doing 'fping google.com' is going to ping the IPv6 IP of google.com on
+IPv6-enabled hosts.
+
+If you need exact compatibility with old versions, you can configure and
+install fping twice: once for ipv4, and once for ipv6:
+
+ ./configure --disable-ipv6; make clean install
+ ./configure --disable-ipv4 --program-suffix=6; make clean install
+
+##### Option -n, not the same as -d anymore
+
+Option -n / --name is now doing a reverse-DNS lookups on host addresses,
+only if they are given as IP address, but not for hostnames. For example,
+if you write 'fping -n google.com', fping would previously do a
+forward-DNS lookup on google.com, and then a reverse-DNS lookup on the
+resolved IP address. Now, it is just going to keep the name 'google.com'.
+That same behavior can be achieved with the option -d / --rdns (which was
+previously an alias for -n).
+
+ fping<4.0 fping>=4.0
+ fping -n NAME NAME->IP->IPNAME NAME
+ fping -d NAME NAME->IP->IPNAME NAME->IP->IPNAME
+
+##### Discarding of late packets
+
+fping will now discard replies, if they arrive after the defined timeout
+for reply packets, specified with -t. This change is relevant only for the
+count and loop modes, where the measured times should be now more
+consistent (see github issue [#32][i32] for details).
+
+To prevent loosing reply packets because of this change, the default
+timeout in count and loop modes is now automatically adjusted to the
+period interval (up to 2000 ms), but it can be overriden with the -t
+option. The default timeout for non-loop/count modes remains 500 ms.
+
+##### No restrictions by default
+
+fping will not enforce -i >= 1 and -p >= 10 anymore, except if you
+'./configure --enable-safe-limits'.
+
+The reasoning to removing the restrictions by default, is that users can
+clog the network with other tools anyway, and these restrictions are
+sometimes getting in the way (for example if you try to ping a lot of
+hosts).
+
+##### Default interval (-i) changed from 25ms to 10ms
+
+The default minimum interval between ping probes has been changed from
+25ms to 10ms. The reason is that 25ms is very high, considering today's
+fast networks: it generates at most 31 kbps of traffic (for IPv4 and
+default payload size).
+
+## New features
+
+- Unified 'fping' and 'fping6' into one binary ([#80][i80])
+- Long option names for all options
+- IPv6 enabled by default
+- New option -4 to force IPv4
+- New option -6 to force IPv6
+- Keep original name if a hostname is given with -n/--name
+- Option -d/--rdns now always does a rdns-lookup, even for names, as '-n' was doing until now
+- Enforce -t timeout on reply packets, by discarding late packets ([#32][i32])
+- Auto-adjust timeout for -c/-C/-l mode to value of -p
+
+## Bugfixes and other changes
+
+- -i/-p restrictions disabled by default (enable with --enable-safe-limits)
+- Default interval -i changed from 25ms to 10ms
+- Fix compatibility issue with GNU Hurd
+- A C99 compiler is now required
+- Option parsing with optparse (https://github.com/skeeto/optparse). Thanks Christopher Wellons!
+- New changelog file format
+
+[i32]: https://github.com/schweikert/fping/issues/32
+[i80]: https://github.com/schweikert/fping/issues/80
+
+(see doc/CHANGELOG.pre-v4 for older changes)
--- /dev/null
+ * Original author: Roland Schemers <schemers@stanford.edu>
+ * IPv6 Support: Jeroen Massar <jeroen@unfix.org / jeroen@ipng.nl>
+ * Improved main loop: David Schweikert <david@schweikert.ch>
+ * Debian Merge, TOS settings: Tobi Oetiker <tobi@oetiker.ch>
+ * Bugfixes, byte order & senseful seq.-numbers: Stephan Fuhrmann (stephan.fuhrmann AT 1und1.de)
+ *
+ * Redistribution and use in source and binary forms are permitted
+ * provided that the above copyright notice and this paragraph are
+ * duplicated in all such forms and that any documentation,
+ * advertising materials, and other materials related to such
+ * distribution and use acknowledge that the software was developed
+ * by Stanford University. The name of the University may not be used
+ * to endorse or promote products derived from this software without
+ * specific prior written permission.
+ * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED
+ * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE.
--- /dev/null
+Basic Installation
+==================
+
+ These are generic installation instructions.
+
+ --> See the README file for fping-specific instructions. <--
+
+ The `configure' shell script attempts to guess correct values for
+various system-dependent variables used during compilation. It uses
+those values to create a `Makefile' in each directory of the package.
+It may also create one or more `.h' files containing system-dependent
+definitions. Finally, it creates a shell script `config.status' that
+you can run in the future to recreate the current configuration, a file
+`config.cache' that saves the results of its tests to speed up
+reconfiguring, and a file `config.log' containing compiler output
+(useful mainly for debugging `configure').
+
+ If you need to do unusual things to compile the package, please try
+to figure out how `configure' could check whether to do them, and mail
+diffs or instructions to the address given in the `README' so they can
+be considered for the next release. If at some point `config.cache'
+contains results you don't want to keep, you may remove or edit it.
+
+ The file `configure.in' is used to create `configure' by a program
+called `autoconf'. You only need `configure.in' if you want to change
+it or regenerate `configure' using a newer version of `autoconf'.
+
+The simplest way to compile this package is:
+
+ 1. `cd' to the directory containing the package's source code and type
+ `./configure' to configure the package for your system. If you're
+ using `csh' on an old version of System V, you might need to type
+ `sh ./configure' instead to prevent `csh' from trying to execute
+ `configure' itself.
+
+ Running `configure' takes a while. While running, it prints some
+ messages telling which features it is checking for.
+
+ 2. Type `make' to compile the package.
+
+ 3. Optionally, type `make check' to run any self-tests that come with
+ the package.
+
+ 4. Type `make install' to install the programs and any data files and
+ documentation.
+
+ 5. You can remove the program binaries and object files from the
+ source code directory by typing `make clean'. To also remove the
+ files that `configure' created (so you can compile the package for
+ a different kind of computer), type `make distclean'. There is
+ also a `make maintainer-clean' target, but that is intended mainly
+ for the package's developers. If you use it, you may have to get
+ all sorts of other programs in order to regenerate files that came
+ with the distribution.
+
+Compilers and Options
+=====================
+
+ Some systems require unusual options for compilation or linking that
+the `configure' script does not know about. You can give `configure'
+initial values for variables by setting them in the environment. Using
+a Bourne-compatible shell, you can do that on the command line like
+this:
+ CC=c89 CFLAGS=-O2 LIBS=-lposix ./configure
+
+Or on systems that have the `env' program, you can do it like this:
+ env CPPFLAGS=-I/usr/local/include LDFLAGS=-s ./configure
+
+Compiling For Multiple Architectures
+====================================
+
+ You can compile the package for more than one kind of computer at the
+same time, by placing the object files for each architecture in their
+own directory. To do this, you must use a version of `make' that
+supports the `VPATH' variable, such as GNU `make'. `cd' to the
+directory where you want the object files and executables to go and run
+the `configure' script. `configure' automatically checks for the
+source code in the directory that `configure' is in and in `..'.
+
+ If you have to use a `make' that does not supports the `VPATH'
+variable, you have to compile the package for one architecture at a time
+in the source code directory. After you have installed the package for
+one architecture, use `make distclean' before reconfiguring for another
+architecture.
+
+Installation Names
+==================
+
+ By default, `make install' will install the package's files in
+`/usr/local/bin', `/usr/local/man', etc. You can specify an
+installation prefix other than `/usr/local' by giving `configure' the
+option `--prefix=PATH'.
+
+ You can specify separate installation prefixes for
+architecture-specific files and architecture-independent files. If you
+give `configure' the option `--exec-prefix=PATH', the package will use
+PATH as the prefix for installing programs and libraries.
+Documentation and other data files will still use the regular prefix.
+
+ If the package supports it, you can cause programs to be installed
+with an extra prefix or suffix on their names by giving `configure' the
+option `--program-prefix=PREFIX' or `--program-suffix=SUFFIX'.
+
+Optional Features
+=================
+
+ Some packages pay attention to `--enable-FEATURE' options to
+`configure', where FEATURE indicates an optional part of the package.
+They may also pay attention to `--with-PACKAGE' options, where PACKAGE
+is something like `gnu-as' or `x' (for the X Window System). The
+`README' should mention any `--enable-' and `--with-' options that the
+package recognizes.
+
+ For packages that use the X Window System, `configure' can usually
+find the X include and library files automatically, but if it doesn't,
+you can use the `configure' options `--x-includes=DIR' and
+`--x-libraries=DIR' to specify their locations.
+
+Specifying the System Type
+==========================
+
+ There may be some features `configure' can not figure out
+automatically, but needs to determine by the type of host the package
+will run on. Usually `configure' can figure that out, but if it prints
+a message saying it can not guess the host type, give it the
+`--host=TYPE' option. TYPE can either be a short name for the system
+type, such as `sun4', or a canonical name with three fields:
+ CPU-COMPANY-SYSTEM
+
+See the file `config.sub' for the possible values of each field. If
+`config.sub' isn't included in this package, then this package doesn't
+need to know the host type.
+
+ If you are building compiler tools for cross-compiling, you can also
+use the `--target=TYPE' option to select the type of system they will
+produce code for and the `--build=TYPE' option to select the type of
+system on which you are compiling the package.
+
+Sharing Defaults
+================
+
+ If you want to set default values for `configure' scripts to share,
+you can create a site shell script called `config.site' that gives
+default values for variables like `CC', `cache_file', and `prefix'.
+`configure' looks for `PREFIX/share/config.site' if it exists, then
+`PREFIX/etc/config.site' if it exists. Or, you can set the
+`CONFIG_SITE' environment variable to the location of the site script.
+A warning: not all `configure' scripts look for a site script.
+
+Operation Controls
+==================
+
+ `configure' recognizes the following options to control how it
+operates.
+
+`--cache-file=FILE'
+ Use and save the results of the tests in FILE instead of
+ `./config.cache'. Set FILE to `/dev/null' to disable caching, for
+ debugging `configure'.
+
+`--help'
+ Print a summary of the options to `configure', and exit.
+
+`--quiet'
+`--silent'
+`-q'
+ Do not print messages saying which checks are being made.
+
+`--srcdir=DIR'
+ Look for the package's source code in directory DIR. Usually
+ `configure' can determine that directory automatically.
+
+`--version'
+ Print the version of Autoconf used to generate the `configure'
+ script, and exit.
+
+`configure' also accepts some other, not widely useful, options.
--- /dev/null
+SUBDIRS = doc src
+
+EXTRA_DIST = CHANGELOG.md contrib README.md ci/*.sh ci/*.pl
--- /dev/null
+[](https://travis-ci.org/schweikert/fping)
+[](https://coveralls.io/github/schweikert/fping?branch=develop)
+[](https://scan.coverity.com/projects/schweikert-fping)
+
+# fping
+
+fping is a program to send ICMP echo probes to network hosts, similar to ping,
+but much better performing when pinging multiple hosts. fping has a long long
+story: Roland Schemers did publish a first version of it in 1992 and it has
+established itself since then as a standard tool.
+
+_Current maintainer_:
+ David Schweikert \<david@schweikert.ch\>
+
+_Website_:
+ https://fping.org/
+
+_Mailing-list_:
+ https://groups.google.com/group/fping-users
+
+## Installation
+
+If you want to install fping from source, proceed as follows:
+
+0. Run `./autogen.sh`
+ (only if you got the source from Github).
+1. Run `./configure` with the correct arguments.
+ (see: `./configure --help`)
+2. Run `make; make install`.
+3. Make fping either setuid, or, if under Linux:
+ `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. The --fwmark option needs root or
+cap_net_admin.
+
+## Usage
+
+Have a look at the [fping(8)](doc/fping.pod) manual page for usage help.
+(`fping -h` will also give a minimal help output.)
+
+## Credits
+
+* Original author: Roland Schemers (schemers@stanford.edu)
+* Previous maintainer: RL "Bob" Morgan (morgan@stanford.edu)
+* Initial IPv6 Support: Jeroen Massar (jeroen@unfix.org / jeroen@ipng.nl)
+* Other contributors: see [CHANGELOG.md](CHANGELOG.md)
--- /dev/null
+#!/bin/bash
+
+set -e
+set -x
+
+if [[ "$OSTYPE" == "darwin"* ]]; then
+ exit 0
+fi
+
+AUTOCONF=http://ftp.gnu.org/gnu/autoconf/autoconf-2.71.tar.gz
+AUTOMAKE=http://ftp.gnu.org/gnu/automake/automake-1.16.5.tar.gz
+LIBTOOL=http://ftp.gnu.org/gnu/libtool/libtool-2.4.6.tar.gz
+PREFIX=$(pwd)/ci/build
+PATH=$(pwd)/ci/build/bin:$PATH
+
+if [ ! -d ci ]; then
+ echo "you must run this in the root fping directory" >&2
+ exit 1
+fi
+
+# remove standard versions
+sudo apt-get remove -qq autoconf automake autotools-dev libtool
+
+# prepare build environment
+cd ci
+rm -rf build
+mkdir -p build/src
+cd build/src
+
+# autoconf
+(
+AUTOCONF_FILE=$(basename $AUTOCONF)
+AUTOCONF_DIR=$(echo $AUTOCONF_FILE | sed -e 's/\.tar.*//')
+wget $AUTOCONF
+tar xf $AUTOCONF_FILE
+cd $AUTOCONF_DIR
+./configure --prefix=$PREFIX
+make install
+)
+
+# automake
+(
+AUTOMAKE_FILE=$(basename $AUTOMAKE)
+AUTOMAKE_DIR=$(echo $AUTOMAKE_FILE | sed -e 's/\.tar.*//')
+wget $AUTOMAKE
+tar xf $AUTOMAKE_FILE
+cd $AUTOMAKE_DIR
+./configure --prefix=$PREFIX
+make install
+)
+
+# libtool
+(
+LIBTOOL_FILE=$(basename $LIBTOOL)
+LIBTOOL_DIR=$(echo $LIBTOOL_FILE | sed -e 's/\.tar.*//')
+wget $LIBTOOL
+tar xf $LIBTOOL_FILE
+cd $LIBTOOL_DIR
+./configure --prefix=$PREFIX
+make install
+)
--- /dev/null
+#!/bin/sh
+
+set -ex
+
+curl -L http://cpanmin.us | perl - -L $HOME/perl5 App::cpanminus
+$HOME/perl5/bin/cpanm --sudo Test::Command
--- /dev/null
+#!/bin/bash
+
+# only do this on Mac OS X
+if [ `uname -s` != "Darwin" ]; then
+ exit 0;
+fi
+
+if [[ ! `ifconfig lo0` =~ 127\.0\.0\.2 ]]; then
+ sudo ifconfig lo0 127.0.0.2/8 alias
+ sudo ifconfig lo0 127.0.0.3/8 alias
+ sudo ifconfig lo0 127.0.0.4/8 alias
+ sudo ifconfig lo0 127.0.0.5/8 alias
+fi
--- /dev/null
+#!/bin/bash
+set -x
+
+PATH=$(pwd)/ci/build/bin:$PATH
+
+if [ ! -d ci ]; then
+ echo "you must run this in the root fping directory" >&2
+ exit 1
+fi
+
+autoreconf -i
+./configure --enable-ipv4 --enable-ipv6 --enable-safe-limits --prefix=/opt/fping
+make CFLAGS="-g -O0 -fprofile-arcs -ftest-coverage"
+## setcap currently doesn't work anymore on travis-ci
+#sudo setcap cap_net_raw+ep src/fping
+## setcap debugging:
+#pwd
+#df -k .
+#which setcap
+#uname -a
+#mount
+#
+#sudo apt-get install strace
+#sudo strace setcap cap_net_raw+ep src/fping
+
+# use setuid, since setcap is not available
+sudo chown root src/fping
+sudo chmod u+s src/fping
--- /dev/null
+#!/bin/bash
+
+# upload to bintray.com/schweikert
+
+set -e
+
+VERSION=$(ls fping-*.tar.gz | sed -e 's/^fping-//' | sed -e 's/\.tar\.gz$//')
+if [[ "$VERSION" =~ ^[0-9]+\.[0-9]+$ ]]; then
+ REPO=release
+else
+ REPO=beta
+fi
+curl -T fping-$VERSION.tar.gz -uschweikert:$BINTRAY_API_KEY https://api.bintray.com/content/schweikert/$REPO/fping/$VERSION/fping-$VERSION.tar.gz
+echo
--- /dev/null
+#!/bin/sh
+
+set -xe
+
+if [ "$TRAVIS_DIST" = "trusty" ]; then
+ echo "skip coveralls on trusty because coveralls errors out due to python issues"
+ exit 0
+elif [ "$TRAVIS_OS_NAME" = "osx" ]; then
+ pip3 install --user cpp-coveralls
+ PATH="${PATH}:$(python3 -c 'import site; print(site.USER_BASE)')/bin"
+else
+ pip install --user cpp-coveralls
+fi
+
+export COVERALLS_PARALLEL=true
+coveralls --build-root src --exclude src/optparse.c --exclude ci --exclude config.h --gcov-options '\-lp'
--- /dev/null
+#!/bin/sh
+
+set -e
+
+COVERITY_SCAN_PROJECT_NAME=schweikert/fping
+COVERITY_SCAN_EMAIL=david@schweikert.ch
+
+if [ -z "$COVERITY_SCAN_TOKEN" ]; then
+ echo "ERROR: COVERITY_SCAN_TOKEN not defined." >&2
+ exit 1
+fi
+
+curl -o /tmp/cov-analysis-linux64.tgz https://scan.coverity.com/download/linux64 \
+ --form project=$COVERITY_SCAN_PROJECT_NAME --form token=$COVERITY_SCAN_TOKEN
+tar xfz /tmp/cov-analysis-linux64.tgz
+./autogen.sh
+./configure --enable-ipv4 --enable-ipv6 --enable-safe-limits --prefix=/opt/fping
+cov-analysis-linux64-*/bin/cov-build --dir cov-int make -j4
+tar cfz cov-int.tar.gz cov-int
+curl https://scan.coverity.com/builds?project=$COVERITY_SCAN_PROJECT_NAME \
+ --form token=$COVERITY_SCAN_TOKEN \
+ --form email=$COVERITY_SCAN_EMAIL \
+ --form file=@cov-int.tar.gz \
+ --form version="`git rev-parse --short HEAD`" \
+ --form description="`git rev-parse --short HEAD` / $TRAVIS_BUILD_ID "
--- /dev/null
+#!/bin/bash
+
+sudo setcap cap_net_raw,cap_net_admin+ep src/fping
+
+if [[ ! $PATH =~ fping/src ]]; then
+ PATH=/home/dws/checkouts/fping/src:$PATH
+fi
--- /dev/null
+#!/bin/sh
+
+lcov -c -no-external \
+ -d . \
+ -b src \
+ -o lcov-all.info
+
+lcov --remove lcov-all.info -o lcov.info \
+ '*/optparse.c'
--- /dev/null
+#!/bin/sh
+
+set -ex
+
+PATH=`pwd`/src:$PATH
+
+prove ci/test-*.pl
+ci/test-tarball.sh
--- /dev/null
+#!/usr/bin/perl -w
+
+use Test::Command tests => 15;
+use Test::More;
+
+# ping 127.0.0.1
+{
+ my $cmd = Test::Command->new(cmd => "fping 127.0.0.1");
+ $cmd->exit_is_num(0);
+ $cmd->stdout_is_eq("127.0.0.1 is alive\n");
+ $cmd->stderr_is_eq("");
+}
+
+# ping ::1
+SKIP: {
+ #system("/sbin/ifconfig >&2");
+ if($ENV{SKIP_IPV6}) {
+ skip 'Skip IPv6 tests', 3;
+ }
+ my $cmd = Test::Command->new(cmd => "fping ::1");
+ $cmd->exit_is_num(0);
+ $cmd->stdout_is_eq("::1 is alive\n");
+ $cmd->stderr_is_eq("");
+}
+
+# ping ff02::1
+SKIP: {
+ #system("/sbin/ifconfig >&2");
+ if($ENV{SKIP_IPV6}) {
+ skip 'Skip IPv6 tests', 3;
+ }
+ my $cmd = Test::Command->new(cmd => "fping ff02::1");
+ $cmd->exit_is_num(0);
+ $cmd->stdout_is_eq("ff02::1 is alive\n");
+ $cmd->stderr_like(qr{ \[<- .*\]});
+}
+
+# ping 3 times 127.0.0.1
+{
+ my $cmd = Test::Command->new(cmd => "fping -p 100 -C3 127.0.0.1");
+ $cmd->exit_is_num(0);
+ $cmd->stdout_like(qr{127\.0\.0\.1 : \[0\], 64 bytes, \d\.\d+ ms \(\d\.\d+ avg, 0% loss\)
+127\.0\.0\.1 : \[1\], 64 bytes, \d\.\d+ ms \(\d\.\d+ avg, 0% loss\)
+127\.0\.0\.1 : \[2\], 64 bytes, \d\.\d+ ms \(\d\.\d+ avg, 0% loss\)
+});
+ $cmd->stderr_like(qr{127\.0\.0\.1 : \d\.\d+ \d\.\d+ \d\.\d+\n});
+}
+
+# invalid target name
+{
+ my $cmd = Test::Command->new(cmd => "fping host.name.invalid");
+ $cmd->exit_is_num(2);
+ $cmd->stdout_is_eq("");
+ $cmd->stderr_like(qr{host\.name\.invalid: .+\n});
+}
--- /dev/null
+#!/usr/bin/perl -w
+
+use Test::Command tests => 15;
+
+my $I_HELP = " -I, --iface=IFACE bind to a particular interface\n";
+$I_HELP = '' if $^O eq 'darwin';
+
+# fping -h (special pre-parse code path)
+my $cmd1 = Test::Command->new(cmd => "fping -h");
+$cmd1->exit_is_num(0);
+$cmd1->stdout_like(qr{Usage: fping \[options\] \[targets\.\.\.\]
+
+Probing options:
+.*
+ -v, --version show version
+}s);
+$cmd1->stderr_is_eq("");
+
+# fping -4 -h (normal option parsing code path)
+my $cmd4 = Test::Command->new(cmd => "fping -4 -h");
+$cmd4->exit_is_num(0);
+$cmd4->stdout_like(qr{Usage: fping \[options\] \[targets\.\.\.\]
+
+Probing options:
+.*
+ -v, --version show version
+}s);
+$cmd4->stderr_is_eq("");
+
+# fping -v
+my $cmd2 = Test::Command->new(cmd => "fping -v");
+$cmd2->exit_is_num(0);
+$cmd2->stdout_like(qr{fping: Version \S+});
+$cmd2->stderr_is_eq("");
+
+# fping with unknown option
+my $cmd3 = Test::Command->new(cmd => "fping -Z");
+$cmd3->exit_is_num(1);
+$cmd3->stdout_is_eq("");
+$cmd3->stderr_like(qr{^fping: (illegal|invalid) option -- '?Z'?\nsee 'fping -h' for usage information\n$});
+
+# fping with unknown long option
+my $cmd5 = Test::Command->new(cmd => "fping --unknown-long-option");
+$cmd5->exit_is_num(1);
+$cmd5->stdout_is_eq("");
+$cmd5->stderr_like(qr{^fping: (illegal|invalid) option -- '?unknown-long-option'?\nsee 'fping -h' for usage information\n$});
--- /dev/null
+#!/usr/bin/perl -w
+
+use Test::Command tests => 36;
+
+# fping -i 0
+my $cmd1 = Test::Command->new(cmd => "fping -i 0 -T10 -g 127.0.0.1/29");
+$cmd1->exit_is_num(1);
+$cmd1->stdout_is_eq("");
+$cmd1->stderr_is_eq(<<END);
+fping: -i must be >= 1
+END
+
+# fping -p 9
+my $cmd2 = Test::Command->new(cmd => "fping -c3 -p 9 127.0.0.1");
+$cmd2->exit_is_num(1);
+$cmd2->stdout_is_eq("");
+$cmd2->stderr_is_eq(<<END);
+fping: -p must be >= 10
+END
+
+# fping -H 300
+my $cmd5 = Test::Command->new(cmd => "fping -H 300 127.0.0.1");
+$cmd5->exit_is_num(1);
+$cmd5->stdout_is_eq("");
+$cmd5->stderr_is_eq("fping: ttl 300 out of range\n");
+
+# fping -a -u
+my $cmd6 = Test::Command->new(cmd => "fping -a -u 127.0.0.1");
+$cmd6->exit_is_num(1);
+$cmd6->stdout_is_eq("");
+$cmd6->stderr_is_eq("fping: specify only one of a, u\n");
+
+# fping -c -l
+my $cmd7 = Test::Command->new(cmd => "fping -c3 -l 127.0.0.1");
+$cmd7->exit_is_num(1);
+$cmd7->stdout_is_eq("");
+$cmd7->stderr_is_eq("fping: specify only one of c, l\n");
+
+# fping -b 65508
+my $cmd8 = Test::Command->new(cmd => "fping -b 65508 127.0.0.1");
+$cmd8->exit_is_num(1);
+$cmd8->stdout_is_eq("");
+$cmd8->stderr_is_eq("fping: data size 65508 not valid, must not be larger than 65507\n");
+
+# fping -B 0.9
+my $cmd9 = Test::Command->new(cmd => "fping -B 0.9 127.0.0.1");
+$cmd9->exit_is_num(1);
+$cmd9->stdout_is_eq("");
+$cmd9->stderr_is_eq("fping: backoff factor 0.9 not valid, must be between 1.0 and 5.0\n");
+
+# fping -B 5.1
+my $cmd10 = Test::Command->new(cmd => "fping -B 5.1 127.0.0.1");
+$cmd10->exit_is_num(1);
+$cmd10->stdout_is_eq("");
+$cmd10->stderr_is_eq("fping: backoff factor 5.1 not valid, must be between 1.0 and 5.0\n");
+
+# non-negative only
+for my $arg (qw(i p Q t)) {
+ my $cmd = Test::Command->new(cmd => "fping -$arg -1");
+ $cmd->exit_is_num(1);
+ $cmd->stdout_is_eq("");
+ $cmd->stderr_like(qr{Usage:});
+}
--- /dev/null
+#!/usr/bin/perl -w
+
+use Test::Command tests => 68;
+use Test::More;
+use Time::HiRes qw(gettimeofday tv_interval);
+
+# -4 only use IPv4 addresses
+# -6 only use IPv6 addresses
+# -a show targets that are alive
+# -A show targets by address
+# -b n amount of ping data to send, in bytes (default 56)
+# -B f set exponential backoff factor to f
+
+# fping -4 -6
+{
+my $cmd = Test::Command->new(cmd => "fping -4 -6 127.0.0.1");
+$cmd->exit_is_num(1);
+$cmd->stdout_is_eq("");
+$cmd->stderr_is_eq("fping: can't specify both -4 and -6\n");
+}
+
+# fping -6 -4
+{
+my $cmd = Test::Command->new(cmd => "fping -6 -4 127.0.0.1");
+$cmd->exit_is_num(1);
+$cmd->stdout_is_eq("");
+$cmd->stderr_is_eq("fping: can't specify both -4 and -6\n");
+}
+
+# fping -4
+{
+my $cmd = Test::Command->new(cmd => "fping -4 127.0.0.1");
+$cmd->exit_is_num(0);
+$cmd->stdout_is_eq("127.0.0.1 is alive\n");
+$cmd->stderr_is_eq("");
+}
+
+{
+my $cmd = Test::Command->new(cmd => "fping -4 ::1");
+$cmd->exit_is_num(2);
+$cmd->stdout_is_eq("");
+$cmd->stderr_like(qr{^::1:.*(not supported|not known)});
+}
+
+# fping -6
+SKIP: {
+ if($ENV{SKIP_IPV6}) {
+ skip 'Skip IPv6 tests', 3;
+ }
+ my $cmd = Test::Command->new(cmd => "fping -6 ::1");
+ $cmd->exit_is_num(0);
+ $cmd->stdout_is_eq("::1 is alive\n");
+ $cmd->stderr_is_eq("");
+}
+
+{
+my $cmd = Test::Command->new(cmd => "fping -6 127.0.0.1");
+$cmd->exit_is_num(2);
+$cmd->stdout_is_eq("");
+$cmd->stderr_like(qr{127\.0\.0\.1:.*(not supported|not known)});
+}
+
+# fping -a
+{
+my $cmd = Test::Command->new(cmd => "fping -a 127.0.0.1 127.0.0.2");
+$cmd->exit_is_num(0);
+$cmd->stdout_is_eq("127.0.0.1\n127.0.0.2\n");
+$cmd->stderr_is_eq("");
+}
+
+# fping -a --print-ttl
+{
+my $cmd = Test::Command->new(cmd => "fping -a --print-ttl 127.0.0.1 127.0.0.2");
+$cmd->exit_is_num(0);
+$cmd->stdout_like(qr{127\.0\.0\.1 \(TTL \d+\)\n127\.0\.0\.2 \(TTL \d+\)\n});
+$cmd->stderr_is_eq("");
+}
+
+# fping -a --icmp-timestamp
+SKIP: {
+if($^O eq 'darwin') {
+ skip 'On macOS, this test is unreliable', 3;
+}
+my $cmd = Test::Command->new(cmd => "fping -a --icmp-timestamp 127.0.0.1");
+$cmd->exit_is_num(0);
+$cmd->stdout_like(qr{127\.0\.0\.1 timestamps: Originate=\d+ Receive=\d+ Transmit=\d+ Localreceive=\d+});
+$cmd->stderr_is_eq("");
+}
+
+# fping --print-ttl
+{
+my $cmd = Test::Command->new(cmd => "fping --print-ttl 127.0.0.1");
+$cmd->exit_is_num(0);
+$cmd->stdout_like(qr{127\.0\.0\.1 is alive \(TTL \d+\)});
+$cmd->stderr_is_eq("");
+}
+
+# fping --icmp-timestamp
+SKIP: {
+if($^O eq 'darwin') {
+ skip 'On macOS, this test is unreliable', 3;
+}
+my $cmd = Test::Command->new(cmd => "fping --icmp-timestamp 127.0.0.1");
+$cmd->exit_is_num(0);
+$cmd->stdout_like(qr{127\.0\.0\.1 is alive, timestamps: Originate=\d+ Receive=\d+ Transmit=\d+ Localreceive=\d+});
+$cmd->stderr_is_eq("");
+}
+
+# fping --icmp-timestamp --print-tos --print-ttl -e
+SKIP: {
+if($^O eq 'darwin') {
+ skip 'On macOS, this test is unreliable', 3;
+}
+my $cmd = Test::Command->new(cmd => "fping --icmp-timestamp --print-tos --print-ttl -e 127.0.0.1");
+$cmd->exit_is_num(0);
+$cmd->stdout_like(qr{127\.0\.0\.1 is alive, timestamps: Originate=\d+ Receive=\d+ Transmit=\d+ Localreceive=\d+ \(TOS \d+\) \(TTL \d+\) \(\d+(\.\d*)? ms\)});
+$cmd->stderr_is_eq("");
+}
+
+# fping --icmp-timestamp ::1
+SKIP: {
+ if($ENV{SKIP_IPV6}) {
+ skip 'Skip IPv6 tests', 3;
+ }
+ my $cmd = Test::Command->new(cmd => "fping --icmp-timestamp ::1");
+ $cmd->exit_is_num(2);
+ $cmd->stdout_is_eq("");
+ $cmd->stderr_like(qr{^::1:.*(not supported|not known)});
+}
+
+# fping --print-ttl with IPv6
+SKIP: {
+ if($ENV{SKIP_IPV6}) {
+ skip 'Skip IPv6 tests', 3;
+ }
+ my $cmd = Test::Command->new(cmd => "fping --print-ttl ::1");
+ $cmd->exit_is_num(0);
+ $cmd->stdout_like(qr{::1 is alive \(TTL unknown\)\n});
+ $cmd->stderr_is_eq("");
+}
+
+# fping -A
+{
+my $cmd = Test::Command->new(cmd => "fping -4 -A localhost");
+$cmd->exit_is_num(0);
+$cmd->stdout_is_eq("127.0.0.1 is alive\n");
+$cmd->stderr_is_eq("");
+}
+
+# fping -b
+{
+my $cmd = Test::Command->new(cmd => "fping -b 1000 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 --icmp-timestamp -b
+{
+my $cmd = Test::Command->new(cmd => "fping --icmp-timestamp -b 1000 127.0.0.1");
+$cmd->exit_is_num(1);
+$cmd->stdout_is_eq("");
+$cmd->stderr_like(qr{cannot change ICMP Timestamp size});
+}
+
+# fping -b --icmp-timestamp
+{
+my $cmd = Test::Command->new(cmd => "fping -b 1000 --icmp-timestamp 127.0.0.1");
+$cmd->exit_is_num(1);
+$cmd->stdout_is_eq("");
+$cmd->stderr_like(qr{cannot change ICMP Timestamp size});
+}
+
+# fping --icmp-timestamp -b 12 (ICMP Timestamp data size)
+{
+my $cmd = Test::Command->new(cmd => "fping --icmp-timestamp -b 12 127.0.0.1");
+$cmd->exit_is_num(1);
+$cmd->stdout_is_eq("");
+$cmd->stderr_like(qr{cannot change ICMP Timestamp size});
+}
+
+# fping -6 --icmp-timestamp
+SKIP: {
+ if($ENV{SKIP_IPV6}) {
+ skip 'Skip IPv6 tests', 3;
+ }
+ my $cmd = Test::Command->new(cmd => "fping -6 --icmp-timestamp ::1");
+ $cmd->exit_is_num(1);
+ $cmd->stdout_is_eq("");
+ $cmd->stderr_like(qr{ICMP Timestamp is IPv4 only});
+}
+
+# fping --icmp-timestamp -6
+SKIP: {
+ if($ENV{SKIP_IPV6}) {
+ skip 'Skip IPv6 tests', 3;
+ }
+ my $cmd = Test::Command->new(cmd => "fping --icmp-timestamp -6 ::1");
+ $cmd->exit_is_num(1);
+ $cmd->stdout_is_eq("");
+ $cmd->stderr_is_eq("fping: can't specify both -4 and -6\n");
+}
+
+# fping -B
+SKIP: {
+ if($^O eq 'darwin') {
+ skip 'timing test not reliable on macOS', 5;
+ }
+ my $t0 = [gettimeofday];
+ my $cmd = Test::Command->new(cmd => "fping -t 100 -r 3 -B 2 8.8.8.7");
+ $cmd->exit_is_num(1);
+ $cmd->stdout_is_eq("8.8.8.7 is unreachable\n");
+ $cmd->stderr_like(qr{^(|(8.8.8.7: error while sending ping: No route to host\n)+)$});
+ my $elapsed = tv_interval($t0);
+ # 0.1 + 0.2 + 0.4 + 0.8 = 1.5
+ cmp_ok($elapsed, '>=', 1.5);
+ cmp_ok($elapsed, '<', 1.9);
+}
--- /dev/null
+#!/usr/bin/perl -w
+
+use Test::Command tests => 87;
+use Test::More;
+
+# -c n count of pings to send to each target (default 1)
+# -C n same as -c, report results in verbose format
+# --check-source discard replies not from target address
+# -d reverse name lookup
+# -D print timestamp before each output line
+# -e show elapsed time on return packets
+
+# fping -c n
+{
+my $cmd = Test::Command->new(cmd => "fping -4 -c 2 -p 100 localhost 127.0.0.1");
+$cmd->exit_is_num(0);
+$cmd->stdout_like(qr{localhost : \[0\], 64 bytes, \d\.\d+ ms \(\d\.\d+ avg, 0% loss\)
+127\.0\.0\.1 : \[0\], 64 bytes, \d\.\d+ ms \(\d\.\d+ avg, 0% loss\)
+localhost : \[1\], 64 bytes, \d\.\d+ ms \(\d\.\d+ avg, 0% loss\)
+127\.0\.0\.1 : \[1\], 64 bytes, \d\.\d+ ms \(\d\.\d+ avg, 0% loss\)
+});
+
+$cmd->stderr_like(qr{localhost : xmt/rcv/%loss = 2/2/0%, min/avg/max = \d\.\d+/\d\.\d+/\d\.\d+
+127\.0\.0\.1 : xmt/rcv/%loss = 2/2/0%, min/avg/max = \d\.\d+/\d\.\d+/\d\.\d+
+});
+}
+
+# fping -c n --print-tos --print-ttl
+{
+my $cmd = Test::Command->new(cmd => "fping -4 -c 2 -p 100 --print-tos --print-ttl localhost 127.0.0.1");
+$cmd->exit_is_num(0);
+$cmd->stdout_like(qr{localhost : \[0\], 64 bytes, \d\.\d+ ms \(\d\.\d+ avg, 0% loss\) \(TOS \d+\) \(TTL \d+\)
+127\.0\.0\.1 : \[0\], 64 bytes, \d\.\d+ ms \(\d\.\d+ avg, 0% loss\) \(TOS \d+\) \(TTL \d+\)
+localhost : \[1\], 64 bytes, \d\.\d+ ms \(\d\.\d+ avg, 0% loss\) \(TOS \d+\) \(TTL \d+\)
+127\.0\.0\.1 : \[1\], 64 bytes, \d\.\d+ ms \(\d\.\d+ avg, 0% loss\) \(TOS \d+\) \(TTL \d+\)
+});
+
+$cmd->stderr_like(qr{localhost : xmt/rcv/%loss = 2/2/0%, min/avg/max = \d\.\d+/\d\.\d+/\d\.\d+
+127\.0\.0\.1 : xmt/rcv/%loss = 2/2/0%, min/avg/max = \d\.\d+/\d\.\d+/\d\.\d+
+});
+}
+
+# fping -c n -q
+{
+my $cmd = Test::Command->new(cmd => "fping -q -c 2 -p 100 localhost 127.0.0.1");
+$cmd->exit_is_num(0);
+$cmd->stdout_is_eq("");
+$cmd->stderr_like(qr{localhost : xmt/rcv/%loss = 2/2/0%, min/avg/max = \d\.\d+/\d\.\d+/\d\.\d+
+127\.0\.0\.1 : xmt/rcv/%loss = 2/2/0%, min/avg/max = \d\.\d+/\d\.\d+/\d\.\d+
+});
+}
+
+# fping -c n -a (-a is ignored)
+{
+my $cmd = Test::Command->new(cmd => "fping -a -c 2 -p 100 localhost 127.0.0.1");
+$cmd->exit_is_num(0);
+$cmd->stdout_like(qr{localhost : \[0\], 64 bytes, \d\.\d+ ms \(\d\.\d+ avg, 0% loss\)
+127\.0\.0\.1 : \[0\], 64 bytes, \d\.\d+ ms \(\d\.\d+ avg, 0% loss\)
+localhost : \[1\], 64 bytes, \d\.\d+ ms \(\d\.\d+ avg, 0% loss\)
+127\.0\.0\.1 : \[1\], 64 bytes, \d\.\d+ ms \(\d\.\d+ avg, 0% loss\)
+});
+
+$cmd->stderr_like(qr{localhost : xmt/rcv/%loss = 2/2/0%, min/avg/max = \d\.\d+/\d\.\d+/\d\.\d+
+127\.0\.0\.1 : xmt/rcv/%loss = 2/2/0%, min/avg/max = \d\.\d+/\d\.\d+/\d\.\d+
+});
+}
+
+# fping -c n -u (-u is ignored)
+{
+my $cmd = Test::Command->new(cmd => "fping -u -c 2 -p 100 localhost 127.0.0.1");
+$cmd->exit_is_num(0);
+$cmd->stdout_like(qr{localhost : \[0\], 64 bytes, \d\.\d+ ms \(\d\.\d+ avg, 0% loss\)
+127\.0\.0\.1 : \[0\], 64 bytes, \d\.\d+ ms \(\d\.\d+ avg, 0% loss\)
+localhost : \[1\], 64 bytes, \d\.\d+ ms \(\d\.\d+ avg, 0% loss\)
+127\.0\.0\.1 : \[1\], 64 bytes, \d\.\d+ ms \(\d\.\d+ avg, 0% loss\)
+});
+
+$cmd->stderr_like(qr{localhost : xmt/rcv/%loss = 2/2/0%, min/avg/max = \d\.\d+/\d\.\d+/\d\.\d+
+127\.0\.0\.1 : xmt/rcv/%loss = 2/2/0%, min/avg/max = \d\.\d+/\d\.\d+/\d\.\d+
+});
+}
+
+# fping -c n ff02::1
+SKIP: {
+ if($ENV{SKIP_IPV6}) {
+ skip 'Skip IPv6 tests', 3;
+ }
+ my $cmd = Test::Command->new(cmd => "fping -c 1 ff02::1");
+ $cmd->exit_is_num(0);
+ $cmd->stdout_like(qr{ff02::1 : \[0\], 64 bytes, \d\.\d+ ms \(\d\.\d+ avg, 0% loss\)\n});
+ $cmd->stderr_like(qr{ \[<- .*\]
+ff02::1 : xmt/rcv/%loss = 1/1/0%, min/avg/max = \d\.\d+/\d\.\d+/\d\.\d+\n});
+}
+
+# fping --icmp-timestamp -c n 127.0.0.1
+SKIP: {
+if($^O eq 'darwin') {
+ skip 'On macOS, this test is unreliable', 3;
+}
+my $cmd = Test::Command->new(cmd => "fping -4 --icmp-timestamp -c 2 127.0.0.1");
+$cmd->exit_is_num(0);
+$cmd->stdout_like(qr{127\.0\.0\.1 : \[0\], 20 bytes, \d\.\d+ ms \(\d\.\d+ avg, 0% loss\), timestamps: Originate=\d+ Receive=\d+ Transmit=\d+ Localreceive=\d+
+127\.0\.0\.1 : \[1\], 20 bytes, \d\.\d+ ms \(\d\.\d+ avg, 0% loss\), timestamps: Originate=\d+ Receive=\d+ Transmit=\d+ Localreceive=\d+
+});
+
+$cmd->stderr_like(qr{127\.0\.0\.1 : xmt/rcv/%loss = 2/2/0%, min/avg/max = \d\.\d+/\d\.\d+/\d\.\d+
+});
+}
+
+# fping --icmp-timestamp --print-tos --print-ttl -c n 127.0.0.1
+SKIP: {
+if($^O eq 'darwin') {
+ skip 'On macOS, this test is unreliable', 3;
+}
+my $cmd = Test::Command->new(cmd => "fping -4 --icmp-timestamp --print-tos --print-ttl -p 100 -c 2 127.0.0.1");
+$cmd->exit_is_num(0);
+$cmd->stdout_like(qr{127\.0\.0\.1 : \[0\], 20 bytes, \d\.\d+ ms \(\d\.\d+ avg, 0% loss\), timestamps: Originate=\d+ Receive=\d+ Transmit=\d+ Localreceive=\d+ \(TOS \d+\) \(TTL \d+\)
+127\.0\.0\.1 : \[1\], 20 bytes, \d\.\d+ ms \(\d\.\d+ avg, 0% loss\), timestamps: Originate=\d+ Receive=\d+ Transmit=\d+ Localreceive=\d+ \(TOS \d+\) \(TTL \d+\)
+});
+
+$cmd->stderr_like(qr{127\.0\.0\.1 : xmt/rcv/%loss = 2/2/0%, min/avg/max = \d\.\d+/\d\.\d+/\d\.\d+
+});
+}
+
+# fping -C n
+{
+my $cmd = Test::Command->new(cmd => "fping -4 -C 2 -p 100 localhost 127.0.0.1");
+$cmd->exit_is_num(0);
+$cmd->stdout_like(qr{localhost : \[0\], 64 bytes, \d\.\d+ ms \(\d\.\d+ avg, 0% loss\)
+127\.0\.0\.1 : \[0\], 64 bytes, \d\.\d+ ms \(\d\.\d+ avg, 0% loss\)
+localhost : \[1\], 64 bytes, \d\.\d+ ms \(\d\.\d+ avg, 0% loss\)
+127\.0\.0\.1 : \[1\], 64 bytes, \d\.\d+ ms \(\d\.\d+ avg, 0% loss\)
+});
+
+$cmd->stderr_like(qr{localhost : \d\.\d+ \d\.\d+
+127\.0\.0\.1 : \d\.\d+ \d\.\d+
+});
+}
+
+# fping -C n --print-tos --print-ttl
+{
+my $cmd = Test::Command->new(cmd => "fping -4 -C 2 -p 100 --print-tos --print-ttl localhost 127.0.0.1");
+$cmd->exit_is_num(0);
+$cmd->stdout_like(qr{localhost : \[0\], 64 bytes, \d\.\d+ ms \(\d\.\d+ avg, 0% loss\) \(TOS \d+\) \(TTL \d+\)
+127\.0\.0\.1 : \[0\], 64 bytes, \d\.\d+ ms \(\d\.\d+ avg, 0% loss\) \(TOS \d+\) \(TTL \d+\)
+localhost : \[1\], 64 bytes, \d\.\d+ ms \(\d\.\d+ avg, 0% loss\) \(TOS \d+\) \(TTL \d+\)
+127\.0\.0\.1 : \[1\], 64 bytes, \d\.\d+ ms \(\d\.\d+ avg, 0% loss\) \(TOS \d+\) \(TTL \d+\)
+});
+
+$cmd->stderr_like(qr{localhost : \d\.\d+ \d\.\d+
+127\.0\.0\.1 : \d\.\d+ \d\.\d+
+});
+}
+
+# fping -C n -q
+{
+my $cmd = Test::Command->new(cmd => "fping -C 5 -q -p 100 localhost");
+$cmd->exit_is_num(0);
+$cmd->stdout_is_eq("");
+$cmd->stderr_like(qr{localhost :( \d\.\d+){5}
+});
+}
+
+# fping -C n -a (-a is ignored)
+{
+my $cmd = Test::Command->new(cmd => "fping -a -C 2 -p 100 localhost 127.0.0.1");
+$cmd->exit_is_num(0);
+$cmd->stdout_like(qr{localhost : \[0\], 64 bytes, \d\.\d+ ms \(\d\.\d+ avg, 0% loss\)
+127\.0\.0\.1 : \[0\], 64 bytes, \d\.\d+ ms \(\d\.\d+ avg, 0% loss\)
+localhost : \[1\], 64 bytes, \d\.\d+ ms \(\d\.\d+ avg, 0% loss\)
+127\.0\.0\.1 : \[1\], 64 bytes, \d\.\d+ ms \(\d\.\d+ avg, 0% loss\)
+});
+
+$cmd->stderr_like(qr{localhost : \d\.\d+ \d\.\d+
+127\.0\.0\.1 : \d\.\d+ \d\.\d+
+});
+}
+
+# fping -C n -u (-u is ignored)
+{
+my $cmd = Test::Command->new(cmd => "fping -u -C 2 -p 100 localhost 127.0.0.1");
+$cmd->exit_is_num(0);
+$cmd->stdout_like(qr{localhost : \[0\], 64 bytes, \d\.\d+ ms \(\d\.\d+ avg, 0% loss\)
+127\.0\.0\.1 : \[0\], 64 bytes, \d\.\d+ ms \(\d\.\d+ avg, 0% loss\)
+localhost : \[1\], 64 bytes, \d\.\d+ ms \(\d\.\d+ avg, 0% loss\)
+127\.0\.0\.1 : \[1\], 64 bytes, \d\.\d+ ms \(\d\.\d+ avg, 0% loss\)
+});
+
+$cmd->stderr_like(qr{localhost : \d\.\d+ \d\.\d+
+127\.0\.0\.1 : \d\.\d+ \d\.\d+
+});
+}
+
+# fping -C n -i -q
+{
+my $cmd = Test::Command->new(cmd => "fping --quiet --interval=1 --vcount=20 --period=50 127.0.0.1 127.0.0.2");
+$cmd->exit_is_num(0);
+$cmd->stdout_is_eq("");
+$cmd->stderr_like(qr{127\.0\.0\.1 :( \d\.\d+){20}
+127\.0\.0\.2 :( \d\.\d+){20}
+});
+}
+
+# fping -C n ff02::1
+SKIP: {
+ if($ENV{SKIP_IPV6}) {
+ skip 'Skip IPv6 tests', 3;
+ }
+ my $cmd = Test::Command->new(cmd => "fping -C 1 ff02::1");
+ $cmd->exit_is_num(0);
+ $cmd->stdout_like(qr{ff02::1 : \[0\], 64 bytes, \d\.\d+ ms \(\d\.\d+ avg, 0% loss\)\n});
+ $cmd->stderr_like(qr{ \[<- .*\]\nff02::1 : \d\.\d+\n});
+}
+
+# fping --check-source
+{
+my $cmd = Test::Command->new(cmd => "fping --check-source 127.0.0.1 127.0.0.2");
+$cmd->exit_is_num(0);
+$cmd->stdout_is_eq("127.0.0.1 is alive\n127.0.0.2 is alive\n");
+$cmd->stderr_is_eq("");
+}
+
+# fping --check-source (to IPv6 multicast address -> accept no reply)
+SKIP: {
+ if($ENV{SKIP_IPV6}) {
+ skip 'Skip IPv6 tests', 3;
+ }
+ my $cmd = Test::Command->new(cmd => "fping --check-source ff02::1");
+ $cmd->exit_is_num(1);
+ $cmd->stdout_is_eq("ff02::1 is unreachable\n");
+ $cmd->stderr_is_eq("");
+}
+
+# fping -c N --check-source
+SKIP: {
+ if($ENV{SKIP_IPV6}) {
+ skip 'Skip IPv6 tests', 3;
+ }
+ my $cmd = Test::Command->new(cmd => "fping -c1 --check-source 127.0.0.1 ff02::1");
+ $cmd->exit_is_num(1);
+ $cmd->stdout_like(qr{127\.0\.0\.1 : \[0\], 64 bytes, \d\.\d+ ms \(\d\.\d+ avg, 0% loss\)
+ff02::1 : \[0\], timed out \(NaN avg, 100% loss\)
+});
+ $cmd->stderr_like(qr{
+127\.0\.0\.1 : xmt/rcv/%loss = 1/1/0%, min/avg/max = \d\.\d+/\d\.\d+/\d\.\d+
+ff02::1 : xmt/rcv/%loss = 1/0/100%
+});
+}
+
+# fping -C N --check-source
+SKIP: {
+ if($ENV{SKIP_IPV6}) {
+ skip 'Skip IPv6 tests', 3;
+ }
+ my $cmd = Test::Command->new(cmd => "fping -C1 --check-source 127.0.0.1 ff02::1");
+ $cmd->exit_is_num(1);
+ $cmd->stdout_like(qr{127\.0\.0\.1 : \[0\], 64 bytes, \d\.\d+ ms \(\d\.\d+ avg, 0% loss\)
+ff02::1 : \[0\], timed out \(NaN avg, 100% loss\)
+});
+ $cmd->stderr_like(qr{
+127\.0\.0\.1 : \d\.\d+
+ff02::1 : -
+});
+}
+
+# fping -d
+{
+my $cmd = Test::Command->new(cmd => "fping -d 127.0.0.1");
+$cmd->exit_is_num(0);
+$cmd->stdout_is_eq("localhost is alive\n");
+$cmd->stderr_is_eq("");
+}
+
+# fping -D
+{
+my $cmd = Test::Command->new(cmd => "fping -D -c 2 -p 100 127.0.0.1");
+$cmd->exit_is_num(0);
+$cmd->stdout_like(qr{\[\d+\.\d+\] 127\.0\.0\.1 : \[0\], 64 bytes, \d\.\d+ ms \(\d\.\d+ avg, 0% loss\)
+\[\d+\.\d+\] 127\.0\.0\.1 : \[1\], 64 bytes, \d\.\d+ ms \(\d\.\d+ avg, 0% loss\)
+});
+
+$cmd->stderr_like(qr{127\.0\.0\.1 : xmt/rcv/%loss = 2/2/0%, min/avg/max = \d\.\d+/\d\.\d+/\d\.\d+
+});
+}
+
+# fping -D (timestamp not before 2001-09-09)
+{
+my $cmd = Test::Command->new(cmd => "fping -D -c 2 -p 100 127.0.0.1");
+$cmd->exit_is_num(0);
+$cmd->stdout_like(qr{\[[1-9]\d{9,}\.\d+\] 127\.0\.0\.1 : \[0\], 64 bytes, \d\.\d+ ms \(\d\.\d+ avg, 0% loss\)
+\[[1-9]\d{9,}\.\d+\] 127\.0\.0\.1 : \[1\], 64 bytes, \d\.\d+ ms \(\d\.\d+ avg, 0% loss\)
+});
+$cmd->stderr_like(qr{127\.0\.0\.1 : xmt/rcv/%loss = 2/2/0%, min/avg/max = \d\.\d+/\d\.\d+/\d\.\d+
+});
+}
+
+# fping -D --timestamp-format=ctime
+{
+my $cmd = Test::Command->new(cmd => "fping -D --timestamp-format=ctime -c 2 -p 100 127.0.0.1");
+$cmd->exit_is_num(0);
+$cmd->stdout_like(qr{\[\w+\s\w+\s+\d+\s[\d+:]+\s\d+\] 127\.0\.0\.1 : \[0\], 64 bytes, \d\.\d+ ms \(\d\.\d+ avg, 0% loss\)
+\[\w+\s\w+\s+\d+\s[\d+:]+\s\d+\] 127\.0\.0\.1 : \[1\], 64 bytes, \d\.\d+ ms \(\d\.\d+ avg, 0% loss\)
+});
+
+$cmd->stderr_like(qr{127\.0\.0\.1 : xmt/rcv/%loss = 2/2/0%, min/avg/max = \d\.\d+/\d\.\d+/\d\.\d+
+});
+}
+
+# fping -D --timestamp-format=iso
+{
+my $cmd = Test::Command->new(cmd => "fping -D --timestamp-format=iso -c 2 -p 100 127.0.0.1");
+$cmd->exit_is_num(0);
+$cmd->stdout_like(qr{\[[\d+-]+T[\d+:]+\+\d+\] 127\.0\.0\.1 : \[0\], 64 bytes, \d\.\d+ ms \(\d\.\d+ avg, 0% loss\)
+\[[\d+-]+T[\d+:]+\+\d+\] 127\.0\.0\.1 : \[1\], 64 bytes, \d\.\d+ ms \(\d\.\d+ avg, 0% loss\)
+});
+
+$cmd->stderr_like(qr{127\.0\.0\.1 : xmt/rcv/%loss = 2/2/0%, min/avg/max = \d\.\d+/\d\.\d+/\d\.\d+
+});
+}
+
+# fping -D --timestamp-format=rfc3339
+{
+my $cmd = Test::Command->new(cmd => "fping -D --timestamp-format=rfc3339 -c 2 -p 100 127.0.0.1");
+$cmd->exit_is_num(0);
+$cmd->stdout_like(qr{\[[\d+-]+\s[\d+:]+\] 127\.0\.0\.1 : \[0\], 64 bytes, \d\.\d+ ms \(\d\.\d+ avg, 0% loss\)
+\[[\d+-]+\s[\d+:]+\] 127\.0\.0\.1 : \[1\], 64 bytes, \d\.\d+ ms \(\d\.\d+ avg, 0% loss\)
+});
+
+$cmd->stderr_like(qr{127\.0\.0\.1 : xmt/rcv/%loss = 2/2/0%, min/avg/max = \d\.\d+/\d\.\d+/\d\.\d+
+});
+}
+
+# fping -D --timestamp-format
+{
+my $cmd = Test::Command->new(cmd => "fping -D --timestamp-format -c 2 -p 100 127.0.0.1");
+$cmd->exit_is_num(1);
+$cmd->stdout_is_eq("");
+$cmd->stderr_like(qr{Usage:});
+}
+
+# fping -D --timestamp-format="%Y-%m-%d %H:%M:%S"
+{
+my $cmd = Test::Command->new(cmd => "fping -D --timestamp-format=\"%Y-%m-%d %H:%M:%S\" -c 2 -p 100 127.0.0.1");
+$cmd->exit_is_num(1);
+$cmd->stdout_is_eq("");
+$cmd->stderr_like(qr{Usage:});
+}
+
+# fping -e
+{
+my $cmd = Test::Command->new(cmd => "fping -e 127.0.0.1");
+$cmd->exit_is_num(0);
+$cmd->stdout_like(qr{127\.0\.0\.1 is alive \(\d\.\d+ ms\)
+});
+$cmd->stderr_is_eq("");
+}
+
+# fping -e -a
+{
+my $cmd = Test::Command->new(cmd => "fping -e -a 127.0.0.1");
+$cmd->exit_is_num(0);
+$cmd->stdout_like(qr{127\.0\.0\.1 \(\d\.\d+ ms\)
+});
+$cmd->stderr_is_eq("");
+}
--- /dev/null
+#!/usr/bin/perl -w
+
+use Test::Command tests => 75;
+use Test::More;
+use File::Temp;
+
+# -f file read list of targets from a file ( - means stdin) (only if no -g specified)
+# -g generate target list (only if no -f specified)
+# (specify the start and end IP in the target list, or supply a IP netmask)
+# (ex. ../src/fping -g 192.168.1.0 192.168.1.255 or ../src/fping -g 192.168.1.0/24)
+# -H n Set the IP TTL value (Time To Live hops)
+
+my $tmpfile = File::Temp->new();
+print $tmpfile "127.0.0.1\n127.0.0.2\n";
+close($tmpfile);
+
+my $tmpfile2 = File::Temp->new();
+print $tmpfile2 "# comment\n127.0.0.1\n\n127.0.0.2\n";
+close($tmpfile2);
+
+# fping without option (-> equivalent to 'fping -f -')
+{
+my $cmd = Test::Command->new(cmd => "cat ".$tmpfile->filename." | fping");
+$cmd->exit_is_num(0);
+$cmd->stdout_is_eq("127.0.0.1 is alive\n127.0.0.2 is alive\n");
+$cmd->stderr_is_eq("");
+}
+
+# fping -f -
+{
+my $cmd = Test::Command->new(cmd => "cat ".$tmpfile->filename." | fping -f -");
+$cmd->exit_is_num(0);
+$cmd->stdout_is_eq("127.0.0.1 is alive\n127.0.0.2 is alive\n");
+$cmd->stderr_is_eq("");
+}
+
+# fping -f file
+{
+my $cmd = Test::Command->new(cmd => "fping -f ".$tmpfile->filename);
+$cmd->exit_is_num(0);
+$cmd->stdout_is_eq("127.0.0.1 is alive\n127.0.0.2 is alive\n");
+$cmd->stderr_is_eq("");
+}
+
+# fping -f file (with comment and empty line)
+{
+my $cmd = Test::Command->new(cmd => "fping -f ".$tmpfile2->filename);
+$cmd->exit_is_num(0);
+$cmd->stdout_is_eq("127.0.0.1 is alive\n127.0.0.2 is alive\n");
+$cmd->stderr_is_eq("");
+}
+
+# fping -f non-existing-file (error)
+{
+my $cmd = Test::Command->new(cmd => "fping -f file-does-not-exist");
+$cmd->exit_is_num(4);
+$cmd->stdout_is_eq("");
+$cmd->stderr_like(qr{: fopen :});
+}
+
+# fping -g (error: no argument)
+{
+my $cmd = Test::Command->new(cmd => "fping -g");
+$cmd->exit_is_num(1);
+$cmd->stdout_is_eq("");
+$cmd->stderr_like(qr{^Usage: fping \[options\] \[targets\.\.\.\]});
+}
+
+# fping -g (error: single argument, but not in cidr format)
+{
+my $cmd = Test::Command->new(cmd => "fping -g 127.0.0.1");
+$cmd->exit_is_num(1);
+$cmd->stdout_is_eq("");
+$cmd->stderr_like(qr{^Usage: fping \[options\] \[targets\.\.\.\]});
+}
+
+# fping -g (error: CIDR network is not an IP address)
+{
+my $cmd = Test::Command->new(cmd => "fping -g xxx/32");
+$cmd->exit_is_num(1);
+$cmd->stdout_is_eq("");
+$cmd->stderr_like(qr{can't parse address xxx});
+}
+
+# fping -g (error: start of range is not an IP address)
+{
+my $cmd = Test::Command->new(cmd => "fping -g xxx 127.0.0.1");
+$cmd->exit_is_num(1);
+$cmd->stdout_is_eq("");
+$cmd->stderr_like(qr{can't parse address xxx});
+}
+
+# fping -g (error: end of range is not an IP address)
+{
+my $cmd = Test::Command->new(cmd => "fping -g 127.0.0.1 yyy");
+$cmd->exit_is_num(1);
+$cmd->stdout_is_eq("");
+$cmd->stderr_like(qr{can't parse address yyy});
+}
+
+# fping -g (error: too many arguments)
+{
+my $cmd = Test::Command->new(cmd => "fping -g 127.0.0.1 127.0.0.2 127.0.0.3");
+$cmd->exit_is_num(1);
+$cmd->stdout_is_eq("");
+$cmd->stderr_like(qr{^Usage: fping \[options\] \[targets\.\.\.\]});
+}
+
+# fping -g (range)
+{
+my $cmd = Test::Command->new(cmd => "fping -g 127.0.0.1 127.0.0.5");
+$cmd->exit_is_num(0);
+$cmd->stdout_is_eq("127.0.0.1 is alive\n127.0.0.2 is alive\n127.0.0.3 is alive\n127.0.0.4 is alive\n127.0.0.5 is alive\n");
+$cmd->stderr_is_eq("");
+}
+
+# fping -g (empty range)
+{
+my $cmd = Test::Command->new(cmd => "fping -g 127.0.0.2 127.0.0.1");
+$cmd->exit_is_num(1);
+$cmd->stdout_is_eq("");
+$cmd->stderr_is_eq("");
+}
+
+# fping -g (too large range)
+{
+my $cmd = Test::Command->new(cmd => "fping -g 127.0.0.1 127.255.255.254");
+$cmd->exit_is_num(1);
+$cmd->stdout_is_eq("");
+$cmd->stderr_is_eq("fping: -g parameter generates too many addresses\n");
+}
+
+# fping -g (cidr)
+{
+my $cmd = Test::Command->new(cmd => "fping -g 127.0.0.1/30");
+$cmd->exit_is_num(0);
+$cmd->stdout_is_eq("127.0.0.1 is alive\n127.0.0.2 is alive\n");
+$cmd->stderr_is_eq("");
+}
+
+# fping -g (cidr - long prefixes: point-to-point)
+{
+my $cmd = Test::Command->new(cmd => "fping -g 127.0.0.2/31");
+$cmd->exit_is_num(0);
+$cmd->stdout_is_eq("127.0.0.2 is alive\n127.0.0.3 is alive\n");
+$cmd->stderr_is_eq("");
+}
+
+# fping -g (cidr - long prefixes: host)
+{
+my $cmd = Test::Command->new(cmd => "fping -g 127.0.0.2/32");
+$cmd->exit_is_num(0);
+$cmd->stdout_is_eq("127.0.0.2 is alive\n");
+$cmd->stderr_is_eq("");
+}
+
+# fping -g (cidr - too long prefixes)
+{
+my $cmd = Test::Command->new(cmd => "fping -g 127.0.0.2/33");
+$cmd->exit_is_num(1);
+$cmd->stdout_is_eq("");
+$cmd->stderr_is_eq("fping: netmask must be between 1 and 32 (is: 33)\n");
+}
+
+# fping -g (cidr - too short prefixes)
+{
+my $cmd = Test::Command->new(cmd => "fping -g 127.0.0.2/0");
+$cmd->exit_is_num(1);
+$cmd->stdout_is_eq("");
+$cmd->stderr_is_eq("fping: netmask must be between 1 and 32 (is: 0)\n");
+}
+
+# fping -g (cidr - too many addresses)
+{
+my $cmd = Test::Command->new(cmd => "fping -g 127.0.0.0/8");
+$cmd->exit_is_num(1);
+$cmd->stdout_is_eq("");
+$cmd->stderr_is_eq("fping: -g parameter generates too many addresses\n");
+}
+
+# fping -g (range - no IPv6 generator)
+SKIP: {
+ if($ENV{SKIP_IPV6}) {
+ skip 'Skip IPv6 tests', 3;
+ }
+ my $cmd = Test::Command->new(cmd => "fping -6 -g ::1 ::1");
+ $cmd->exit_is_num(1);
+ $cmd->stdout_is_eq("");
+ $cmd->stderr_is_eq("fping: -g works only with IPv4 addresses\n");
+}
+
+# fping -g (range - no IPv6 generator - start address IPv6)
+SKIP: {
+ if($ENV{SKIP_IPV6}) {
+ skip 'Skip IPv6 tests', 3;
+ }
+ my $cmd = Test::Command->new(cmd => "fping -6 -g ::1 127.0.0.1");
+ $cmd->exit_is_num(1);
+ $cmd->stdout_is_eq("");
+ $cmd->stderr_is_eq("fping: -g works only with IPv4 addresses\n");
+}
+
+# fping -g (range - no IPv6 generator - end address IPv6)
+SKIP: {
+ if($ENV{SKIP_IPV6}) {
+ skip 'Skip IPv6 tests', 3;
+ }
+ my $cmd = Test::Command->new(cmd => "fping -6 -g 127.0.0.1 ::1");
+ $cmd->exit_is_num(1);
+ $cmd->stdout_is_eq("");
+ $cmd->stderr_is_eq("fping: -g works only with IPv4 addresses\n");
+}
+
+# fping -g (CIDR - no IPv6 generator)
+SKIP: {
+ if($ENV{SKIP_IPV6}) {
+ skip 'Skip IPv6 tests', 3;
+ }
+ my $cmd = Test::Command->new(cmd => "fping -6 -g ::1/128");
+ $cmd->exit_is_num(1);
+ $cmd->stdout_is_eq("");
+ $cmd->stderr_is_eq("fping: -g works only with IPv4 addresses\n");
+}
+
+# fping -H
+{
+my $cmd = Test::Command->new(cmd => "fping -H 1 127.0.0.1");
+$cmd->exit_is_num(0);
+$cmd->stdout_is_eq("127.0.0.1 is alive\n");
+$cmd->stderr_is_eq("");
+}
--- /dev/null
+#!/usr/bin/perl -w
+
+use Test::Command tests => 16;
+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
+
+# fping -i n
+{
+my $cmd = Test::Command->new(cmd => "fping -i 100 127.0.0.1 127.0.0.2");
+$cmd->exit_is_num(0);
+$cmd->stdout_is_eq("127.0.0.1 is alive\n127.0.0.2 is alive\n");
+$cmd->stderr_is_eq("");
+}
+
+# fping -l
+{
+my $cmd = Test::Command->new(cmd => '(sleep 2; pkill fping)& fping -p 900 -l 127.0.0.1');
+$cmd->stdout_like(qr{127\.0\.0\.1 : \[0\], 64 bytes, \d\.\d+ ms \(\d\.\d+ avg, 0% loss\)
+127\.0\.0\.1 : \[1\], 64 bytes, \d\.\d+ ms \(\d\.\d+ avg, 0% loss\)
+});
+}
+
+# fping -l --print-tos --print-ttl
+{
+my $cmd = Test::Command->new(cmd => '(sleep 2; pkill fping)& fping -p 900 --print-ttl --print-tos -l 127.0.0.1');
+$cmd->stdout_like(qr{127\.0\.0\.1 : \[0\], 64 bytes, \d\.\d+ ms \(\d\.\d+ avg, 0% loss\) \(TOS \d+\) \(TTL \d+\)
+127\.0\.0\.1 : \[1\], 64 bytes, \d\.\d+ ms \(\d\.\d+ avg, 0% loss\) \(TOS \d+\) \(TTL \d+\)
+});
+}
+
+# fping -k
+SKIP: {
+if($^O ne 'linux') {
+ skip '-k option is only supported on Linux', 3;
+}
+my $cmd = Test::Command->new(cmd => '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');
+$cmd->stdout_like(qr{127\.0\.0\.1 : \[0\], 64 bytes, \d\.\d+ ms \(\d\.\d+ avg, 0% loss\)
+127\.0\.0\.1 : \[1\], 64 bytes, \d\.\d+ ms \(\d\.\d+ avg, 0% loss\)
+127\.0\.0\.1 : \[2\], 64 bytes, \d\.\d+ ms \(\d\.\d+ avg, 0% loss\)
+127\.0\.0\.1 : \[3\], 64 bytes, \d\.\d+ ms \(\d\.\d+ avg, 0% loss\)
+127\.0\.0\.1 : \[4\], 64 bytes, \d\.\d+ ms \(\d\.\d+ avg, 0% loss\)
+});
+$cmd->stderr_like(qr{\[\d+:\d+:\d+\]
+127\.0\.0\.1 : xmt/rcv/%loss = \d+/\d+/\d+%, min/avg/max = \d+\.\d+/\d+\.\d+/\d+\.\d+
+});
+}
+
+# fping -l -Q
+SKIP: {
+if($^O eq 'darwin') {
+ skip 'On macOS, this test is unreliable', 2;
+}
+my $cmd = Test::Command->new(cmd => '(sleep 2; pkill fping)& fping -p 850 -l -Q 1 127.0.0.1');
+$cmd->stdout_is_eq("");
+$cmd->stderr_like(qr{\[\d\d:\d\d:\d\d\]
+127\.0\.0\.1 : xmt/rcv/%loss = \d/\d/\d%, min/avg/max = \d\.\d+/\d\.\d+/\d\.\d+
+\[\d\d:\d\d:\d\d\]
+127\.0\.0\.1 : xmt/rcv/%loss = \d/\d/\d%, min/avg/max = \d\.\d+/\d\.\d+/\d\.\d+
+});
+}
+
+# fping -l -t
+{
+my $cmd = Test::Command->new(cmd => '(sleep 2; pkill fping)& fping -p 900 -t 1500 -l 127.0.0.1');
+$cmd->stdout_like(qr{127\.0\.0\.1 : \[0\], 64 bytes, \d\.\d+ ms \(\d\.\d+ avg, 0% loss\)
+127\.0\.0\.1 : \[1\], 64 bytes, \d\.\d+ ms \(\d\.\d+ avg, 0% loss\)
+});
+}
+
+# fping -M
+SKIP: {
+ if($^O eq 'darwin') {
+ skip '-M option not supported on macOS', 3;
+ }
+ my $cmd = Test::Command->new(cmd => "fping -M 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 -m -> test-14-internet-hosts
--- /dev/null
+#!/usr/bin/perl -w
+
+use Test::Command tests => 48;
+use Test::More;
+
+# -n show targets by name (-d is equivalent)
+# -O n set the type of service (tos) flag on the ICMP packets
+# -p n interval between ping packets to one target (in millisec)
+# (in looping and counting modes, default 1000)
+# -q quiet (don't show per-target/per-ping results)
+# -Q n same as -q, but show summary every n seconds
+
+# fping -n -> test-14-internet-hosts
+
+# fping -d -n
+{
+my $cmd = Test::Command->new(cmd => "fping -d -n 127.0.0.1");
+$cmd->exit_is_num(1);
+$cmd->stdout_is_eq("");
+$cmd->stderr_is_eq("fping: use either one of -d or -n\n");
+}
+
+# fping -n -d
+{
+my $cmd = Test::Command->new(cmd => "fping -n -d 127.0.0.1");
+$cmd->exit_is_num(1);
+$cmd->stdout_is_eq("");
+$cmd->stderr_is_eq("fping: use either one of -d or -n\n");
+}
+
+# fping -o
+{
+my $cmd = Test::Command->new(cmd => "fping -t100 -p 100 -o -c 5 8.8.8.7");
+$cmd->exit_is_num(1);
+$cmd->stdout_is_eq("8.8.8.7 : [0], timed out (NaN avg, 100% loss)
+8.8.8.7 : [1], timed out (NaN avg, 100% loss)
+8.8.8.7 : [2], timed out (NaN avg, 100% loss)
+8.8.8.7 : [3], timed out (NaN avg, 100% loss)
+8.8.8.7 : [4], timed out (NaN avg, 100% loss)
+");
+$cmd->stderr_like(qr{^\s*8\.8\.8\.7 : xmt/rcv/%loss = 5/0/100%, outage\(ms\) = 50\d\s*$});
+}
+
+# fping -O
+{
+my $cmd = Test::Command->new(cmd => "fping -O 2 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 -O --print-tos
+{
+my $cmd = Test::Command->new(cmd => "fping -O 2 --print-tos 127.0.0.1");
+$cmd->exit_is_num(0);
+$cmd->stdout_like(qr{127\.0\.0\.1 is alive \(TOS \d+\)
+});
+$cmd->stderr_is_eq("");
+}
+
+# fping -a -O --print-tos
+{
+my $cmd = Test::Command->new(cmd => "fping -a -O 32 --print-tos 127.0.0.1");
+$cmd->exit_is_num(0);
+$cmd->stdout_like(qr{127\.0\.0\.1 \(TOS \d+\)
+});
+$cmd->stderr_is_eq("");
+}
+
+# fping --print-tos
+{
+my $cmd = Test::Command->new(cmd => "fping --print-tos 127.0.0.1");
+$cmd->exit_is_num(0);
+$cmd->stdout_like(qr{127\.0\.0\.1 is alive \(TOS 0\)
+});
+$cmd->stderr_is_eq("");
+}
+
+# fping --print-tos with IPv6
+SKIP: {
+ if($ENV{SKIP_IPV6}) {
+ skip 'Skip IPv6 tests', 3;
+ }
+ my $cmd = Test::Command->new(cmd => "fping --print-tos ::1");
+ $cmd->exit_is_num(0);
+ $cmd->stdout_like(qr{::1 is alive \(TOS unknown\)\n});
+ $cmd->stderr_is_eq("");
+}
+
+# fping -q
+{
+my $cmd = Test::Command->new(cmd => "fping -q -p 100 -c 3 127.0.0.1");
+$cmd->exit_is_num(0);
+$cmd->stdout_is_eq("");
+$cmd->stderr_like(qr{127\.0\.0\.1 : xmt/rcv/%loss = 3/3/0%, min/avg/max = \d\.\d+/\d\.\d+/\d\.\d+
+});
+}
+
+# fping -Q
+{
+my $cmd = Test::Command->new(cmd => "fping -Q 1 -p 400 -c 4 127.0.0.1");
+$cmd->exit_is_num(0);
+$cmd->stdout_is_eq("");
+$cmd->stderr_like(qr{\[\d+:\d+:\d+\]
+127\.0\.0\.1 : xmt/rcv/%loss = 3/3/0%, min/avg/max = \d\.\d+/\d\.\d+/\d\.\d+
+127\.0\.0\.1 : xmt/rcv/%loss = 4/4/0%, min/avg/max = \d\.\d+/\d\.\d+/\d\.\d+
+});
+}
+
+# fping -Q (longer test to show two time stamps and reset statistics)
+{
+my $cmd = Test::Command->new(cmd => "fping -Q 1 -p 550 -c 5 127.0.0.1");
+$cmd->exit_is_num(0);
+$cmd->stdout_is_eq("");
+$cmd->stderr_like(qr{\[\d+:\d+:\d+\]
+127\.0\.0\.1 : xmt/rcv/%loss = 2/2/0%, min/avg/max = \d\.\d+/\d\.\d+/\d\.\d+
+\[\d+:\d+:\d+\]
+127\.0\.0\.1 : xmt/rcv/%loss = 2/2/0%, min/avg/max = \d\.\d+/\d\.\d+/\d\.\d+
+127\.0\.0\.1 : xmt/rcv/%loss = 5/5/0%, min/avg/max = \d\.\d+/\d\.\d+/\d\.\d+
+});
+}
+
+# fping -Q n ignores non-number characters after the number, except for keywords
+{
+my $cmd = Test::Command->new(cmd => "fping -Q 1whatever -p 550 -c 5 127.0.0.1");
+$cmd->exit_is_num(0);
+$cmd->stdout_is_eq("");
+$cmd->stderr_like(qr{\[\d+:\d+:\d+\]
+127\.0\.0\.1 : xmt/rcv/%loss = 2/2/0%, min/avg/max = \d\.\d+/\d\.\d+/\d\.\d+
+\[\d+:\d+:\d+\]
+127\.0\.0\.1 : xmt/rcv/%loss = 2/2/0%, min/avg/max = \d\.\d+/\d\.\d+/\d\.\d+
+127\.0\.0\.1 : xmt/rcv/%loss = 5/5/0%, min/avg/max = \d\.\d+/\d\.\d+/\d\.\d+
+});
+}
+
+# fping -Q n ignores unknown keywords
+{
+my $cmd = Test::Command->new(cmd => "fping -Q 1,not_a_keyword -p 550 -c 5 127.0.0.1");
+$cmd->exit_is_num(0);
+$cmd->stdout_is_eq("");
+$cmd->stderr_like(qr{\[\d+:\d+:\d+\]
+127\.0\.0\.1 : xmt/rcv/%loss = 2/2/0%, min/avg/max = \d\.\d+/\d\.\d+/\d\.\d+
+\[\d+:\d+:\d+\]
+127\.0\.0\.1 : xmt/rcv/%loss = 2/2/0%, min/avg/max = \d\.\d+/\d\.\d+/\d\.\d+
+127\.0\.0\.1 : xmt/rcv/%loss = 5/5/0%, min/avg/max = \d\.\d+/\d\.\d+/\d\.\d+
+});
+}
+
+# fping -Q n,cumulative
+{
+my $cmd = Test::Command->new(cmd => "fping -Q 1,cumulative -p 550 -c 5 127.0.0.1");
+$cmd->exit_is_num(0);
+$cmd->stdout_is_eq("");
+$cmd->stderr_like(qr{\[\d+:\d+:\d+\]
+127\.0\.0\.1 : xmt/rcv/%loss = 2/2/0%, min/avg/max = \d\.\d+/\d\.\d+/\d\.\d+
+\[\d+:\d+:\d+\]
+127\.0\.0\.1 : xmt/rcv/%loss = 4/4/0%, min/avg/max = \d\.\d+/\d\.\d+/\d\.\d+
+127\.0\.0\.1 : xmt/rcv/%loss = 5/5/0%, min/avg/max = \d\.\d+/\d\.\d+/\d\.\d+
+});
+}
+
+# fping -Q -o
+{
+my $cmd = Test::Command->new(cmd => "fping -c4 -Q1 -p550 -o 8.8.8.7");
+$cmd->exit_is_num(1);
+$cmd->stdout_is_eq("");
+$cmd->stderr_like(qr{\[\d+:\d+:\d+\]
+8\.8\.8\.7 : xmt/rcv/%loss = 1/0/100%, outage\(ms\) = 55\d
+\[\d+:\d+:\d+\]
+8\.8\.8\.7 : xmt/rcv/%loss = 2/0/100%, outage\(ms\) = 110\d
+8\.8\.8\.7 : xmt/rcv/%loss = 4/0/100%, outage\(ms\) = 220\d
+});
+}
+
+# fping -Q n,cumulative -o
+{
+my $cmd = Test::Command->new(cmd => "fping -c4 -Q1,cumulative -p550 -o 8.8.8.7");
+$cmd->exit_is_num(1);
+$cmd->stdout_is_eq("");
+$cmd->stderr_like(qr{\[\d+:\d+:\d+\]
+8\.8\.8\.7 : xmt/rcv/%loss = 1/0/100%, outage\(ms\) = 55\d
+\[\d+:\d+:\d+\]
+8\.8\.8\.7 : xmt/rcv/%loss = 3/0/100%, outage\(ms\) = 165\d
+8\.8\.8\.7 : xmt/rcv/%loss = 4/0/100%, outage\(ms\) = 220\d
+});
+}
+
--- /dev/null
+#!/usr/bin/perl -w
+
+use Test::Command tests => 30;
+use Test::More;
+
+# -R random bytes
+# -r n number of retries (default 3)
+# -s print final stats
+# -S addr set source address
+# -t n individual target initial timeout (in millisec) (default 500)
+# -T n ignored (for compatibility with fping 2.4)
+
+# fping -R
+{
+my $cmd = Test::Command->new(cmd => "fping -q -R -c3 -p100 127.0.0.1");
+$cmd->exit_is_num(0);
+$cmd->stdout_is_eq("");
+$cmd->stderr_like(qr{127\.0\.0\.1 : xmt/rcv/%loss = 3/3/0%.*});
+}
+
+SKIP: {
+ if($ENV{SKIP_IPV6}) {
+ skip 'Skip IPv6 tests', 3;
+ }
+ my $cmd = Test::Command->new(cmd => "fping -q -R -c3 -p100 ::1");
+ $cmd->exit_is_num(0);
+ $cmd->stdout_is_eq("");
+ $cmd->stderr_like(qr{::1 : xmt/rcv/%loss = 3/3/0%.*});
+}
+
+# fping -r tested in test-4-options-a-b.pl
+
+# fping -s
+{
+my $cmd = Test::Command->new(cmd => "fping -s 127.0.0.1");
+$cmd->exit_is_num(0);
+$cmd->stdout_is_eq("127.0.0.1 is alive\n");
+$cmd->stderr_like(qr{\s*
+\s*1 targets
+\s*1 alive
+\s*0 unreachable
+\s*0 unknown addresses
+\s*
+\s*0 timeouts \(waiting for response\)
+\s*1 ICMP Echos sent
+\s*1 ICMP Echo Replies received
+\s*0 other ICMP received
+
+\s*\d\.\d+ ms \(min round trip time\)
+\s*\d\.\d+ ms \(avg round trip time\)
+\s*\d\.\d+ ms \(max round trip time\)
+\s*\d\.\d+ sec \(elapsed real time\)
+});
+}
+
+# fping -s (no host reachable)
+{
+my $cmd = Test::Command->new(cmd => "fping -r0 -t100 -s 8.8.0.0");
+$cmd->exit_is_num(1);
+$cmd->stdout_is_eq("8.8.0.0 is unreachable\n");
+$cmd->stderr_like(qr{\s*
+\s*1 targets
+\s*0 alive
+\s*1 unreachable
+\s*0 unknown addresses
+\s*
+\s*1 timeouts \(waiting for response\)
+\s*1 ICMP Echos sent
+\s*0 ICMP Echo Replies received
+\s*0 other ICMP received
+
+\s*\d\.\d+ ms \(min round trip time\)
+\s*\d\.\d+ ms \(avg round trip time\)
+\s*\d\.\d+ ms \(max round trip time\)
+\s*\d\.\d+ sec \(elapsed real time\)
+});
+}
+
+# fping -s (both valid and invalid host name)
+{
+my $cmd = Test::Command->new(cmd => "fping -s 127.0.0.1 host.name.invalid");
+$cmd->exit_is_num(2);
+$cmd->stdout_is_eq("127.0.0.1 is alive\n");
+$cmd->stderr_like(qr{host\.name\.invalid: .+
+\s*
+\s*1 targets
+\s*1 alive
+\s*0 unreachable
+\s*1 unknown addresses
+\s*
+\s*0 timeouts \(waiting for response\)
+\s*1 ICMP Echos sent
+\s*1 ICMP Echo Replies received
+\s*0 other ICMP received
+
+\s*\d\.\d+ ms \(min round trip time\)
+\s*\d\.\d+ ms \(avg round trip time\)
+\s*\d\.\d+ ms \(max round trip time\)
+\s*\d\.\d+ sec \(elapsed real time\)
+});
+}
+
+# fping -S
+{
+my $cmd = Test::Command->new(cmd => "fping -S 127.0.0.1 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 -S (wrong source address)
+{
+my $cmd = Test::Command->new(cmd => "fping -S 192.0.2.47 127.0.0.1");
+$cmd->exit_is_num(4);
+$cmd->stdout_is_eq("");
+$cmd->stderr_like(qr{fping: cannot bind source address : .+\n});
+}
+
+# fping -S
+SKIP: {
+ if($ENV{SKIP_IPV6}) {
+ skip 'Skip IPv6 tests', 3;
+ }
+ my $cmd = Test::Command->new(cmd => "fping -S ::1 ::1");
+ $cmd->exit_is_num(0);
+ $cmd->stdout_is_eq("::1 is alive\n");
+ $cmd->stderr_is_eq("");
+}
+
+# fping -S (wrong IPv6 source address)
+SKIP: {
+ if($ENV{SKIP_IPV6}) {
+ skip 'Skip IPv6 tests', 3;
+ }
+ my $cmd = Test::Command->new(cmd => "fping -S 2001:db8::1 ::1");
+ $cmd->exit_is_num(4);
+ $cmd->stdout_is_eq("");
+ $cmd->stderr_like(qr{fping: cannot bind source address : .+\n});
+}
+
+# fping -S
+{
+my $cmd = Test::Command->new(cmd => "fping -S bla");
+$cmd->exit_is_num(1);
+$cmd->stdout_is_eq("");
+$cmd->stderr_is_eq("fping: can't parse source address: bla\n");
+}
+
+# (note: fping -t also tested in test-4-options-a-b.pl)
--- /dev/null
+#!/usr/bin/perl -w
+
+use Test::Command tests => 15;
+
+# -u show targets that are unreachable
+# -v show version
+# -x shows if >=N hosts are reachable or not
+# -X exits true immediately when N hosts are found
+
+# fping -u
+{
+my $cmd = Test::Command->new(cmd => "fping -r0 -u 8.8.0.0 127.0.0.1");
+$cmd->exit_is_num(1);
+$cmd->stdout_is_eq("8.8.0.0\n");
+$cmd->stderr_is_eq("");
+}
+
+# fping -v
+{
+my $cmd = Test::Command->new(cmd => "fping -v");
+$cmd->exit_is_num(0);
+$cmd->stdout_like(qr{ping: Version [45]\.\d+(-rc\d+)?});
+$cmd->stderr_is_eq("");
+}
+
+# fping -x
+{
+my $cmd = Test::Command->new(cmd => "fping -x 1 8.8.0.0 127.0.0.1");
+$cmd->exit_is_num(0);
+$cmd->stdout_is_eq("Enough hosts reachable (required: 1, reachable: 1)\n");
+$cmd->stderr_is_eq("");
+}
+
+# fping -x
+{
+my $cmd = Test::Command->new(cmd => "fping -x 2 8.8.0.0 127.0.0.1");
+$cmd->exit_is_num(1);
+$cmd->stdout_is_eq("Not enough hosts reachable (required: 2, reachable: 1)\n");
+$cmd->stderr_is_eq("");
+}
+
+# fping -X
+{
+my $cmd = Test::Command->new(cmd => "fping -X 1 --generate 127.0.0.0/29");
+$cmd->exit_is_num(0);
+$cmd->stdout_is_eq("Enough hosts reachable (required: 1, reachable: 1)\n");
+$cmd->stderr_is_eq("");
+}
--- /dev/null
+#!/usr/bin/perl -w
+
+use English;
+use File::Copy;
+use File::Temp qw/ tempdir /;
+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 = get_ping_gid_range();
+
+# Make a copy of the binary so that we get rid of setuid bit
+my $tmpdir = tempdir(CLEANUP => 1);
+my $fping_bin = `which fping`; chomp $fping_bin;
+my $fping_copy = "$tmpdir/fping.copy";
+copy($fping_bin, $fping_copy);
+chmod 0755, $fping_copy;
+
+# Determine what test to run, based on whether unprivileged
+# pings are allowed.
+if(scalar grep { $_ >= $allowed[0] && $_ <= $allowed[1] } @gids) {
+ diag('test unprivileged mode');
+ test_unprivileged_works();
+}
+else {
+ test_privileged_fails();
+}
+
+sub test_unprivileged_works {
+ plan tests => 15;
+
+ {
+ my $cmd = Test::Command->new(cmd => "$fping_copy 127.0.0.1");
+ $cmd->exit_is_num(0);
+ $cmd->stdout_is_eq("127.0.0.1 is alive\n");
+ $cmd->stderr_is_eq("");
+ }
+ {
+ my $cmd = Test::Command->new(cmd => "$fping_copy --print-tos 127.0.0.1");
+ $cmd->exit_is_num(0);
+ $cmd->stdout_is_eq("127.0.0.1 is alive (TOS unknown)\n");
+ $cmd->stderr_is_eq("");
+ }
+ {
+ my $cmd = Test::Command->new(cmd => "$fping_copy --print-ttl 127.0.0.1");
+ $cmd->exit_is_num(0);
+ $cmd->stdout_is_eq("127.0.0.1 is alive (TTL unknown)\n");
+ $cmd->stderr_is_eq("");
+ }
+ SKIP: {
+ if($^O ne 'linux') {
+ skip '-k option is only supported on Linux', 3;
+ }
+ my $cmd = Test::Command->new(cmd => "$fping_copy -4 -k 256 127.0.0.1");
+ $cmd->exit_is_num(0);
+ $cmd->stdout_is_eq("127.0.0.1 is alive\n");
+ $cmd->stderr_like(qr{fwmark ipv4: .+\n});
+ }
+ SKIP: {
+ if($^O ne 'linux') {
+ skip '-k option is only supported on Linux', 3;
+ }
+ if($ENV{SKIP_IPV6}) {
+ skip 'Skip IPv6 tests', 3;
+ }
+ my $cmd = Test::Command->new(cmd => "$fping_copy -6 -k 256 ::1");
+ $cmd->exit_is_num(0);
+ $cmd->stdout_is_eq("::1 is alive\n");
+ $cmd->stderr_like(qr{fwmark ipv6: .+\n});
+ }
+}
+
+sub test_privileged_fails {
+ plan tests => 3;
+
+ {
+ my $cmd = Test::Command->new(cmd => "$fping_copy 127.0.0.1");
+ $cmd->exit_is_num(4);
+ $cmd->stdout_is_eq("");
+ $cmd->stderr_like(qr{: can't create socket \(must run as root\?\)});
+ }
+}
--- /dev/null
+#!/usr/bin/perl -w
+
+use Test::Command tests => 84;
+use Test::More;
+
+# some options require a numeric argument
+for my $arg (qw(b B c C H i O p Q r t x X)) {
+ for my $test_input (qw(xxx '')) {
+ my $cmd = Test::Command->new(cmd => "fping -$arg $test_input");
+ $cmd->exit_is_num(1);
+ $cmd->stdout_is_eq("");
+ $cmd->stderr_like(qr{Usage:});
+ }
+}
+
+# fping -k, only supported on Linux, requires a number
+SKIP: {
+ if($^O ne 'linux') {
+ skip '-k option is only supported on Linux', 6;
+ }
+ for my $test_input (qw(xxx '')) {
+ my $cmd = Test::Command->new(cmd => "fping -k $test_input 127.0.0.1");
+ $cmd->exit_is_num(1);
+ $cmd->stdout_is_eq("");
+ $cmd->stderr_like(qr{Usage:});
+ }
+}
--- /dev/null
+#!/usr/bin/perl -w
+
+use Test::Command tests => 3;
+
+# fping
+{
+my $cmd = Test::Command->new(cmd => "fping nosuchname.example.com");
+$cmd->exit_is_num(2);
+$cmd->stdout_is_eq("");
+$cmd->stderr_like(qr{^nosuchname\.example\.com: .*not (known|found)});
+}
--- /dev/null
+#!/usr/bin/perl -w
+
+use Test::Command;
+use Test::More;
+
+# evaluate if we have internet
+if(!gethostbyname("www.google.com")) {
+ plan skip_all => 'Can\'t resolve www.google.com -> no internet?';
+ exit 0;
+}
+
+plan tests => 30;
+
+my $re_num = qr{\d+(?:\.\d+)?};
+
+# fping
+{
+my $cmd = Test::Command->new(cmd => "fping -q -i 10 -p 20 -c 3 -t200 8.8.8.8 www.france-telecom.fr www.google.com");
+$cmd->exit_is_num(0);
+$cmd->stdout_is_eq("");
+$cmd->stderr_like(qr{8\.8\.8\.8\s*: xmt/rcv/%loss = [123]/[123]/\d+%, min/avg/max = $re_num/$re_num/$re_num
+www\.france-telecom\.fr\s*: xmt/rcv/%loss = [123]/[123]/\d+%, min/avg/max = $re_num/$re_num/$re_num
+www\.google\.com\s*: xmt/rcv/%loss = [123]/[123]/\d+%, min/avg/max = $re_num/$re_num/$re_num
+});
+}
+
+# fping -A -n
+{
+my $cmd = Test::Command->new(cmd => "fping -A -n 8.8.8.8");
+$cmd->exit_is_num(0);
+$cmd->stdout_is_eq("dns.google (8.8.8.8) is alive\n");
+$cmd->stderr_is_eq("");
+}
+
+# fping -4 -A -n
+{
+my $cmd = Test::Command->new(cmd => "fping -4 -A -n dns.google");
+$cmd->exit_is_num(0);
+$cmd->stdout_like(qr{^dns.google \(8\.8\.(4\.4|8\.8)\) is alive\n$});
+$cmd->stderr_is_eq("");
+}
+
+# fping -4 --addr --rdns
+{
+my $cmd = Test::Command->new(cmd => "fping -4 --addr --rdns www.apple.com");
+$cmd->exit_is_num(0);
+$cmd->stdout_like(qr{^\S+\.akamaitechnologies\.com \(\d+\.\d+\.\d+\.\d+\) is alive\n$});
+$cmd->stderr_is_eq("");
+}
+
+# fping -4 --addr --name
+{
+my $cmd = Test::Command->new(cmd => "fping -4 --addr --name www.google.com");
+$cmd->exit_is_num(0);
+$cmd->stdout_like(qr{^www\.google\.com \(\d+\.\d+\.\d+\.\d+\) is alive\n$});
+$cmd->stderr_is_eq("");
+}
+
+# fping -A -n (IPv6)
+SKIP: {
+ if($ENV{SKIP_IPV6}) {
+ skip 'Skip IPv6 tests', 3;
+ }
+ my $cmd = Test::Command->new(cmd => "fping -6 -n -A dns.google");
+ $cmd->exit_is_num(0);
+ $cmd->stdout_like(qr{^dns.google \(2001:4860:4860::88(44|88)\) is alive\n$});
+ $cmd->stderr_is_eq("");
+}
+
+# fping -m
+SKIP: {
+ if($ENV{SKIP_IPV6}) {
+ skip 'Skip IPv6 tests', 3;
+ }
+ my $cmd = Test::Command->new(cmd => "fping -A -m dns.google");
+ $cmd->exit_is_num(0);
+ $cmd->stdout_like(qr{^.* is alive\n.* is alive\n.* is alive\n.* is alive\n});
+ $cmd->stderr_is_eq("");
+}
+
+# fping -m -A
+{
+my $cmd = Test::Command->new(cmd => "fping -4 -A -m www.cloudflare.com");
+$cmd->exit_is_num(0);
+$cmd->stdout_like(qr{\d+\.\d+\.\d+\.\d+ is alive\n\d+\.\d+\.\d+\.\d+ is alive\n});
+$cmd->stderr_is_eq("");
+}
+
+# fping -n
+{
+my $cmd = Test::Command->new(cmd => "fping -n 8.8.8.8");
+$cmd->exit_is_num(0);
+$cmd->stdout_is_eq("dns.google is alive\n");
+$cmd->stderr_is_eq("");
+}
+
+# fping -M
+SKIP: {
+ if($^O eq 'darwin') {
+ skip '-M option not supported on macOS', 3;
+ }
+ my $cmd = Test::Command->new(cmd => "fping -r 0 -b 10000 -M 8.8.8.8");
+ $cmd->exit_is_num(1);
+ $cmd->stdout_is_eq("8.8.8.8 is unreachable\n");
+ $cmd->stderr_is_eq("8.8.8.8: error while sending ping: Message too long\n");
+}
--- /dev/null
+#!/usr/bin/perl -w
+
+use Test::Command;
+use Test::More;
+
+plan tests => 3;
+
+# fping
+{
+my $cmd = Test::Command->new(cmd => "fping -c 2 -Q 1 -N 127.0.0.1");
+$cmd->exit_is_num(0);
+$cmd->stdout_like(qr{CHART fping\.127_0_0_1_packets '' 'FPing Packets' packets '127.0.0.1' fping\.packets line 110020 1
+DIMENSION xmt sent absolute 1 1
+DIMENSION rcv received absolute 1 1
+BEGIN fping\.127_0_0_1_packets
+SET xmt = 1
+SET rcv = 1
+END
+CHART fping\.127_0_0_1_quality '' 'FPing Quality' percentage '127.0.0.1' fping\.quality area 110010 1
+DIMENSION returned '' absolute 1 1
+BEGIN fping\.127_0_0_1_quality
+SET returned = 100
+END
+CHART fping\.127_0_0_1_latency '' 'FPing Latency' ms '127.0.0.1' fping\.latency area 110000 1
+DIMENSION min minimum absolute 1 1000000
+DIMENSION max maximum absolute 1 1000000
+DIMENSION avg average absolute 1 1000000
+BEGIN fping\.127_0_0_1_latency
+SET min = \d+
+SET avg = \d+
+SET max = \d+
+END}
+);
+$cmd->stderr_like(qr{127.0.0.1 : xmt/rcv/%loss = 2/2/0%, min/avg/max = \d\.\d+/\d\.\d+/\d\.\d+});
+}
--- /dev/null
+#!/usr/bin/perl -w
+
+# regression testing for github issue #56
+#
+use Test::Command;
+use Test::More;
+
+if( $^O eq 'darwin' ) {
+ plan skip_all => 'Test disabled on MacOS';
+ exit 0;
+}
+
+plan tests => 3;
+
+my $cmd1 = Test::Command->new(cmd => "fping -t100 -p100 -C3 255.255.255.255");
+$cmd1->exit_is_num(1);
+$cmd1->stdout_is_eq("");
+$cmd1->stderr_is_eq("\n255.255.255.255 : - - -\n");
--- /dev/null
+#!/usr/bin/perl -w
+
+# regression testing for github issue #58
+
+use Test::Command tests => 3;
+
+my $cmd1 = Test::Command->new(cmd => "fping -a -g 2001:db8:120:4161::4/64");
+$cmd1->exit_is_num(1);
+$cmd1->stdout_is_eq("");
+$cmd1->stderr_is_eq("fping: -g works only with IPv4 addresses\n");
--- /dev/null
+#!/bin/bash
+
+# make sure that the .tar.gz file contains everything necessary
+# to build fping
+
+set -e
+set -x
+
+# skip on macos
+if [[ "$OSTYPE" == "darwin"* ]]; then
+ exit 0
+fi
+
+make dist
+VERSION=$(ls fping-*.tar.gz | sed -e 's/^fping-//' | sed -e 's/\.tar\.gz$//')
+if [ -z "$VERSION" ]; then
+ echo "tar.gz file not found." >&2
+ exit 1
+fi
+
+# unarchive
+TMPDIR=$(mktemp -d --tmpdir=ci)
+cd $TMPDIR
+tar xf ../../fping-$VERSION.tar.gz
+DIRNAME=$(ls)
+
+# build
+cd $DIRNAME
+./configure --enable-ipv4 --enable-ipv6 --prefix=/opt/fping
+make
+sudo make install
--- /dev/null
+dnl Process this file with autoconf to produce a configure script.
+
+dnl Minimum Autoconf version required.
+AC_PREREQ(2.59)
+
+AC_INIT([fping],[5.4])
+
+m4_ifdef([AC_AUTOCONF_VERSION],[AC_USE_SYSTEM_EXTENSIONS], [AC_GNU_SOURCE])
+
+# Detect Operatingsystem
+AC_CANONICAL_TARGET
+only_clock_realtime=no
+
+case "${target}" in
+ *darwin*)
+ only_clock_realtime=yes
+ ;;
+ *freebsd*)
+ only_clock_realtime=yes
+ ;;
+ *openbsd*)
+ only_clock_realtime=yes
+ ;;
+esac
+
+dnl --disable-ipv4
+AC_ARG_ENABLE([ipv4],
+ AS_HELP_STRING([--disable-ipv4], [Disable support for pinging IPv4 hosts]))
+AM_CONDITIONAL([IPV4], [test "x$enable_ipv4" != "xno"])
+AM_COND_IF([IPV4], [AC_DEFINE([IPV4], [1], [IPv4 enabled])])
+
+dnl --disable-ipv6
+AC_ARG_ENABLE([ipv6],
+ AS_HELP_STRING([--disable-ipv6], [Disable support for pinging IPv6 hosts]))
+AS_IF([test "x$enable_ipv6" != "xno"], [
+ dnl Test if IPv6 is supported
+ AC_CHECK_HEADERS([netinet/icmp6.h], [have_ipv6="yes"], [], [[
+ #include <netinet/in.h>
+ #include <sys/types.h>
+ ]])
+])
+dnl Can't disable both IPv4 and IPv6
+AS_IF([test "x$enable_ipv4" = "xno" -a "x$enable_ipv6" = "xno"], [
+ AC_MSG_ERROR([Need to enable IPv4 or IPv6. Can't disable both!)])
+])
+dnl IPv6 required, but not supported?
+AS_IF([test \( "x$enable_ipv6" = "xyes" -o "x$enable_ipv4" = "xno" \) -a "x$have_ipv6" != "xyes" ], [
+ AC_MSG_ERROR([IPv6 not supported on this platform (netinet/icmp6.h header not found)])
+])
+AM_CONDITIONAL([IPV6], [test "x$have_ipv6" = "xyes"])
+AM_COND_IF([IPV6], [AC_DEFINE([IPV6], [1], [IPv6 enabled])])
+
+AC_ARG_ENABLE([timestamp],
+ AS_HELP_STRING([--disable-timestamp], [Disable kernel-based packet timestaping (SO_TIMESTAMPNS)]))
+AS_IF([test "x$enable_timestamp" != "xno"], [
+ AC_CHECK_DECL([SO_TIMESTAMPNS], [AC_DEFINE(HAVE_SO_TIMESTAMPNS, [1], [SO_TIMESTAMPNS is defined])], [have_so_timestamp="no"], [#include <sys/types.h>
+#include <sys/socket.h>])
+])
+dnl Test if --enable-timestamp is explicitely enabled and make an error if this platform doesn't support it
+AS_IF([test "x$enable_timestamp" = "xyes" -a "x$have_so_timestamp" = "xno"], [
+ AC_MSG_ERROR([--enable-timestamp not supported on this platform])
+])
+AS_IF([test "x$only_clock_realtime" = "xyes"], [AC_DEFINE(ONLY_CLOCK_REALTIME, [1], [ONLY_CLOCK_REALTIME is defined])])
+
+AC_ARG_ENABLE([safe-limits],
+ AS_HELP_STRING([--enable-safe-limits], [Restrict timing parameters (-i, -p) within "safe" limits]))
+AS_IF([test "x$enable_safe_limits" = "xyes"], [
+ AC_DEFINE(FPING_SAFE_LIMITS, [1], [safe limits should be enforced])])
+
+AC_ARG_ENABLE([debug],
+ AS_HELP_STRING([--enable-debug], [enable debugging @<:@default=no@:>@]), [enable_debug=$enableval], [enable_debug=no])
+AS_IF([test "x$enable_debug" = "xyes"], [
+ AC_DEFINE([DEBUG], [1], [Define if debugging is enabled])])
+
+AM_INIT_AUTOMAKE([-Wall -Werror foreign])
+AM_MAINTAINER_MODE
+
+AC_CONFIG_HEADERS([config.h])
+
+dnl Checks for programs.
+
+AC_PROG_CC
+AM_PROG_CC_C_O
+m4_version_prereq([2.70],,[AC_PROG_CC_STDC])
+AC_PROG_CPP
+AC_PROG_INSTALL
+
+dnl Checks for libraries.
+
+AC_CHECK_FUNC(gethostbyname)
+if test $ac_cv_func_gethostbyname = no; then
+ AC_CHECK_LIB(nsl, gethostbyname)
+fi
+AC_CHECK_FUNC(connect)
+if test $ac_cv_func_connect = no; then
+ AC_CHECK_LIB(socket, connect)
+fi
+AC_CHECK_FUNC(sigaction)
+if test $ac_cv_func_sigaction = yes; then
+ AC_DEFINE([USE_SIGACTION],[1],[Define if sigaction is available.])
+fi
+
+AC_CHECK_FUNCS([strftime], [],
+ [AC_MSG_ERROR([strftime function is required but not found])])
+
+AH_TOP([
+#ifndef CONFIG_H
+#define CONFIG_H
+])
+
+AH_BOTTOM([
+/* some OSes do not define this ... lets take a wild guess */
+
+#ifndef INADDR_NONE
+# define INADDR_NONE 0xffffffffU
+#endif
+
+#endif /* CONFIG_H */
+
+])
+
+dnl Checks for header files.
+AC_CHECK_HEADERS([unistd.h sys/file.h stdlib.h sys/select.h])
+
+AC_CONFIG_FILES([Makefile
+ doc/Makefile
+ src/Makefile])
+
+AC_OUTPUT
--- /dev/null
+FROM debian:stable
+
+# Base
+RUN apt-get update && apt-get install -y \
+ build-essential \
+ automake \
+ autoconf \
+ m4
+
+# Add source code
+COPY ./ /app
+
+# Compile
+WORKDIR /app
+RUN autoreconf --install
+RUN ./configure && make && make install
+ENTRYPOINT ["fping"]
\ No newline at end of file
--- /dev/null
+Summary: send ICMP echo probes to multiple hosts
+Name: fping
+Version: 4.2
+Release: 1
+License: Freely redistributable without restriction
+Group: Applications/System
+Source0: http://fping.org/dist/%{name}-%{version}.tar.gz
+BuildRoot: %{_tmppath}/%{name}-%{version}-%{release}-buildroot
+
+%description
+fping is a program to send ICMP echo probes to network hosts, similar to ping,
+but much better performing when pinging multiple hosts. fping has a very long
+history: Roland Schemers did publish a first version of it in 1992 and it has
+established itself since then as a standard tool for network diagnostics and
+statistics.
+
+%prep
+%setup -q
+
+%build
+
+if [ ! -f ./configure ] ; then
+ ./autogen.sh
+fi
+
+# fping
+%configure --enable-ipv4
+make
+
+# fping6
+%configure --enable-ipv6
+make
+%{__mv} -f src/fping src/fping6
+
+%install
+rm -rf $RPM_BUILD_ROOT
+make DESTDIR=$RPM_BUILD_ROOT install
+
+# fping6
+%{__install} -Dp -m4755 src/fping6 %{buildroot}%{_sbindir}/fping6
+%{__ln_s} -f fping.8 %{buildroot}%{_mandir}/man8/fping6.8
+
+%clean
+rm -rf $RPM_BUILD_ROOT
+
+%files
+%defattr(-,root,root,-)
+%attr(4755, root, root) /usr/sbin/fping
+%attr(4755, root, root) /usr/sbin/fping6
+%doc README.md COPYING CHANGELOG.md
+/usr/share/man/man8/fping.8.gz
+/usr/share/man/man8/fping6.8.gz
+
+%post
+if [ -x /usr/sbin/setcap ]; then
+ /bin/chmod 0755 /usr/sbin/fping*
+ /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
+* Mon Dec 24 2012 Marcus Vinicius Ferreira <ferreira.mv@gmail.com>
+- Missing './configure' script when cloning from master.
+- Making 'fping6'.
+- Fix setuid permission to 'rwsr-xr-x'.
+- doc files.
+- Replacing setuid permission if 'setcap' is present on post-install.
+- Using 'http://fping.org/dist/' for release source distributions.
+
+* Mon Jul 16 2012 Stephen Schaefer <sschaefer@acm.org>
+- Initial build
+
+# vim:ft=spec:
+
--- /dev/null
+2017-02-09 David Schweikert <david@schweikert.ch>
+ * Version 3.16
+ * (feature) Support kernel-timestamping of received packets (#46)
+ * (feature) Simplify restrictions: only -i >= 1 and -p >= 10 are enforced now
+ * (bugfix) Fix option -m to return all IPs of a hostname
+ * (bugfix) Fix option -H (ttl) for IPv6
+ * (bugfix) Fix option -M (don't fragment) for IPv6
+ * (bugfix) Fix option -O (ToS) for IPv6
+ * (bugfix) Fix compatibility issue with AIX (#69, @blentzgh)
+ * (bugfix) Fix option -q not suppressing some ICMP error messages (#83)
+ * (bugfix) Fix option -M expecting an argument, when it shouldn't
+ * (bugfix) Fix minor issues found by Coverity Scan
+
+2017-01-11 David Schweikert <david@schweikert.ch>
+ * Version 3.15
+ * (bugfix) Fix compiler errors on platforms other than Linux (related
+ to the new -M option, #109)
+ * Test suite fixes for macOS
+
+2017-01-10 David Schweikert <david@schweikert.ch>
+ * Version 3.14
+ * (feature) Ignore network and broadcast for cidrs /31 and /32 (#102, Martin Topholm)
+ * (feature) New option '-M' to set the "Don't Fragment" flag (#91, Don Bowman)
+ * (feature) New option '-N' to output statistics for netdata (see: http://my-netdata.io/, #105, Costa Tsaousis)
+ * (feature) New option '-o' to calculate total outage time (#90, @jgerbeck)
+ * (bugfix) Exit code should be 2 when the hostname can't be resolved
+ (fixes #98, reported by @green-fox)
+ * (bugfix) Fix issue compliling on RHEL/Centos 7 (#95, @jbackman)
+ * (bugfix) Lower -i limit to 1 instead of 10
+ * (bugfix) Improve interval preciseness of -Q reporting
+ * (bugfix) Fix occasional false positive in -Q reporting (#97)
+ * (bugfix) Solaris 10 portability fix (#107, Peter Bray)
+
+2015-10-21 David Schweikert <david@schweikert.ch>
+ * Version 3.13
+ * (bugfix) Fix ICMP errors sometimes causing crashes with fping >= 3.11
+ (fixes #85, reported by Jamie Heilman and Bill Blough)
+
+2015-10-14 David Schweikert <david@schweikert.ch>
+ * Version 3.12
+ * (bugfix) Fix fping6 -R (fixes #84, reported by Stuart Henderson)
+
+2015-10-12 David Schweikert <david@schweikert.ch>
+ * Version 3.11
+ * (feature) New option -R to use random bytes instead of NULLs (#72, Anthony DeRobertis)
+ * (feature) Small documentation and performance improvements (Ryan Underwood)
+ * (bugfix) Fix double entries with fping -u and unreachable hosts
+ * (internal) Use sockaddr_storage and simplify code, so that we can one day support both IPv4 and IPv6 with the same binary
+
+2014-05-03 David Schweikert <david@schweikert.ch>
+ * Version 3.10
+ * Fix confusing error message with -g and IPv6 addresses (#58, reported by Axel Beckert)
+ * Allow option '-f' also for non-root (since setuid privileges are dropped)
+ * Do not retry twice DNS lookup on DNS lookup problem
+ * Remove support for NIS groups
+ * Better document -B backoff-factor and when it can be used (#33, Oleksiy Zagorskyi)
+ * More tests added
+
+2014-03-08 David Schweikert <david@schweikert.ch>
+ * Version 3.9
+ * Fix random output on socket error (reported by Aleksandrs Saveljevs, #56)
+ * Support ppc64le architecture by including alpha libtool version
+ (reported by Amit Kumar Gupta and Aravinda B Thunug)
+ * Fix compilation problem on FreeBSD (#57)
+ * Initial test suite and continous intergration (with travis-ci.org / coveralls.io)
+ * Don't output usage information on error
+
+2013-11-08 David Schweikert <david@schweikert.ch>
+ * Version 3.8
+ * Fix segmentation fault introduced in version 3.7 with loop mode (reported
+ by Vlad Glagolev, #55)
+
+2013-11-04 David Schweikert <david@schweikert.ch>
+ * Version 3.7
+ * Allow running as non-root on Mac OS X by using non-privileged ICMP (#7)
+ * Remove unnecessary IPv6 socket options
+ * Fix again compatibility issue with FreeBSD (Shawn Chu)
+ * Fix fping hanging forever on permanent sendto failure (Shawn Chu)
+ * Fix duplicate echo reply packets causing early stop in count mode
+ (reported by Ramon Schwammberger, #53)
+
+2013-10-10 David Schweikert <david@schweikert.ch>
+ * Version 3.6
+ * Fix loop issue after 65536 pings (reported by Peter Folk and GBert, #12)
+ * Minimum ping data size is now 0
+ * Removed setsockopt IPV6_CHECKSUM, which shouldn't be set and breaks
+ compiling on Solaris (reported by Juergen Arndt)
+ * Fix wrong min RTT value with -Q option (reported by Alexander Ivanov, #51)
+
+2013-05-22 David Schweikert <david@schweikert.ch>
+ * Version 3.5
+ * Fix sprint_tm buffer size crash (reported by Japheth Cleaver)
+ * Addded -D flag to print timestamps (Toke Høiland-Jørgensen)
+ * Fix fping6 build on OS X 10.8 (unknown contributor)
+ * Fix compatibility issue with FreeBSD (Alexandre Raynaud, Jason Harris, #39)
+ * Fping.spec: fix setuid permissions and provides fping6 (Marcus Vinicius Ferreira)
+ * Re-create configure script with autoconf 2.69 for aarch64 support (Chuck Anderson, #45)
+
+2012-09-04 David Schweikert <david@schweikert.ch>
+ * Version 3.4
+ * Revert "Output statistics to stdout instead of stderr", because it breaks
+ tools assuming the output goes to stderr
+
+2012-08-19 David Schweikert <david@schweikert.ch>
+ * Version 3.3
+ * Do not output icmp errors with -q (#1)
+ * Add --enable-ipv4 and --enable-ipv6 options to configure (Niclas Zeising)
+ * Fix removing of unreachable hosts when doing loop (Thomas Liske, #13 #23)
+ * Fix -A for fping6 (reported by Matt LaPlante, #14)
+ * Fix "options inet6" breaking IPv4 name resolution (reported by Matt LaPlante, #17)
+ * Output statistics to stdout instead of stderr (suggested by Simon Leinen, #9)
+ * Set default data size to 56 bytes on all architectures (#18)
+ * Added contrib/fping.spec (Stephen Schaefer, #24)
+ * Convert man-page source to POD for easier maintenance
+ * Fix error message on DNS error for IPv6 hosts (#27)
+ * Fix -n flag in fping6 (#28)
+ * Man-page fix: TOS option typo (Thomas Liske, #23)
+ * Man-page fix: inconsistency in regards to numeric arguments (Robert Henney)
+ * Man-page fix: better description of option -q (#15)
+
+2012-05-29 David Schweikert <david@schweikert.ch>
+ * Version 3.2
+ * Improve documentation for -g option (G.W. Haywood)
+ * Performance optimization for big select timeouts (#10, Andrey Bondarenko)
+ * Fix restart of select call after interrupt signal (#8, Boian Bonev)
+ * Fix infinite loop caused by linked list corruption (#11, Boian Bonev)
+
+2012-04-26 David Schweikert <david@schweikert.ch>
+ * Version 3.1
+ * -g option (generate): exclude network and broadcast address for cidr
+ ranges (idea by Eric Brander)
+ * do not explicitely check if running as root, to make it possible to
+ install fping with linux capabilities instead of making it setuid
+ (setcap cap_net_raw+ep fping)
+ * ANSI C (C89) compiler now a requirement
+ * Portability fixes
+ * Reorganized source directory
+ * Bugfix: fix timeout issue on Solaris (Sandor Geller)
+ * Man-page fixes (Axel Beckert)
+ * Added -H option to specify number of hops (Paul Duda)
+ * Output usage information to stdout when called with -h (Paul Duda)
+
+2011-12-28 David Schweikert <david@schweikert.ch>
+ * Version 3.0
+ * rewritten main loop for improved performance
+ * -T parameter (select timeout) now obsolete
+ * Maintenance taken over from unresponsive previous maintainer
+ (anybody please step up, if you disagree)
+ * New homepage: www.fping.org
+
+2009-12-21 Tobi Oetiker <tobi@oetiker.ch>
+ * Version v2.4b2-to3-ipv6
+ * added -On option to set the TOS octet
+ * Removed unused variables from code
+ * updated to current autoconf standards
+ * Merged Debian changes (see below)
+
+----------------------------------------------------------------------
+
+fping (2.4b2-to-ipv6-16.1) unstable; urgency=low
+
+ * NMU during Moenchengladbach BSP
+ * Fixes FTBFS on kfreebsd (Closes: #555398)
+ * Fixes typo "Paramter" in binary
+
+ -- Axel Beckert <abe@deuxchevaux.org> Sat, 23 Jan 2010 16:22:02 +0100
+
+fping (2.4b2-to-ipv6-16) unstable; urgency=low
+
+ * Fix the following bugs
+ - Network byte order sensitivity was missing completely.
+ Added hopefully all missing calls.
+ - The sequence numbering scheme used led to packet drops.
+ Changed it to a more senseful numbering scheme.
+ - Some minor C programming mistakes ('=' instead of '==').
+ Patch by Stephan Fuhrmann; closes: #502569
+ * Add support for command line select timeout setting
+ Patch by Marton Balint; closes: #502575
+ * Remove symlinks in /usr/sbin; closes: #377732
+ * Standards-Version is 3.8.0
+
+ -- Anibal Monsalve Salazar <anibal@debian.org> Sat, 18 Oct 2008 12:04:52 +1100
+
+fping (2.4b2-to-ipv6-15) unstable; urgency=low
+
+ * Added interface binding (-I) for fping
+ Patch by Peter Naulls <peter@mushroomnetworks.com>
+ Closes: #439014
+ * Fixed a couple of typos in fping.8. Closes: #423180
+ * Added homepage control header
+ * Bumped Standards-Version to 3.7.3
+ * Fixed the following lintian issue:
+ - debian-rules-sets-DH_COMPAT
+
+ -- Anibal Monsalve Salazar <anibal@debian.org> Mon, 03 Mar 2008 17:46:17 +1100
+
+fping (2.4b2-to-ipv6-13) unstable; urgency=low
+
+ * Fixed stdout flush problem, closes: #340146.
+ Patch by Bart Martens <bart.martens@advalvas.be>.
+
+ -- Anibal Monsalve Salazar <anibal@debian.org> Fri, 30 Dec 2005 08:30:09 +1100
+
+fping (2.4b2-to-ipv6-12) unstable; urgency=low
+
+ * Fixed "problem with option -r (retry limit)", closes: #318402.
+ Patch by Qingning Huo <qingningh@lanware.co.uk>.
+
+ -- Anibal Monsalve Salazar <anibal@debian.org> Sat, 08 Oct 2005 21:26:35 +1000
+
+fping (2.4b2-to-ipv6-11) unstable; urgency=low
+
+ * Fixed "would be useful to specify 'source address' like ping for multi
+ homed machines", closes: #198486.
+ Patch by Marc Haber <mh+debian-bugs@zugschlus.de>.
+
+ -- Anibal Monsalve Salazar <anibal@debian.org> Thu, 02 Jun 2005 08:14:54 +1000
+
+fping (2.4b2-to-ipv6-10) unstable; urgency=low
+
+ * Fixed "unnecessary delay with the -c option after the last packet"
+ (Closes: #293856). Patch by Niko Tyni <ntyni@iki.fi>
+
+ -- Anibal Monsalve Salazar <anibal@debian.org> Sun, 06 Feb 2005 23:25:57 +1100
+
+fping (2.4b2-to-ipv6-9) unstable; urgency=low
+
+ * Fixed "fping6 always does reverse lookup" (Closes: #273647).
+ Patch by Jeroen Massar and forwarded by Bernhard Schmidt <berni@birkenwald.de>
+
+ -- Anibal Monsalve Salazar <A.Monsalve.Salazar@IEEE.org> Mon, 10 Jan 2005 00:01:32 +1100
+
+fping (2.4b2-to-ipv6-7) unstable; urgency=low
+
+ * Build fping in build/ipv[46] instead of build and build-ipv6.
+ * Made DNS errors non-fatal for IPv6 (closes: #198056).
+
+ -- Herbert Xu <herbert@debian.org> Fri, 20 Jun 2003 21:36:30 +1000
+
+fping (2.4b2-to-ipv6-6) unstable; urgency=low
+
+ * Do not use incorrect linux.h file (closes: #85468).
+
+ -- Herbert Xu <herbert@debian.org> Sat, 17 May 2003 14:13:11 +1000
+
+fping (2.4b2-to-ipv6-5) unstable; urgency=low
+
+ * Fixed yet another divide by zero bug (closes: #148445).
+
+ -- Herbert Xu <herbert@debian.org> Tue, 4 Jun 2002 12:18:03 +1000
+
+fping (2.4b2-to-ipv6-4) unstable; urgency=low
+
+ * Made fping6 setuid (closes: #136386).
+ * Moved fping back into bin.
+ * Partially applied IPv6 patch to fix IPv6 checksums (closes: #136479).
+
+ -- Herbert Xu <herbert@debian.org> Sun, 7 Apr 2002 20:36:56 +1000
+
+fping (2.4b2-to-ipv6-3) unstable; urgency=low
+
+ * Added compatibility symlink for fping (closes: #135203).
+
+ -- Herbert Xu <herbert@debian.org> Sat, 23 Feb 2002 08:34:11 +1100
+
+fping (2.4b2-to-ipv6-2) unstable; urgency=low
+
+ * Fixed another divide by zero error (closes: #132370).
+
+ -- Herbert Xu <herbert@debian.org> Thu, 7 Feb 2002 20:10:48 +1100
+
+fping (2.4b2-to-ipv6-1) unstable; urgency=low
+
+ * New upstream release.
+ * Install fping into sbin as done by upstream.
+
+ -- Herbert Xu <herbert@debian.org> Fri, 1 Feb 2002 22:11:59 +1100
+
+fping (2.2b2-3) unstable; urgency=low
+
+ * Removed INSTALL file from package (closes: #84050).
+ * Fixed alignment bug.
+
+ -- Herbert Xu <herbert@debian.org> Sat, 10 Feb 2001 19:25:18 +1100
+
+fping (2.2b2-2) unstable; urgency=low
+
+ * Made changes for dpkg-statoverride (closes: #83838).
+
+ -- Herbert Xu <herbert@debian.org> Sun, 28 Jan 2001 21:53:05 +1100
+
+fping (2.2b2-1) unstable; urgency=low
+
+ * New upstream release.
+ * Fixed typo that prevented -d from working (closes: #83255).
+ * Drop root privileges after opening the socket (closes: #81589).
+ * Fixed the options [tip], they were out by a factor of 10
+ (Richard Kettlewell, closes: #83742).
+
+ -- Herbert Xu <herbert@debian.org> Sun, 28 Jan 2001 00:09:41 +1100
+
+fping (2.2b1-2) unstable; urgency=low
+
+ * Fixed typo in control file, spotted by William Ono (closes: #49909).
+
+ -- Herbert Xu <herbert@debian.org> Mon, 15 May 2000 12:27:03 +1000
+
+fping (2.2b1-1) unstable; urgency=low
+
+ * Initial release.
+ * Fixed divide by zero error (closes: #29902).
+
+ -- Herbert Xu <herbert@debian.org> Sat, 30 Oct 1999 16:36:19 +1000
+---------------------------------------------------------------------------------
+
+Wed Jan 16 2002
+Jeroen Massar
+- Revision v2.4b2-to-IPv6
+ - Added IPv6 support.
+ - Added -I option for selecting source IPv4/IPv6 address.
+ - Makefile.in now generates a Makefile which will build both
+ fping (IPv4) and fping6 (IPv6). Thus it makes an fping (IPv4 only)
+ and an fping6 (IPv6 only).
+ - num_unreachable was counted twice when a sendto() generated errors.
+ - See http://unfix.org/projects/ipv6/
+
+Tue Mar 14 2001
+Jason Ewasiuk <jasone@remote.net>
+- Revision v2.4b1
+ - added -g option for generating IPs from a start to an end value
+ - two available options, generate IPs from start IP to end IP
+ or from a passed netmask, such as 192.168.1.0/24
+
+Thu Feb 15 2001
+Jason Ewasiuk <jasone@remote.net>
+- Revision v2.3b1
+ - formatting changes to code layout (fping.c)
+ NOTE: Best viewed with a tab stop of 4
+ - merged in changes from Debian c/o Herbert Xu
+ <herbert@gondor.apana.org.au>
+ - Compilation fix on alphas with glibc
+ - Alignment issues (note from JE: in wait_for_reply())
+ - A typo with the time specified on the command line
+ (note from JE: bug was using 10 instead of 100)
+ - Drop privileges after obtaining socket
+ (note from JE: might be moot, since prog exits if
+ user is not root)
+ - touched all files in package to this date
+ - couple new #ifdefs added for future WIN32 support
+ (Haven't got to adding this yet, will take a lot of rewriting.)
+
+Fri Dec 8 10:33:13 2000 Roland Schemers <schemers@stanford.edu>
+
+ * stop using sys_errlist and start using strerror
+ fixed bug in output of -C
+
+Wed Jan 8 11:18:37 1997 Roland Schemers <schemers@stanford.edu>
+
+ * Created ChangeLog file. What follows was from the CHANGES file.
+
+* Revision 2.0 1994/10/31 21:26:23 morgan
+
+ Substantial rewrite, including new features:
+
+ support some traditional ping features:
+ loop mode
+ specify size of data packets
+ specify how many pings to send
+ show per-response data
+ interpret ICMPs other than ICMP Echo response
+
+ also
+
+ rewrote main loop completely
+ make timings in tenths of milliseconds
+ do exponential backoff on retries
+ port to more systems
+ add some debugging stuff
+ do better checking on whether received ICMP is for us
+
+* Revision 1.24 1993/12/10 23:11:39 schemers
+
+ commented out seteuid(getuid()) since it isn't needed
+
+* Revision 1.23 1993/12/10 18:33:41 schemers
+
+ Took out the -f option for non-root users. This can be enabled by
+ defining ENABLE_F_OPTION before compiling. There is a call to
+ access before opening the file, but there is a race condition.
+ Reading from stdin is much safer.
+
+
+* Revision 1.22 1993/11/16 19:49:24 schemers
+
+ Took out setuid(getuid()) and used access() system call to
+ check for access to the file specified with "-f".
+
+* Revision 1.21 1993/07/20 18:08:19 schemers
+
+ commented out the test to make sure the ping packet came from the
+ same IP address as the one we sent to. This could cause problems on
+ multi-homed hosts.
+
+* Revision 1.20 1993/02/23 00:16:38 schemers
+
+fixed syntax error (should have compiled before checking in...)
+
+* Revision 1.19 1993/02/23 00:15:15 schemers
+
+turned off printing of "is alive" when -a is specified.
+
+* Revision 1.18 1992/07/28 15:16:44 schemers
+
+added a fflush(stdout) call before the summary is sent to stderr, so
+everything shows up in the right order.
+
+* Revision 1.17 1992/07/23 03:29:42 schemers
+* Revision 1.16 1992/07/22 19:24:37 schemers
+
+Fixed declaration of timeval_diff. Didn't notice the problem because
+I use 'cc' in stead of gcc under Ultrix. Time to switch? :-)
+
+Modified file reaing so it would skip blank lines or lines starting
+with a '#'. Now you can do something like:
+
+fping -ad < /etc/hosts
+
+* Revision 1.15 1992/07/21 17:07:18 schemers
+
+Put in sanity checks so only root can specify "dangerous" options.
+Changed usage to show switchs in alphabetical order.
+* Revision 1.14 1992/07/21 16:40:52 schemers
+* Revision 1.13 1992/07/17 21:02:17 schemers
+
+Changed the default timeout to 2500 msec, and retry to 3. This was
+due to suggestions from people with slow (WAN) networks. The default
+1 sec timeout was too fast.
+
+
+Added '-e' option for showing elapsed (round-trip) times on pakets, and
+modified the -s option to include min, max, and average round-trip times,
+and over all elapsed time.
+
+Modified action taken when a error is returned from sendto. The action
+taken now considers the host unreachable and prints the hostname
+followed by the errno message. The program will not exit and will continue
+to try other hosts.
+
+* Revision 1.12 1992/07/17 16:38:54 schemers
+* Revision 1.11 1992/07/17 16:28:38 schemers
+
+ move socket create call so I could do a setuid(getuid()) before the
+ fopen call is made. Once the socket is created root privs aren't needed
+ to send stuff out on it.
+
+ moved num_timeout counter. It really was for debug purposes and didn't
+ make sense to the general public :-) Now it is the number of timeouts
+ (pings that didn't get received with the time limit).
+
+
+* Revision 1.10 1992/07/16 16:24:38 schemers
+* Revision 1.9 1992/07/16 16:00:04 schemers
+* Revision 1.8 1992/07/16 05:44:41 schemers
+
+Added _NO_PROTO stuff for older compilers, and _POSIX_SOURCE
+for unistd.h, and _POSIX_SOURCE for stdlib.h. Also added
+check for __cplusplus.
+
+Now compiles ok under Ultrix 3.1, and Sun4 using cc. Also compiled
+ok using g++ 2.2.2.
+
+Changed '-a' and '-u' flags to be mutually exclusive (makes sense, since
+specifiying both '-a' and '-u' is the same as not specifiying anything.
+Since '-a' and '-u' are mutually exclusive, these options now only print
+the hostname, and not the 'is alive' or 'is unreachable' messages.
+This makes it much easier to do stuff like:
+
+#!/usr/local/bin/perl
+$hosts_to_backup=`cat /etc/hosts.backup|fping -a`;
+
+Since you don't have to strip off the 'is alive' messages.
+
+Changed usage to and stats to print to stderr instead of stdout.
+
+-----------------------------------------------------------------------------
+
+RCS header info from original fping.c package (no longer required)
+
+/*
+ ***************************************************
+ *
+ * Standard RCS Header information (see co(1))
+ *
+ * $Author: schemers $
+ *
+ * $Date: 1997/01/08 20:29:33 $
+ *
+ * $Revision: 2.2 $
+ *
+ * $Locker: $
+ *
+ * $Source: /afs/ir/group/networking/src/fping/fping-2.2/src/RCS/fping.c,v $
+ *
+ * $State: Exp $
+ *
+ * $Log: fping.c,v $
+ *
+ * Revision 2.2 1997/01/08 20:29:33 schemers
+ * changes for autoconf/automake
+ *
+ * Revision 2.1 1997/01/08 19:07:18 schemers
+ * checked in RL "Bob"'s changes before configure'ing
+ *
+ * Revision 2.0 1994/10/31 21:26:23 schemers
+ * many changes by RL "Bob" Morgan
+ * add timing data collection, loop mode, per-packet output, etc
+ *
+ * Revision 1.24 1993/12/10 23:11:39 schemers
+ * commented out seteuid(getuid()) since it isn't needed
+ *
+ * Revision 1.23 1993/12/10 18:33:41 schemers
+ * Took out the -f option for non-root users. This can be enabled by
+ * defining ENABLE_F_OPTION before compiling. There is a call to
+ * access before opening the file, but there is a race condition.
+ * Reading from stdin is much safer.
+ *
+ * Revision 1.22 1993/11/16 19:49:24 schemers
+ * Took out setuid(getuid()) and used access() system call to
+ * check for access to the file specified with "-f".
+ *
+ * Revision 1.21 1993/07/20 18:08:19 schemers
+ * commented out the test to make sure the ping packet came from the
+ * same IP address as the one we sent to. This could cause problems on
+ * multi-homed hosts.
+ *
+ * Revision 1.20 1993/02/23 00:16:38 schemers
+ * fixed syntax error (should have compiled before checking in...)
+ *
+ * Revision 1.19 1993/02/23 00:15:15 schemers
+ * turned off printing of "is alive" when -a is specified.
+ *
+ * Revision 1.18 1992/07/28 15:16:44 schemers
+ * added a fflush(stdout) call before the summary is sent to stderr, so
+ * everything shows up in the right order.
+ *
+ * Revision 1.17 1992/07/23 03:29:42 schemers
+ * fixed declaration of timeval_diff.
+ *
+ * Revision 1.16 1992/07/22 19:24:37 schemers
+ * Modified file reading so it would skip blanks lines or lines starting
+ * with a '#'. Now you can do something like:
+ *
+ * fping -ad < /etc/hosts
+ *
+ * Revision 1.15 1992/07/21 17:07:18 schemers
+ * Put in sanity checks so only root can specify "dangerous" options.
+ * Changed usage to show switchs in alphabetical order.
+ *
+ * Revision 1.14 1992/07/21 16:40:52 schemers
+ * Now when sendto returns an error, the host is considered unreachable and
+ * and the error message (from errno) is displayed.
+ *
+ * Revision 1.13 1992/07/17 21:02:17 schemers
+ * changed default timeout to 2500 msec (for WANs), and default try
+ * to 3. This gives 10 second overall timeout.
+ *
+ * Added -e option for showing elapsed (round-trip) time on packets
+ *
+ * Modified -s option to inlude to round-trip stats
+ *
+ * Added #ifndef DEFAULT_* stuff its easier to change the defaults
+ *
+ * Reorganized main loop.
+ *
+ * cleaned up timeval stuff. removed set_timeval and timeval_expired
+ * since they aren't needed anymore. Just use timeval_diff.
+ *
+ * Revision 1.12 1992/07/17 16:38:54 schemers
+ * move socket create call so I could do a setuid(getuid()) before the
+ * fopen call is made. Once the socket is created root privs aren't needed
+ * to send stuff out on it.
+ *
+ * Revision 1.11 1992/07/17 16:28:38 schemers
+ * moved num_timeout counter. It really was for debug purposes and didn't
+ * make sense to the general public :-) Now it is the number of timeouts
+ * (pings that didn't get received with the time limit).
+ *
+ * Revision 1.10 1992/07/16 16:24:38 schemers
+ * changed usage() to use fprintf(stderr,"...");
+ *
+ * Revision 1.9 1992/07/16 16:00:04 schemers
+ * Added _NO_PROTO stuff for older compilers, and _POSIX_SOURCE
+ * for unistd.h, and _POSIX_SOURCE for stdlib.h. Also added
+ * check for __cplusplus.
+ *
+ * Revision 1.8 1992/07/16 05:44:41 schemers
+ * changed -a and -u to only show hostname in results. This is
+ * for easier parsing. Also added -v flag
+ *
+ * Revision 1.7 1992/07/14 18:45:23 schemers
+ * initialized last_time in add_host function
+ *
+ * Revision 1.6 1992/07/14 18:32:40 schemers
+ * changed select to use FD_ macros
+ *
+ * Revision 1.5 1992/07/14 17:21:22 schemers
+ * standardized exit status codes
+ *
+ * Revision 1.4 1992/06/26 15:25:35 schemers
+ * changed name from rrping to fping
+ *
+ * Revision 1.3 1992/06/24 15:39:32 schemers
+ * added -d option for unreachable systems
+ *
+ * Revision 1.2 1992/06/23 03:01:23 schemers
+ * misc fixes from R.L. "Bob" Morgan
+ *
+ * Revision 1.1 1992/06/19 18:23:52 schemers
+ * Initial revision
+ *
+ *--------------------------------------------------
+ * Copyright (c) 1992, 1994, 1997 Board of Trustees
+ * Leland Stanford Jr. University
+ ***************************************************
+ */
+
+
--- /dev/null
+man_MANS = fping.8
+
+EXTRA_DIST = fping.8 fping.pod README.1992 CHANGELOG.pre-v4
+
+fping.8: fping.pod
+ pod2man -c "" -s 8 -r "fping" $< >$@
--- /dev/null
+Original README (from 1992)
+
+ fping - A tool to quickly ping N number of hosts to determine
+ their reachability.
+
+ Roland J. Schemers III - Stanford University
+ schemers@Stanford.EDU
+
+ fping is a ping(1) like program which uses the Internet Control
+ Message Protocol (ICMP) echo request to determine if a host is
+ up. fping is different from ping in that you can specify any
+ number of hosts on the command line, or specify a file containing
+ the lists of hosts to ping. Instead of trying one host until it
+ timeouts or replies, fping will send out a ping packet and move
+ on to the next host in a round-robin fashion. If a host replies,
+ it is noted and removed from the list of hosts to check. If a host
+ does not respond within a certain time limit and/or retry limit it
+ will be considered unreachable.
+
+Site
+ Stanford University has a large TCP/IP network with over 16,000
+ assigned IP addresses and over 100 IP subnets.
+
+Problem and Issues
+
+ With a large a number of IP addresses in use, its becomes more and
+ more time consuming to check on which IP addresses are actively
+ in use, and which critical machines (routers, bridges, servers, etc)
+ are reachable. One example is we have a program which goes through
+ all of our routers arp caches looking for IP addresses that are in
+ use. After finding a list of IP addresses that aren't in any arp
+ caches fping can then be used to see if these IP addresses really
+ aren't being used, or are just behind the routers. Checking 2500
+ hosts (99% of which are unreachable) via ping can take hours.
+
+ fping was written to solve the problem of pinging N number of hosts
+ in an efficient manner. By sending out pings in a round-robin fashion
+ and checking on responses as they come in at random, a large number of
+ hosts can be checked at once.
+
+ Unlike ping, fping is meant to be used in scripts and its
+ output is easy to parse.
+
--- /dev/null
+.\" Automatically generated by Pod::Man 4.14 (Pod::Simple 3.43)
+.\"
+.\" Standard preamble:
+.\" ========================================================================
+.de Sp \" Vertical space (when we can't use .PP)
+.if t .sp .5v
+.if n .sp
+..
+.de Vb \" Begin verbatim text
+.ft CW
+.nf
+.ne \\$1
+..
+.de Ve \" End verbatim text
+.ft R
+.fi
+..
+.\" Set up some character translations and predefined strings. \*(-- will
+.\" give an unbreakable dash, \*(PI will give pi, \*(L" will give a left
+.\" double quote, and \*(R" will give a right double quote. \*(C+ will
+.\" give a nicer C++. Capital omega is used to do unbreakable dashes and
+.\" therefore won't be available. \*(C` and \*(C' expand to `' in nroff,
+.\" nothing in troff, for use with C<>.
+.tr \(*W-
+.ds C+ C\v'-.1v'\h'-1p'\s-2+\h'-1p'+\s0\v'.1v'\h'-1p'
+.ie n \{\
+. ds -- \(*W-
+. ds PI pi
+. if (\n(.H=4u)&(1m=24u) .ds -- \(*W\h'-12u'\(*W\h'-12u'-\" diablo 10 pitch
+. if (\n(.H=4u)&(1m=20u) .ds -- \(*W\h'-12u'\(*W\h'-8u'-\" diablo 12 pitch
+. ds L" ""
+. ds R" ""
+. ds C` ""
+. ds C' ""
+'br\}
+.el\{\
+. ds -- \|\(em\|
+. ds PI \(*p
+. ds L" ``
+. ds R" ''
+. ds C`
+. ds C'
+'br\}
+.\"
+.\" Escape single quotes in literal strings from groff's Unicode transform.
+.ie \n(.g .ds Aq \(aq
+.el .ds Aq '
+.\"
+.\" If the F register is >0, we'll generate index entries on stderr for
+.\" titles (.TH), headers (.SH), subsections (.SS), items (.Ip), and index
+.\" entries marked with X<> in POD. Of course, you'll have to process the
+.\" output yourself in some meaningful fashion.
+.\"
+.\" Avoid warning from groff about undefined register 'F'.
+.de IX
+..
+.nr rF 0
+.if \n(.g .if rF .nr rF 1
+.if (\n(rF:(\n(.g==0)) \{\
+. if \nF \{\
+. de IX
+. tm Index:\\$1\t\\n%\t"\\$2"
+..
+. if !\nF==2 \{\
+. nr % 0
+. nr F 2
+. \}
+. \}
+.\}
+.rr rF
+.\"
+.\" Accent mark definitions (@(#)ms.acc 1.5 88/02/08 SMI; from UCB 4.2).
+.\" Fear. Run. Save yourself. No user-serviceable parts.
+. \" fudge factors for nroff and troff
+.if n \{\
+. ds #H 0
+. ds #V .8m
+. ds #F .3m
+. ds #[ \f1
+. ds #] \fP
+.\}
+.if t \{\
+. ds #H ((1u-(\\\\n(.fu%2u))*.13m)
+. ds #V .6m
+. ds #F 0
+. ds #[ \&
+. ds #] \&
+.\}
+. \" simple accents for nroff and troff
+.if n \{\
+. ds ' \&
+. ds ` \&
+. ds ^ \&
+. ds , \&
+. ds ~ ~
+. ds /
+.\}
+.if t \{\
+. ds ' \\k:\h'-(\\n(.wu*8/10-\*(#H)'\'\h"|\\n:u"
+. ds ` \\k:\h'-(\\n(.wu*8/10-\*(#H)'\`\h'|\\n:u'
+. ds ^ \\k:\h'-(\\n(.wu*10/11-\*(#H)'^\h'|\\n:u'
+. ds , \\k:\h'-(\\n(.wu*8/10)',\h'|\\n:u'
+. ds ~ \\k:\h'-(\\n(.wu-\*(#H-.1m)'~\h'|\\n:u'
+. ds / \\k:\h'-(\\n(.wu*8/10-\*(#H)'\z\(sl\h'|\\n:u'
+.\}
+. \" troff and (daisy-wheel) nroff accents
+.ds : \\k:\h'-(\\n(.wu*8/10-\*(#H+.1m+\*(#F)'\v'-\*(#V'\z.\h'.2m+\*(#F'.\h'|\\n:u'\v'\*(#V'
+.ds 8 \h'\*(#H'\(*b\h'-\*(#H'
+.ds o \\k:\h'-(\\n(.wu+\w'\(de'u-\*(#H)/2u'\v'-.3n'\*(#[\z\(de\v'.3n'\h'|\\n:u'\*(#]
+.ds d- \h'\*(#H'\(pd\h'-\w'~'u'\v'-.25m'\f2\(hy\fP\v'.25m'\h'-\*(#H'
+.ds D- D\\k:\h'-\w'D'u'\v'-.11m'\z\(hy\v'.11m'\h'|\\n:u'
+.ds th \*(#[\v'.3m'\s+1I\s-1\v'-.3m'\h'-(\w'I'u*2/3)'\s-1o\s+1\*(#]
+.ds Th \*(#[\s+2I\s-2\h'-\w'I'u*3/5'\v'-.3m'o\v'.3m'\*(#]
+.ds ae a\h'-(\w'a'u*4/10)'e
+.ds Ae A\h'-(\w'A'u*4/10)'E
+. \" corrections for vroff
+.if v .ds ~ \\k:\h'-(\\n(.wu*9/10-\*(#H)'\s-2\u~\d\s+2\h'|\\n:u'
+.if v .ds ^ \\k:\h'-(\\n(.wu*10/11-\*(#H)'\v'-.4m'^\v'.4m'\h'|\\n:u'
+. \" for low resolution devices (crt and lpr)
+.if \n(.H>23 .if \n(.V>19 \
+\{\
+. ds : e
+. ds 8 ss
+. ds o a
+. ds d- d\h'-1'\(ga
+. ds D- D\h'-1'\(hy
+. ds th \o'bp'
+. ds Th \o'LP'
+. ds ae ae
+. ds Ae AE
+.\}
+.rm #[ #] #H #V #F C
+.\" ========================================================================
+.\"
+.IX Title "FPING 8"
+.TH FPING 8 "2025-08-19" "fping" ""
+.\" For nroff, turn off justification. Always turn off hyphenation; it makes
+.\" way too many mistakes in technical documents.
+.if n .ad l
+.nh
+.SH "NAME"
+fping \- send ICMP ECHO_REQUEST packets to network hosts
+.SH "SYNOPSIS"
+.IX Header "SYNOPSIS"
+\&\fBfping\fR [ \fIoptions\fR ] [ \fIsystems...\fR ]
+.SH "DESCRIPTION"
+.IX Header "DESCRIPTION"
+\&\fBfping\fR is a program like \fBping\fR which uses the Internet Control Message
+Protocol (\s-1ICMP\s0) echo request to determine if a target host is responding.
+\&\fBfping\fR differs from \fBping\fR in that you can specify any number of targets on the
+command line, or specify a file containing the lists of targets to ping.
+Instead of sending to one target until it times out or replies, \fBfping\fR will
+send out a ping packet and move on to the next target in a round-robin fashion.
+In the default mode, if a target replies, it is noted and removed from the list
+of targets to check; if a target does not respond within a certain time limit
+and/or retry limit it is designated as unreachable. \fBfping\fR also supports
+sending a specified number of pings to a target, or looping indefinitely (as in
+\&\fBping\fR ). Unlike \fBping\fR, \fBfping\fR is meant to be used in scripts, so its
+output is designed to be easy to parse. Current statistics can be obtained without
+termination of process with signal \s-1SIGQUIT\s0 (^\e from the keyboard on most systems).
+.SH "OPTIONS"
+.IX Header "OPTIONS"
+.IP "\fB\-4\fR, \fB\-\-ipv4\fR" 5
+.IX Item "-4, --ipv4"
+Restrict name resolution and IPs to IPv4 addresses.
+.IP "\fB\-6\fR, \fB\-\-ipv6\fR" 5
+.IX Item "-6, --ipv6"
+Restrict name resolution and IPs to IPv6 addresses.
+.IP "\fB\-a\fR, \fB\-\-alive\fR" 5
+.IX Item "-a, --alive"
+Show systems that are alive. (Options \fB\-c\fR and \fB\-C\fR override \fB\-a\fR.)
+.IP "\fB\-A\fR, \fB\-\-addr\fR" 5
+.IX Item "-A, --addr"
+Display targets by address rather than \s-1DNS\s0 name. Combined with \-d, the output
+will be both the ip and (if available) the hostname.
+.IP "\fB\-b\fR, \fB\-\-size\fR=\fI\s-1BYTES\s0\fR" 5
+.IX Item "-b, --size=BYTES"
+Number of bytes of ping data to send. The minimum size (normally 12) allows
+room for the data that \fBfping\fR needs to do its work (sequence number,
+timestamp). The reported received data size includes the \s-1IP\s0 header (normally
+20 bytes) and \s-1ICMP\s0 header (8 bytes), so the minimum total size is 40 bytes.
+Default is 56, as in \fBping\fR. Maximum is the theoretical maximum \s-1IP\s0 datagram
+size (64K), though most systems limit this to a smaller, system-dependent
+number. Cannot be used together with \fB\-\-icmp\-timestamp\fR.
+.IP "\fB\-B\fR, \fB\-\-backoff\fR=\fIN\fR" 5
+.IX Item "-B, --backoff=N"
+Backoff factor. In the default mode, \fBfping\fR sends several requests to a
+target before giving up, waiting longer for a reply on each successive request.
+This parameter is the value by which the wait time (\fB\-t\fR) is multiplied on each
+successive request; it must be entered as a floating-point number (x.y). The
+default is 1.5.
+.IP "\fB\-c\fR, \fB\-\-count\fR=\fIN\fR" 5
+.IX Item "-c, --count=N"
+Number of request packets to send to each target. In this mode, a line is
+displayed for each received response (this can suppressed with \fB\-q\fR or \fB\-Q\fR).
+Also, statistics about responses for each target are displayed when all
+requests have been sent (or when interrupted). This option overrides \fB\-a\fR
+or \fB\-u\fR.
+.IP "\fB\-C\fR, \fB\-\-vcount\fR=\fIN\fR" 5
+.IX Item "-C, --vcount=N"
+Similar to \fB\-c\fR, but the per-target statistics are displayed in a format
+designed for automated response-time statistics gathering. For example:
+.Sp
+.Vb 2
+\& $ fping \-C 5 \-q somehost
+\& somehost : 91.7 37.0 29.2 \- 36.8
+.Ve
+.Sp
+shows the response time in milliseconds for each of the five requests, with the
+\&\f(CW\*(C`\-\*(C'\fR indicating that no response was received to the fourth request. This
+option overrides \fB\-a\fR or \fB\-u\fR.
+.IP "\fB\-\-check\-source\fR" 5
+.IX Item "--check-source"
+Discard Echo replies that are sourced from a different address than the target
+address. This avoids spurious reachability results on busy monitoring systems
+where two \fBfping\fR instances with the same lower 16 bits of the process \s-1ID\s0 may
+be running at the same time.
+.IP "\fB\-d\fR, \fB\-\-rdns\fR" 5
+.IX Item "-d, --rdns"
+Use \s-1DNS\s0 to lookup address of ping target. This allows you to give fping
+a list of \s-1IP\s0 addresses as input and print hostnames in the output. This is similar
+to option \fB\-n\fR/\fB\-\-name\fR, but will force a reverse-DNS lookup even if you give
+hostnames as target (\s-1NAME\-\s0>\s-1IP\-\s0>\s-1NAME\s0).
+.IP "\fB\-D\fR, \fB\-\-timestamp\fR" 5
+.IX Item "-D, --timestamp"
+Add Unix timestamps in front of output lines generated with in looping or counting
+modes (\fB\-l\fR, \fB\-c\fR, or \fB\-C\fR).
+.Sp
+Subcommand: \fB\-\-timestamp\-format\fR=\fIctime|iso|rfc3339\fR
+.Sp
+Allow to change the timestamp format of the \fB\-D\fR option to the following format types.
+.Sp
+\&\fIctime\fR = \*(L"%c\*(R" (Example: Mon Jun 10 07:50:00 2024)
+.Sp
+\&\fIiso\fR = \*(L"%Y\-%m\-%dT%T%z\*(R" (Example: 2024\-06\-10T07:50:00+0200)
+.Sp
+\&\fIrfc3339\fR = \*(L"%Y\-%m\-%d \f(CW%H:\fR%M:%S\*(R" (Example: 2024\-06\-10 07:50:00)
+.IP "\fB\-e\fR, \fB\-\-elapsed\fR" 5
+.IX Item "-e, --elapsed"
+Show elapsed (round-trip) time of packets.
+.IP "\fB\-f\fR, \fB\-\-file\fR" 5
+.IX Item "-f, --file"
+Read list of targets from a file.
+.IP "\fB\-g\fR, \fB\-\-generate\fR \fIaddr/mask\fR" 5
+.IX Item "-g, --generate addr/mask"
+Generate a target list from a supplied \s-1IP\s0 netmask, or a starting and ending \s-1IP.\s0
+Specify the netmask or start/end in the targets portion of the command line. If
+a network with netmask is given, the network and broadcast addresses will be
+excluded. ex. To ping the network 192.168.1.0/24, the specified command line
+could look like either:
+.Sp
+.Vb 1
+\& $ fping \-g 192.168.1.0/24
+.Ve
+.Sp
+or
+.Sp
+.Vb 1
+\& $ fping \-g 192.168.1.1 192.168.1.254
+.Ve
+.IP "\fB\-h\fR, \fB\-\-help\fR" 5
+.IX Item "-h, --help"
+Print usage message.
+.IP "\fB\-H\fR, \fB\-\-ttl\fR=\fIN\fR" 5
+.IX Item "-H, --ttl=N"
+Set the \s-1IP TTL\s0 field (time to live hops).
+.IP "\fB\-\-print\-ttl\fR" 5
+.IX Item "--print-ttl"
+Displays the IPv4 \s-1TTL\s0 value from the \s-1IP\s0 Header in the output.
+If \fBfping\fR cannot read the \s-1TTL\s0 value, \*(L"(\s-1TTL\s0 unknown)\*(R" is returned.
+IPv4 only, requires root privileges or cap_net_raw.
+.IP "\fB\-i\fR, \fB\-\-interval\fR=\fI\s-1MSEC\s0\fR" 5
+.IX Item "-i, --interval=MSEC"
+The minimum amount of time (in milliseconds) between sending a ping packet
+to any target (default is 10, minimum is 1).
+.IP "\fB\-I\fR, \fB\-\-iface\fR=\fI\s-1IFACE\s0\fR" 5
+.IX Item "-I, --iface=IFACE"
+Set the interface (requires \s-1SO_BINDTODEVICE\s0 support).
+.IP "\fB\-\-icmp\-timestamp\fR" 5
+.IX Item "--icmp-timestamp"
+Send \s-1ICMP\s0 timestamp requests (\s-1ICMP\s0 type 13) instead of \s-1ICMP\s0 Echo requests.
+Print \s-1ICMP\s0 timestamps for originate, receive, and transmit, together with
+the local receive time in the same format, in addition to normal output.
+Cannot be used together with \fB\-b\fR because \s-1ICMP\s0 timestamp messages have a fixed size.
+IPv4 only, requires root privileges or cap_net_raw.
+.IP "\fB\-k\fR, \fB\-\-fwmark\fR=\fI\s-1FWMARK\s0\fR" 5
+.IX Item "-k, --fwmark=FWMARK"
+Set \s-1FWMARK\s0 on ping packets for policy-based routing. Requires Linux kernel
+2.6.25<=, and root privileges or cap_net_admin.
+.IP "\fB\-l\fR, \fB\-\-loop\fR" 5
+.IX Item "-l, --loop"
+Loop sending packets to each target indefinitely. Can be interrupted with
+Ctrl-C; statistics about responses for each target are then displayed.
+.IP "\fB\-m\fR, \fB\-\-all\fR" 5
+.IX Item "-m, --all"
+Send pings to each of a target host's multiple \s-1IP\s0 addresses (use of option '\-A'
+is recommended).
+.IP "\fB\-M\fR, \fB\-\-dontfrag\fR" 5
+.IX Item "-M, --dontfrag"
+Set the \*(L"Don't Fragment\*(R" bit in the \s-1IP\s0 header (used to determine/test the \s-1MTU\s0).
+.IP "\fB\-n\fR, \fB\-\-name\fR" 5
+.IX Item "-n, --name"
+If targets are specified as \s-1IP\s0 addresses, do a reverse-DNS lookup on them
+to print hostnames in the output.
+.IP "\fB\-N\fR, \fB\-\-netdata\fR" 5
+.IX Item "-N, --netdata"
+Format output for netdata (\-l \-Q are required). See: <https://netdata.cloud/>
+.IP "\fB\-o\fR, \fB\-\-outage\fR" 5
+.IX Item "-o, --outage"
+Calculate \*(L"outage time\*(R" based on the number of lost pings and the interval used (useful for network convergence tests).
+.IP "\fB\-O\fR, \fB\-\-tos\fR=\fIN\fR" 5
+.IX Item "-O, --tos=N"
+Set the typ of service flag (\s-1TOS\s0). \fIN\fR can be either decimal or hexadecimal
+(0xh) format.
+.IP "\fB\-\-print\-tos\fR" 5
+.IX Item "--print-tos"
+Displays the \s-1TOS\s0 value in the output. If \fBfping\fR cannot read the \s-1TOS\s0 value,
+\&\*(L"(\s-1TOS\s0 unknown)\*(R" is returned.
+IPv4 only, requires root privileges or cap_net_raw.
+.IP "\fB\-p\fR, \fB\-\-period\fR=\fI\s-1MSEC\s0\fR" 5
+.IX Item "-p, --period=MSEC"
+In looping or counting modes (\fB\-l\fR, \fB\-c\fR, or \fB\-C\fR), this parameter sets
+the time in milliseconds that \fBfping\fR waits between successive packets to
+an individual target. Default is 1000 and minimum is 10.
+.IP "\fB\-q\fR, \fB\-\-quiet\fR" 5
+.IX Item "-q, --quiet"
+Quiet. Don't show per-probe results, but only the final summary. Also don't
+show \s-1ICMP\s0 error messages.
+.IP "\fB\-Q\fR, \fB\-\-squiet\fR=\fISECS[,cumulative]\fR" 5
+.IX Item "-Q, --squiet=SECS[,cumulative]"
+Like \fB\-q\fR, but additionally show interval summary results every \fI\s-1SECS\s0\fR
+seconds. With \fIcumulative\fR, show summary results since start instead of
+for the last interval, unless option \fB\-N\fR is used, too.
+.IP "\fB\-r\fR, \fB\-\-retry\fR=\fIN\fR" 5
+.IX Item "-r, --retry=N"
+Retry limit (default 3). This is the number of times an attempt at pinging
+a target will be made, not including the first try.
+.IP "\fB\-R\fR, \fB\-\-random\fR" 5
+.IX Item "-R, --random"
+Instead of using all-zeros as the packet data, generate random bytes.
+Use to defeat, e.g., link data compression.
+.IP "\fB\-s\fR, \fB\-\-stats\fR" 5
+.IX Item "-s, --stats"
+Print cumulative statistics upon exit.
+.IP "\fB\-S\fR, \fB\-\-src\fR=\fIaddr\fR" 5
+.IX Item "-S, --src=addr"
+Set source address.
+.IP "\fB\-t\fR, \fB\-\-timeout\fR=\fI\s-1MSEC\s0\fR" 5
+.IX Item "-t, --timeout=MSEC"
+Initial target timeout in milliseconds. In the default, non-loop mode, the
+default timeout is 500ms, and it represents the amount of time that \fBfping\fR
+waits for a response to its first request. Successive timeouts are multiplied
+by the backoff factor specified with \fB\-B\fR.
+.Sp
+In loop/count mode, the default timeout is automatically adjusted to match
+the \*(L"period\*(R" value (but not more than 2000ms). You can still adjust the timeout
+value with this option, if you wish to, but note that setting a value larger
+than \*(L"period\*(R" produces inconsistent results, because the timeout value can
+be respected only for the last ping.
+.Sp
+Also note that any received replies that are larger than the timeout value, will
+be discarded.
+.IP "\fB\-T\fR \fIn\fR" 5
+.IX Item "-T n"
+Ignored (for compatibility with fping 2.4).
+.IP "\fB\-u\fR, \fB\-\-unreach\fR" 5
+.IX Item "-u, --unreach"
+Show targets that are unreachable. (Options \fB\-c\fR and \fB\-C\fR override \fB\-u\fR.)
+.IP "\fB\-v\fR, \fB\-\-version\fR" 5
+.IX Item "-v, --version"
+Print \fBfping\fR version information.
+.IP "\fB\-x\fR, \fB\-\-reachable\fR=\fIN\fR" 5
+.IX Item "-x, --reachable=N"
+Given a list of hosts, this mode checks if number of reachable hosts is >= N
+and exits true in that case.
+.IP "\fB\-X\fR, \fB\-\-fast\-reachable\fR=\fIN\fR" 5
+.IX Item "-X, --fast-reachable=N"
+Given a list of hosts, this mode immediately exits true once N alive hosts
+have been found.
+.SH "EXAMPLES"
+.IX Header "EXAMPLES"
+Generate 20 pings to two hosts in ca. 1 second (i.e. one ping every 50 ms to
+each host), and report every ping \s-1RTT\s0 at the end:
+.PP
+.Vb 1
+\& $ fping \-\-quiet \-\-interval=1 \-\-vcount=20 \-\-period=50 127.0.0.1 127.0.0.2
+.Ve
+.SH "AUTHORS"
+.IX Header "AUTHORS"
+.IP "\(bu" 4
+Roland J. Schemers \s-1III,\s0 Stanford University, concept and versions 1.x
+.IP "\(bu" 4
+\&\s-1RL\s0 \*(L"Bob\*(R" Morgan, Stanford University, versions 2.x
+.IP "\(bu" 4
+David Papp, versions 2.3x and up
+.IP "\(bu" 4
+David Schweikert, versions 3.0 and up
+.PP
+\&\fBfping website: <http://www.fping.org>\fR
+.SH "DIAGNOSTICS"
+.IX Header "DIAGNOSTICS"
+Exit status is 0 if all the hosts (or the number of hosts specified with \fB\-x\fR
+or \fB\-X\fR) are reachable, 1 if some (or too many with \fB\-x\fR or \fB\-X\fR) hosts
+were unreachable, 2 if any \s-1IP\s0 addresses were not found, 3 for invalid command
+line arguments, and 4 for a system call failure.
+.SH "RESTRICTIONS"
+.IX Header "RESTRICTIONS"
+The number of addresses that can be generated using the \f(CW\*(C`\-g\*(C'\fR, \f(CW\*(C`\-\-generate\*(C'\fR
+option is limited to 131070 (the number of host addresses in one 15\-bit IPv4
+prefix).
+.PP
+If fping was configured with \f(CW\*(C`\-\-enable\-safe\-limits\*(C'\fR, the following values are
+not allowed for non-root users:
+.IP "\(bu" 4
+\&\fB\-i\fR \fIn\fR, where \fIn\fR < 1 msec
+.IP "\(bu" 4
+\&\fB\-p\fR \fIn\fR, where \fIn\fR < 10 msec
+.SH "SEE ALSO"
+.IX Header "SEE ALSO"
+\&\f(CWping(8)\fR
--- /dev/null
+=head1 NAME
+
+fping - send ICMP ECHO_REQUEST packets to network hosts
+
+=head1 SYNOPSIS
+
+B<fping> [ I<options> ] [ I<systems...> ]
+
+=head1 DESCRIPTION
+
+B<fping> is a program like B<ping> which uses the Internet Control Message
+Protocol (ICMP) echo request to determine if a target host is responding.
+B<fping> differs from B<ping> in that you can specify any number of targets on the
+command line, or specify a file containing the lists of targets to ping.
+Instead of sending to one target until it times out or replies, B<fping> will
+send out a ping packet and move on to the next target in a round-robin fashion.
+In the default mode, if a target replies, it is noted and removed from the list
+of targets to check; if a target does not respond within a certain time limit
+and/or retry limit it is designated as unreachable. B<fping> also supports
+sending a specified number of pings to a target, or looping indefinitely (as in
+B<ping> ). Unlike B<ping>, B<fping> is meant to be used in scripts, so its
+output is designed to be easy to parse. Current statistics can be obtained without
+termination of process with signal SIGQUIT (^\ from the keyboard on most systems).
+
+=head1 OPTIONS
+
+=over 5
+
+=item B<-4>, B<--ipv4>
+
+Restrict name resolution and IPs to IPv4 addresses.
+
+=item B<-6>, B<--ipv6>
+
+Restrict name resolution and IPs to IPv6 addresses.
+
+=item B<-a>, B<--alive>
+
+Show systems that are alive. (Options B<-c> and B<-C> override B<-a>.)
+
+=item B<-A>, B<--addr>
+
+Display targets by address rather than DNS name. Combined with -d, the output
+will be both the ip and (if available) the hostname.
+
+=item B<-b>, B<--size>=I<BYTES>
+
+Number of bytes of ping data to send. The minimum size (normally 12) allows
+room for the data that B<fping> needs to do its work (sequence number,
+timestamp). The reported received data size includes the IP header (normally
+20 bytes) and ICMP header (8 bytes), so the minimum total size is 40 bytes.
+Default is 56, as in B<ping>. Maximum is the theoretical maximum IP datagram
+size (64K), though most systems limit this to a smaller, system-dependent
+number. Cannot be used together with B<--icmp-timestamp>.
+
+=item B<-B>, B<--backoff>=I<N>
+
+Backoff factor. In the default mode, B<fping> sends several requests to a
+target before giving up, waiting longer for a reply on each successive request.
+This parameter is the value by which the wait time (B<-t>) is multiplied on each
+successive request; it must be entered as a floating-point number (x.y). The
+default is 1.5.
+
+=item B<-c>, B<--count>=I<N>
+
+Number of request packets to send to each target. In this mode, a line is
+displayed for each received response (this can suppressed with B<-q> or B<-Q>).
+Also, statistics about responses for each target are displayed when all
+requests have been sent (or when interrupted). This option overrides B<-a>
+or B<-u>.
+
+=item B<-C>, B<--vcount>=I<N>
+
+Similar to B<-c>, but the per-target statistics are displayed in a format
+designed for automated response-time statistics gathering. For example:
+
+ $ fping -C 5 -q somehost
+ somehost : 91.7 37.0 29.2 - 36.8
+
+shows the response time in milliseconds for each of the five requests, with the
+C<-> indicating that no response was received to the fourth request. This
+option overrides B<-a> or B<-u>.
+
+=item B<--check-source>
+
+Discard Echo replies that are sourced from a different address than the target
+address. This avoids spurious reachability results on busy monitoring systems
+where two B<fping> instances with the same lower 16 bits of the process ID may
+be running at the same time.
+
+=item B<-d>, B<--rdns>
+
+Use DNS to lookup address of ping target. This allows you to give fping
+a list of IP addresses as input and print hostnames in the output. This is similar
+to option B<-n>/B<--name>, but will force a reverse-DNS lookup even if you give
+hostnames as target (NAME->IP->NAME).
+
+=item B<-D>, B<--timestamp>
+
+Add Unix timestamps in front of output lines generated with in looping or counting
+modes (B<-l>, B<-c>, or B<-C>).
+
+Subcommand: B<--timestamp-format>=I<ctime|iso|rfc3339>
+
+Allow to change the timestamp format of the B<-D> option to the following format types.
+
+I<ctime> = "%c" (Example: Mon Jun 10 07:50:00 2024)
+
+I<iso> = "%Y-%m-%dT%T%z" (Example: 2024-06-10T07:50:00+0200)
+
+I<rfc3339> = "%Y-%m-%d %H:%M:%S" (Example: 2024-06-10 07:50:00)
+
+=item B<-e>, B<--elapsed>
+
+Show elapsed (round-trip) time of packets.
+
+=item B<-f>, B<--file>
+
+Read list of targets from a file.
+
+=item B<-g>, B<--generate> I<addr/mask>
+
+Generate a target list from a supplied IP netmask, or a starting and ending IP.
+Specify the netmask or start/end in the targets portion of the command line. If
+a network with netmask is given, the network and broadcast addresses will be
+excluded. ex. To ping the network 192.168.1.0/24, the specified command line
+could look like either:
+
+ $ fping -g 192.168.1.0/24
+
+or
+
+ $ fping -g 192.168.1.1 192.168.1.254
+
+=item B<-h>, B<--help>
+
+Print usage message.
+
+=item B<-H>, B<--ttl>=I<N>
+
+Set the IP TTL field (time to live hops).
+
+=item B<--print-ttl>
+
+Displays the IPv4 TTL value from the IP Header in the output.
+If B<fping> cannot read the TTL value, "(TTL unknown)" is returned.
+IPv4 only, requires root privileges or cap_net_raw.
+
+=item B<-i>, B<--interval>=I<MSEC>
+
+The minimum amount of time (in milliseconds) between sending a ping packet
+to any target (default is 10, minimum is 1).
+
+=item B<-I>, B<--iface>=I<IFACE>
+
+Set the interface (requires SO_BINDTODEVICE support).
+
+=item B<--icmp-timestamp>
+
+Send ICMP timestamp requests (ICMP type 13) instead of ICMP Echo requests.
+Print ICMP timestamps for originate, receive, and transmit, together with
+the local receive time in the same format, in addition to normal output.
+Cannot be used together with B<-b> because ICMP timestamp messages have a fixed size.
+IPv4 only, requires root privileges or cap_net_raw.
+
+=item B<-k>, B<--fwmark>=I<FWMARK>
+
+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
+Ctrl-C; statistics about responses for each target are then displayed.
+
+=item B<-m>, B<--all>
+
+Send pings to each of a target host's multiple IP addresses (use of option '-A'
+is recommended).
+
+=item B<-M>, B<--dontfrag>
+
+Set the "Don't Fragment" bit in the IP header (used to determine/test the MTU).
+
+=item B<-n>, B<--name>
+
+If targets are specified as IP addresses, do a reverse-DNS lookup on them
+to print hostnames in the output.
+
+=item B<-N>, B<--netdata>
+
+Format output for netdata (-l -Q are required). See: L<https://netdata.cloud/>
+
+=item B<-o>, B<--outage>
+
+Calculate "outage time" based on the number of lost pings and the interval used (useful for network convergence tests).
+
+=item B<-O>, B<--tos>=I<N>
+
+Set the typ of service flag (TOS). I<N> can be either decimal or hexadecimal
+(0xh) format.
+
+=item B<--print-tos>
+
+Displays the TOS value in the output. If B<fping> cannot read the TOS value,
+"(TOS unknown)" is returned.
+IPv4 only, requires root privileges or cap_net_raw.
+
+=item B<-p>, B<--period>=I<MSEC>
+
+In looping or counting modes (B<-l>, B<-c>, or B<-C>), this parameter sets
+the time in milliseconds that B<fping> waits between successive packets to
+an individual target. Default is 1000 and minimum is 10.
+
+=item B<-q>, B<--quiet>
+
+Quiet. Don't show per-probe results, but only the final summary. Also don't
+show ICMP error messages.
+
+=item B<-Q>, B<--squiet>=I<SECS[,cumulative]>
+
+Like B<-q>, but additionally show interval summary results every I<SECS>
+seconds. With I<cumulative>, show summary results since start instead of
+for the last interval, unless option B<-N> is used, too.
+
+=item B<-r>, B<--retry>=I<N>
+
+Retry limit (default 3). This is the number of times an attempt at pinging
+a target will be made, not including the first try.
+
+=item B<-R>, B<--random>
+
+Instead of using all-zeros as the packet data, generate random bytes.
+Use to defeat, e.g., link data compression.
+
+=item B<-s>, B<--stats>
+
+Print cumulative statistics upon exit.
+
+=item B<-S>, B<--src>=I<addr>
+
+Set source address.
+
+=item B<-t>, B<--timeout>=I<MSEC>
+
+Initial target timeout in milliseconds. In the default, non-loop mode, the
+default timeout is 500ms, and it represents the amount of time that B<fping>
+waits for a response to its first request. Successive timeouts are multiplied
+by the backoff factor specified with B<-B>.
+
+In loop/count mode, the default timeout is automatically adjusted to match
+the "period" value (but not more than 2000ms). You can still adjust the timeout
+value with this option, if you wish to, but note that setting a value larger
+than "period" produces inconsistent results, because the timeout value can
+be respected only for the last ping.
+
+Also note that any received replies that are larger than the timeout value, will
+be discarded.
+
+=item B<-T> I<n>
+
+Ignored (for compatibility with fping 2.4).
+
+=item B<-u>, B<--unreach>
+
+Show targets that are unreachable. (Options B<-c> and B<-C> override B<-u>.)
+
+=item B<-v>, B<--version>
+
+Print B<fping> version information.
+
+=item B<-x>, B<--reachable>=I<N>
+
+Given a list of hosts, this mode checks if number of reachable hosts is >= N
+and exits true in that case.
+
+=item B<-X>, B<--fast-reachable>=I<N>
+
+Given a list of hosts, this mode immediately exits true once N alive hosts
+have been found.
+
+=back
+
+=head1 EXAMPLES
+
+Generate 20 pings to two hosts in ca. 1 second (i.e. one ping every 50 ms to
+each host), and report every ping RTT at the end:
+
+ $ fping --quiet --interval=1 --vcount=20 --period=50 127.0.0.1 127.0.0.2
+
+=head1 AUTHORS
+
+=over 4
+
+=item *
+
+Roland J. Schemers III, Stanford University, concept and versions 1.x
+
+=item *
+
+RL "Bob" Morgan, Stanford University, versions 2.x
+
+=item *
+
+David Papp, versions 2.3x and up
+
+=item *
+
+David Schweikert, versions 3.0 and up
+
+=back
+
+B<fping website: L<http://www.fping.org>>
+
+=head1 DIAGNOSTICS
+
+Exit status is 0 if all the hosts (or the number of hosts specified with B<-x>
+or B<-X>) are reachable, 1 if some (or too many with B<-x> or B<-X>) hosts
+were unreachable, 2 if any IP addresses were not found, 3 for invalid command
+line arguments, and 4 for a system call failure.
+
+=head1 RESTRICTIONS
+
+The number of addresses that can be generated using the C<-g>, C<--generate>
+option is limited to 131070 (the number of host addresses in one 15-bit IPv4
+prefix).
+
+If fping was configured with C<--enable-safe-limits>, the following values are
+not allowed for non-root users:
+
+=over 4
+
+=item *
+
+B<-i> I<n>, where I<n> < 1 msec
+
+=item *
+
+B<-p> I<n>, where I<n> < 10 msec
+
+=back
+
+=head1 SEE ALSO
+
+C<ping(8)>
--- /dev/null
+AM_CFLAGS = -Wall -Wextra -Wno-sign-compare
+
+sbin_PROGRAMS = fping
+
+fping_SOURCES = fping.c seqmap.c socket4.c fping.h options.h seqmap.h optparse.c optparse.h
+fping_DEPENDENCIES = ../config.h
+
+if IPV6
+fping_SOURCES += socket6.c
+fping_CFLAGS = $(AM_CFLAGS) -DIPV6
+endif
--- /dev/null
+/*
+ * fping: fast-ping, file-ping, favorite-ping, funky-ping
+ *
+ * Ping a list of target hosts in a round robin fashion.
+ * A better ping overall.
+ *
+ * fping website: http://www.fping.org
+ *
+ * Current maintainer of fping: David Schweikert
+ * Please send suggestions and patches to: david@schweikert.ch
+ *
+ *
+ * Original author: Roland Schemers <schemers@stanford.edu>
+ * IPv6 Support: Jeroen Massar <jeroen@unfix.org / jeroen@ipng.nl>
+ * Improved main loop: David Schweikert <david@schweikert.ch>
+ * Debian Merge, TOS settings: Tobi Oetiker <tobi@oetiker.ch>
+ * Bugfixes, byte order & senseful seq.-numbers: Stephan Fuhrmann (stephan.fuhrmann AT 1und1.de)
+ *
+ *
+ * Redistribution and use in source and binary forms are permitted
+ * provided that the above copyright notice and this paragraph are
+ * duplicated in all such forms and that any documentation,
+ * advertising materials, and other materials related to such
+ * distribution and use acknowledge that the software was developed
+ * by Stanford University. The name of the University may not be used
+ * to endorse or promote products derived from this software without
+ * specific prior written permission.
+ * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED
+ * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE.
+ */
+
+#ifdef __cplusplus
+extern "C" {
+#endif /* __cplusplus */
+
+#include "fping.h"
+#include "config.h"
+#include "options.h"
+#include "optparse.h"
+
+#include <errno.h>
+#include <inttypes.h>
+#include <signal.h>
+#include <stdarg.h>
+#include <stdint.h>
+#include <stdio.h>
+#include <time.h>
+
+#include "seqmap.h"
+
+#ifdef HAVE_UNISTD_H
+#include <unistd.h>
+#endif /* HAVE_UNISTD_H */
+
+#ifdef HAVE_STDLIB_H
+#include <stdlib.h>
+#endif /* HAVE_STDLIB_H */
+
+#include <stddef.h>
+#include <string.h>
+
+#include <sys/socket.h>
+#include <sys/time.h>
+#include <sys/types.h>
+
+#if HAVE_SYS_FILE_H
+#include <sys/file.h>
+#endif /* HAVE_SYS_FILE_H */
+
+#ifdef IPV6
+#include <netinet/icmp6.h>
+#endif
+#include <netinet/in_systm.h>
+
+#include <netinet/ip.h>
+#include <netinet/ip_icmp.h>
+
+#include <arpa/inet.h>
+#include <ctype.h>
+#include <netdb.h>
+
+#include <sys/select.h>
+
+/*** compatibility ***/
+
+/* Mac OS X's getaddrinfo() does not fail if we use an invalid combination,
+ * e.g. AF_INET6 with "127.0.0.1". If we pass AI_UNUSABLE to flags, it behaves
+ * like other platforms. But AI_UNUSABLE isn't available on other platforms,
+ * and we can safely use 0 for flags instead.
+ */
+#ifndef AI_UNUSABLE
+#define AI_UNUSABLE 0
+#endif
+
+/* MSG_TRUNC available on Linux kernel 2.2+, makes recvmsg return the full
+ * length of the raw packet received, even if the buffer is smaller */
+#ifndef MSG_TRUNC
+#define MSG_TRUNC 0
+#define RECV_BUFSIZE 4096
+#else
+#define RECV_BUFSIZE 128
+#endif
+
+/*** externals ***/
+
+extern char *optarg;
+extern int optind, opterr;
+#ifndef h_errno
+extern int h_errno;
+#endif
+
+#ifdef __cplusplus
+}
+#endif /* __cplusplus */
+
+/*** Constants ***/
+
+/* CLOCK_MONTONIC starts under macOS, OpenBSD and FreeBSD with undefined positive point and can not be use
+ * see github PR #217
+ * The configure script detect the predefined operating systems an set CLOCK_REALTIME using over ONLY_CLOCK_REALTIME variable
+ */
+#if HAVE_SO_TIMESTAMPNS || ONLY_CLOCK_REALTIME
+#define CLOCKID CLOCK_REALTIME
+#endif
+
+#if !defined(CLOCKID)
+#if defined(CLOCK_MONOTONIC)
+#define CLOCKID CLOCK_MONOTONIC
+#else
+#define CLOCKID CLOCK_REALTIME
+#endif
+#endif
+
+/*** Ping packet defines ***/
+
+#define MAX_IP_PACKET 65535 /* (theoretical) max IPv4 packet size */
+#define SIZE_IP_HDR 20 /* min IPv4 header size */
+#define SIZE_ICMP_HDR 8 /* from ip_icmp.h */
+#define MAX_PING_DATA (MAX_IP_PACKET - SIZE_IP_HDR - SIZE_ICMP_HDR)
+
+#define MAX_GENERATE 131070 /* maximum number of hosts that -g can generate */
+
+/* sized so as to be like traditional ping */
+#define DEFAULT_PING_DATA_SIZE 56
+
+/* ICMP Timestamp has a fixed payload size of 12 bytes */
+#define ICMP_TIMESTAMP_DATA_SIZE 12
+
+#ifdef FPING_SAFE_LIMITS
+#define MIN_INTERVAL_MS 1 /* in millisec */
+#define MIN_PERHOST_INTERVAL_MS 10 /* in millisec */
+#else
+#define MIN_INTERVAL_MS 0
+/* Set a very low limit for the per-host interval, even if safe limits are
+ * disabled, so that the memory allocation of the event storage is not
+ * unreasonably high. 0.001 ms would mean in theory at least 592 mbps of data
+ * sent to a single host, which probably doesn't make sense in any scenario. */
+#define MIN_PERHOST_INTERVAL_MS 0.001
+#endif
+
+/* response time array flags */
+#define RESP_WAITING -1
+#define RESP_UNUSED -2
+#define RESP_ERROR -3
+#define RESP_TIMEOUT -4
+
+/* debugging flags */
+#if defined(DEBUG) || defined(_DEBUG)
+#define DBG_TRACE 1
+#define DBG_SENT_TIMES 2
+#define DBG_RANDOM_LOSE_FEW 4
+#define DBG_RANDOM_LOSE_MANY 8
+#define DBG_PRINT_PER_SYSTEM 16
+#define DBG_REPORT_ALL_RTTS 32
+#endif /* DEBUG || _DEBUG */
+
+/* Long names for ICMP packet types */
+#define ICMP_TYPE_STR_MAX 18
+char *icmp_type_str[19] = {
+ "ICMP Echo Reply", /* 0 */
+ "",
+ "",
+ "ICMP Unreachable", /* 3 */
+ "ICMP Source Quench", /* 4 */
+ "ICMP Redirect", /* 5 */
+ "",
+ "",
+ "ICMP Echo", /* 8 */
+ "",
+ "",
+ "ICMP Time Exceeded", /* 11 */
+ "ICMP Parameter Problem", /* 12 */
+ "ICMP Timestamp Request", /* 13 */
+ "ICMP Timestamp Reply", /* 14 */
+ "ICMP Information Request", /* 15 */
+ "ICMP Information Reply", /* 16 */
+ "ICMP Mask Request", /* 17 */
+ "ICMP Mask Reply" /* 18 */
+};
+
+char *icmp_unreach_str[16] = {
+ "ICMP Network Unreachable", /* 0 */
+ "ICMP Host Unreachable", /* 1 */
+ "ICMP Protocol Unreachable", /* 2 */
+ "ICMP Port Unreachable", /* 3 */
+ "ICMP Unreachable (Fragmentation Needed)", /* 4 */
+ "ICMP Unreachable (Source Route Failed)", /* 5 */
+ "ICMP Unreachable (Destination Network Unknown)", /* 6 */
+ "ICMP Unreachable (Destination Host Unknown)", /* 7 */
+ "ICMP Unreachable (Source Host Isolated)", /* 8 */
+ "ICMP Unreachable (Communication with Network Prohibited)", /* 9 */
+ "ICMP Unreachable (Communication with Host Prohibited)", /* 10 */
+ "ICMP Unreachable (Network Unreachable For Type Of Service)", /* 11 */
+ "ICMP Unreachable (Host Unreachable For Type Of Service)", /* 12 */
+ "ICMP Unreachable (Communication Administratively Prohibited)", /* 13 */
+ "ICMP Unreachable (Host Precedence Violation)", /* 14 */
+ "ICMP Unreachable (Precedence cutoff in effect)" /* 15 */
+};
+
+#define ICMP_UNREACH_MAXTYPE 15
+
+struct event;
+typedef struct host_entry {
+ int i; /* index into array */
+ char *name; /* name as given by user */
+ char *host; /* text description of host */
+ struct sockaddr_storage saddr; /* internet address */
+ socklen_t saddr_len;
+ int64_t timeout; /* time to wait for response */
+ int64_t last_send_time; /* time of last packet sent */
+ int num_sent; /* number of ping packets sent (for statistics) */
+ int num_recv; /* number of pings received (duplicates ignored) */
+ int num_recv_total; /* number of pings received, including duplicates */
+ int64_t max_reply; /* longest response time */
+ int64_t min_reply; /* shortest response time */
+ int64_t total_time; /* sum of response times */
+ /* _i -> splits (reset on every report interval) */
+ int num_sent_i; /* number of ping packets sent */
+ int num_recv_i; /* number of pings received */
+ int64_t max_reply_i; /* longest response time */
+ int64_t min_reply_i; /* shortest response time */
+ int64_t total_time_i; /* sum of response times */
+ int64_t *resp_times; /* individual response times */
+
+ /* to avoid allocating two struct events each time that we send a ping, we
+ * preallocate here two struct events for each ping that we might send for
+ * this host. */
+ struct event *event_storage_ping;
+ struct event *event_storage_timeout;
+} HOST_ENTRY;
+
+int event_storage_count; /* how many events can be stored in host_entry->event_storage_xxx */
+
+/* basic algorithm to ensure that we have correct data at all times:
+ *
+ * 1. when a ping is sent:
+ * - two events get added into event_queue:
+ * - t+PERIOD: ping event
+ * - t+TIMEOUT: timeout event
+ *
+ * 2. when a ping is received:
+ * - record statistics (increase num_sent and num_received)
+ * - remove timeout event (we store the event in seqmap, so that we can retrieve it when the response is received)
+ *
+ * 3. when a timeout happens:
+ * - record statistics (increase num_sent only)
+ */
+
+#define EV_TYPE_PING 1
+#define EV_TYPE_TIMEOUT 2
+
+struct event {
+ struct event *ev_prev;
+ struct event *ev_next;
+ int64_t ev_time;
+ struct host_entry *host;
+ int ping_index;
+};
+
+struct event_queue {
+ struct event *first;
+ struct event *last;
+};
+
+/*** globals ***/
+
+HOST_ENTRY **table = NULL; /* array of pointers to items in the list */
+
+/* we keep two separate queues: a ping queue, for when the next ping should be
+ * sent, and a timeout queue. the reason for having two separate queues is that
+ * the ping period and the timeout value are different, so if we put them in
+ * the same event queue, we would need to scan many more entries when inserting
+ * into the sorted list.
+ */
+struct event_queue event_queue_ping;
+struct event_queue event_queue_timeout;
+
+char *prog;
+int ident4 = 0; /* our icmp identity field */
+int ident6 = 0;
+int socket4 = -1;
+int socktype4 = -1;
+int using_sock_dgram4 = 0;
+#ifndef IPV6
+int hints_ai_family = AF_INET;
+#else
+int socket6 = -1;
+int socktype6 = -1;
+int hints_ai_family = AF_UNSPEC;
+#endif
+
+volatile sig_atomic_t status_snapshot = 0;
+volatile sig_atomic_t finish_requested = 0;
+
+unsigned int debugging = 0;
+
+/* all time-related values are int64_t nanoseconds */
+unsigned int retry = DEFAULT_RETRY;
+int64_t timeout = (int64_t)DEFAULT_TIMEOUT * 1000000;
+int64_t interval = (int64_t)DEFAULT_INTERVAL * 1000000;
+int64_t perhost_interval = (int64_t)DEFAULT_PERHOST_INTERVAL * 1000000;
+float backoff = DEFAULT_BACKOFF_FACTOR;
+unsigned int ping_data_size = DEFAULT_PING_DATA_SIZE;
+unsigned int count = 1, min_reachable = 0;
+unsigned int trials;
+int64_t report_interval = 0;
+unsigned int ttl = 0;
+int src_addr_set = 0;
+struct in_addr src_addr;
+#ifdef IPV6
+int src_addr6_set = 0;
+struct in6_addr src_addr6;
+#endif
+
+/* global stats */
+int64_t max_reply = 0;
+int64_t min_reply = 0;
+int64_t total_replies = 0;
+int64_t sum_replies = 0;
+int max_hostname_len = 0;
+int num_hosts = 0; /* total number of hosts */
+int num_alive = 0, /* total number alive */
+ num_unreachable = 0, /* total number unreachable */
+ num_noaddress = 0; /* total number of addresses not found */
+int num_timeout = 0, /* number of times select timed out */
+ num_pingsent = 0, /* total pings sent */
+ num_pingreceived = 0, /* total pings received */
+ num_othericmprcvd = 0; /* total non-echo-reply ICMP received */
+
+struct timespec current_time; /* current time (pseudo) */
+int64_t current_time_ns;
+int64_t start_time;
+int64_t end_time;
+int64_t last_send_time; /* time last ping was sent */
+int64_t next_report_time; /* time next -Q report is expected */
+
+/* switches */
+int generate_flag = 0; /* flag for IP list generation */
+int verbose_flag, quiet_flag, stats_flag, unreachable_flag, alive_flag;
+int elapsed_flag, version_flag, count_flag, loop_flag, netdata_flag;
+int per_recv_flag, report_all_rtts_flag, name_flag, addr_flag, backoff_flag, rdns_flag;
+int multif_flag, timeout_flag, fast_reachable;
+int outage_flag = 0;
+int timestamp_flag = 0;
+int timestamp_format_flag = 0;
+int random_data_flag = 0;
+int cumulative_stats_flag = 0;
+int check_source_flag = 0;
+int icmp_request_typ = 0;
+int print_tos_flag = 0;
+int print_ttl_flag = 0;
+int size_flag = 0;
+#if defined(DEBUG) || defined(_DEBUG)
+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 ***/
+
+void add_name(char *name);
+void add_addr(char *name, char *host, struct sockaddr *ipaddr, socklen_t ipaddr_len);
+char *na_cat(char *name, struct in_addr ipaddr);
+void crash_and_burn(char *message);
+void errno_crash_and_burn(char *message);
+char *get_host_by_address(struct in_addr in);
+int send_ping(HOST_ENTRY *h, int index);
+void usage(int);
+int wait_for_reply(int64_t);
+void print_per_system_stats(void);
+void print_per_system_splits(void);
+void stats_reset_interval(HOST_ENTRY *h);
+void print_netdata(void);
+void print_global_stats(void);
+void main_loop();
+void signal_handler(int);
+void finish();
+const char *sprint_tm(int64_t t);
+void ev_enqueue(struct event_queue *queue, struct event *event);
+struct event *ev_dequeue(struct event_queue *queue);
+void ev_remove(struct event_queue *queue, struct event *event);
+void add_cidr(char *);
+void add_range(char *, char *);
+void add_addr_range_ipv4(unsigned long, unsigned long);
+void print_warning(char *fmt, ...);
+int addr_cmp(struct sockaddr *a, struct sockaddr *b);
+void host_add_ping_event(HOST_ENTRY *h, int index, int64_t ev_time);
+void host_add_timeout_event(HOST_ENTRY *h, int index, int64_t ev_time);
+struct event *host_get_timeout_event(HOST_ENTRY *h, int index);
+void stats_add(HOST_ENTRY *h, int index, int success, int64_t latency);
+void update_current_time();
+void print_timestamp_format(int64_t current_time_ns, int timestamp_format);
+static uint32_t ms_since_midnight_utc(int64_t time_val);
+
+/************************************************************
+
+ Function: p_setsockopt
+
+*************************************************************
+
+ Inputs: p_uid: privileged uid. Others as per setsockopt(2)
+
+ Description:
+
+ Elevates privileges to p_uid when required, calls
+ setsockopt, and drops privileges back.
+
+************************************************************/
+
+int p_setsockopt(uid_t p_uid, int sockfd, int level, int optname,
+ const void *optval, socklen_t optlen)
+{
+ const uid_t saved_uid = geteuid();
+ int res;
+
+ if (p_uid != saved_uid && seteuid(p_uid)) {
+ perror("cannot elevate privileges for setsockopt");
+ }
+
+ res = setsockopt(sockfd, level, optname, optval, optlen);
+
+ if (p_uid != saved_uid && seteuid(saved_uid)) {
+ perror("fatal error: could not drop privileges after setsockopt");
+ /* continuing would be a security hole */
+ exit(4);
+ }
+
+ return res;
+}
+
+/************************************************************
+
+ Function: main
+
+*************************************************************
+
+ Inputs: int argc, char** argv
+
+ Description:
+
+ Main program entry point
+
+************************************************************/
+
+int main(int argc, char **argv)
+{
+/* Debug: CPU Performance */
+#if defined(DEBUG) || defined(_DEBUG)
+ clock_t perf_cpu_start, perf_cpu_end;
+ double perf_cpu_time_used;
+ perf_cpu_start = clock();
+#endif /* DEBUG || _DEBUG */
+
+ int c;
+ const uid_t suid = geteuid();
+ int tos = 0;
+ struct optparse optparse_state;
+#ifdef USE_SIGACTION
+ struct sigaction act;
+#endif
+
+ /* pre-parse -h/--help, so that we also can output help information
+ * without trying to open the socket, which might fail */
+ prog = argv[0];
+ if (argc == 2 && (strcmp(argv[1], "-h") == 0 || strcmp(argv[1], "--help") == 0)) {
+ usage(0);
+ }
+
+ socket4 = open_ping_socket_ipv4(&socktype4);
+#ifdef __linux__
+ /* We only treat SOCK_DGRAM differently on Linux, where the IPv4 header
+ * structure is missing in the message.
+ */
+ using_sock_dgram4 = (socktype4 == SOCK_DGRAM);
+#endif
+
+#ifdef IPV6
+ socket6 = open_ping_socket_ipv6(&socktype6);
+ /* if called (sym-linked) via 'fping6', imply '-6'
+ * for backward compatibility */
+ if (strstr(prog, "fping6")) {
+ hints_ai_family = AF_INET6;
+ }
+#endif
+
+ memset(&src_addr, 0, sizeof(src_addr));
+#ifdef IPV6
+ memset(&src_addr6, 0, sizeof(src_addr6));
+#endif
+
+ if (!suid && suid != getuid()) {
+ /* *temporarily* drop privileges */
+ if (seteuid(getuid()) == -1)
+ perror("cannot setuid");
+ }
+
+ optparse_init(&optparse_state, argv);
+ ident4 = ident6 = htons(getpid() & 0xFFFF);
+ verbose_flag = 1;
+ backoff_flag = 1;
+ opterr = 1;
+
+ /* get command line options */
+
+ struct optparse_long longopts[] = {
+ { "ipv4", '4', OPTPARSE_NONE },
+ { "ipv6", '6', OPTPARSE_NONE },
+ { "alive", 'a', OPTPARSE_NONE },
+ { "addr", 'A', OPTPARSE_NONE },
+ { "size", 'b', OPTPARSE_REQUIRED },
+ { "backoff", 'B', OPTPARSE_REQUIRED },
+ { "count", 'c', OPTPARSE_REQUIRED },
+ { "vcount", 'C', OPTPARSE_REQUIRED },
+ { "rdns", 'd', OPTPARSE_NONE },
+ { "timestamp", 'D', OPTPARSE_NONE },
+ { "timestamp-format", '0', OPTPARSE_REQUIRED },
+ { "elapsed", 'e', OPTPARSE_NONE },
+ { "file", 'f', OPTPARSE_REQUIRED },
+ { "generate", 'g', OPTPARSE_NONE },
+ { "help", 'h', OPTPARSE_NONE },
+ { "ttl", 'H', OPTPARSE_REQUIRED },
+ { "interval", 'i', OPTPARSE_REQUIRED },
+ { "iface", 'I', OPTPARSE_REQUIRED },
+ { "icmp-timestamp", '0', OPTPARSE_NONE },
+#ifdef SO_MARK
+ { "fwmark", 'k', OPTPARSE_REQUIRED },
+#endif
+ { "loop", 'l', OPTPARSE_NONE },
+ { "all", 'm', OPTPARSE_NONE },
+ { "dontfrag", 'M', OPTPARSE_NONE },
+ { "name", 'n', OPTPARSE_NONE },
+ { "netdata", 'N', OPTPARSE_NONE },
+ { "outage", 'o', OPTPARSE_NONE },
+ { "tos", 'O', OPTPARSE_REQUIRED },
+ { "period", 'p', OPTPARSE_REQUIRED },
+ { "quiet", 'q', OPTPARSE_NONE },
+ { "squiet", 'Q', OPTPARSE_REQUIRED },
+ { "retry", 'r', OPTPARSE_REQUIRED },
+ { "random", 'R', OPTPARSE_NONE },
+ { "stats", 's', OPTPARSE_NONE },
+ { "src", 'S', OPTPARSE_REQUIRED },
+ { "timeout", 't', OPTPARSE_REQUIRED },
+ { NULL, 'T', OPTPARSE_REQUIRED },
+ { "unreach", 'u', OPTPARSE_NONE },
+ { "version", 'v', OPTPARSE_NONE },
+ { "reachable", 'x', OPTPARSE_REQUIRED },
+ { "fast-reachable", 'X', OPTPARSE_REQUIRED },
+ { "check-source", '0', OPTPARSE_NONE },
+ { "print-tos", '0', OPTPARSE_NONE },
+ { "print-ttl", '0', OPTPARSE_NONE },
+#if defined(DEBUG) || defined(_DEBUG)
+ { NULL, 'z', OPTPARSE_REQUIRED },
+#endif
+ { 0, 0, 0 }
+ };
+
+ float opt_value_float;
+ while ((c = optparse_long(&optparse_state, longopts, NULL)) != EOF) {
+ switch (c) {
+ case '0':
+ if(strstr(optparse_state.optlongname, "timestamp-format") != NULL) {
+ if(strcmp(optparse_state.optarg, "ctime") == 0) {
+ timestamp_format_flag = 1;
+ }else if(strcmp(optparse_state.optarg, "iso") == 0) {
+ timestamp_format_flag = 2;
+ }else if(strcmp(optparse_state.optarg, "rfc3339") == 0) {
+ timestamp_format_flag = 3;
+ }else{
+ usage(1);
+ }
+ } else if (strstr(optparse_state.optlongname, "check-source") != NULL) {
+ check_source_flag = 1;
+ } else if (strstr(optparse_state.optlongname, "icmp-timestamp") != NULL) {
+#ifdef IPV6
+ if (hints_ai_family != AF_UNSPEC && hints_ai_family != AF_INET) {
+ fprintf(stderr, "%s: ICMP Timestamp is IPv4 only\n", prog);
+ exit(1);
+ }
+ hints_ai_family = AF_INET;
+#endif
+ icmp_request_typ = 13;
+ ping_data_size = ICMP_TIMESTAMP_DATA_SIZE;
+ } else if (strstr(optparse_state.optlongname, "print-tos") != NULL) {
+ print_tos_flag = 1;
+ } else if (strstr(optparse_state.optlongname, "print-ttl") != NULL) {
+ print_ttl_flag = 1;
+ } else {
+ usage(1);
+ }
+ break;
+ case '4':
+#ifdef IPV6
+ if (hints_ai_family != AF_UNSPEC && hints_ai_family != AF_INET) {
+ fprintf(stderr, "%s: can't specify both -4 and -6\n", prog);
+ exit(1);
+ }
+ hints_ai_family = AF_INET;
+#endif
+ break;
+ case '6':
+#ifdef IPV6
+ if (hints_ai_family != AF_UNSPEC && hints_ai_family != AF_INET6) {
+ fprintf(stderr, "%s: can't specify both -4 and -6\n", prog);
+ exit(1);
+ }
+ hints_ai_family = AF_INET6;
+#else
+ fprintf(stderr, "%s: IPv6 not supported by this binary\n", prog);
+ exit(1);
+#endif
+ break;
+ case 'M':
+#ifdef IP_MTU_DISCOVER
+ if (socket4 >= 0) {
+ int val = IP_PMTUDISC_DO;
+ if (setsockopt(socket4, IPPROTO_IP, IP_MTU_DISCOVER, &val, sizeof(val))) {
+ perror("setsockopt IP_MTU_DISCOVER");
+ }
+ }
+#ifdef IPV6
+ if (socket6 >= 0) {
+ int val = IPV6_PMTUDISC_DO;
+ if (setsockopt(socket6, IPPROTO_IPV6, IPV6_MTU_DISCOVER, &val, sizeof(val))) {
+ perror("setsockopt IPV6_MTU_DISCOVER");
+ }
+ }
+#endif
+#else
+ fprintf(stderr, "%s, -M option not supported on this platform\n", prog);
+ exit(1);
+#endif
+ break;
+
+ case 't':
+ if (sscanf(optparse_state.optarg, "%f", &opt_value_float) != 1)
+ usage(1);
+ if (opt_value_float < 0) {
+ usage(1);
+ }
+ timeout = opt_value_float * 1000000;
+ timeout_flag = 1;
+ break;
+
+ case 'r':
+ if (sscanf(optparse_state.optarg, "%u", &retry) != 1)
+ usage(1);
+ break;
+
+ case 'i':
+ if (sscanf(optparse_state.optarg, "%f", &opt_value_float) != 1)
+ usage(1);
+ if (opt_value_float < 0) {
+ usage(1);
+ }
+ interval = opt_value_float * 1000000;
+ break;
+
+ case 'p':
+ if (sscanf(optparse_state.optarg, "%f", &opt_value_float) != 1)
+ usage(1);
+ if (opt_value_float < 0) {
+ usage(1);
+ }
+ perhost_interval = opt_value_float * 1000000;
+
+ break;
+
+ case 'c':
+ if (!(count = (unsigned int)atoi(optparse_state.optarg)))
+ usage(1);
+
+ count_flag = 1;
+ break;
+
+ case 'C':
+ if (!(count = (unsigned int)atoi(optparse_state.optarg)))
+ usage(1);
+
+ count_flag = 1;
+ report_all_rtts_flag = 1;
+ break;
+
+ case 'b':
+ if (sscanf(optparse_state.optarg, "%u", &ping_data_size) != 1)
+ usage(1);
+ size_flag = 1;
+ break;
+
+ case 'h':
+ usage(0);
+ break;
+
+ case 'q':
+ verbose_flag = 0;
+ quiet_flag = 1;
+ break;
+
+ case 'Q':
+ verbose_flag = 0;
+ quiet_flag = 1;
+ if (sscanf(optparse_state.optarg, "%f", &opt_value_float) != 1)
+ usage(1);
+ if (opt_value_float < 0) {
+ usage(1);
+ }
+ report_interval = opt_value_float * 1e9;
+
+ /* recognize keyword(s) after number, ignore everything else */
+ {
+ char *comma = strchr(optparse_state.optarg, ',');
+ if ((comma != NULL) && (strcmp(++comma, "cumulative") == 0)) {
+ cumulative_stats_flag = 1;
+ }
+ }
+
+ break;
+
+ case 'e':
+ elapsed_flag = 1;
+ break;
+
+ case 'm':
+ multif_flag = 1;
+ break;
+
+ case 'N':
+ netdata_flag = 1;
+ break;
+
+ case 'n':
+ name_flag = 1;
+ if (rdns_flag) {
+ fprintf(stderr, "%s: use either one of -d or -n\n", prog);
+ exit(1);
+ }
+ break;
+
+ case 'd':
+ rdns_flag = 1;
+ if (name_flag) {
+ fprintf(stderr, "%s: use either one of -d or -n\n", prog);
+ exit(1);
+ }
+ break;
+
+ case 'A':
+ addr_flag = 1;
+ break;
+
+ case 'B':
+ if (!(backoff = atof(optparse_state.optarg)))
+ usage(1);
+
+ break;
+
+ case 's':
+ stats_flag = 1;
+ break;
+
+ case 'D':
+ timestamp_flag = 1;
+ break;
+
+ case 'R':
+ random_data_flag = 1;
+ break;
+
+ case 'l':
+ loop_flag = 1;
+ backoff_flag = 0;
+ break;
+
+ case 'u':
+ unreachable_flag = 1;
+ break;
+
+ case 'a':
+ alive_flag = 1;
+ break;
+
+ case 'H':
+ if (!(ttl = (unsigned int)atoi(optparse_state.optarg)))
+ usage(1);
+ break;
+
+#if defined(DEBUG) || defined(_DEBUG)
+ case 'z':
+ if (sscanf(optparse_state.optarg, "0x%x", &debugging) != 1)
+ if (sscanf(optparse_state.optarg, "%u", &debugging) != 1)
+ usage(1);
+
+ break;
+#endif /* DEBUG || _DEBUG */
+
+ case 'v':
+ printf("%s: Version %s\n", prog, VERSION);
+ exit(0);
+
+ case 'x':
+ if (!(min_reachable = (unsigned int)atoi(optparse_state.optarg)))
+ usage(1);
+ break;
+
+ case 'X':
+ if (!(min_reachable = (unsigned int)atoi(optparse_state.optarg)))
+ usage(1);
+ fast_reachable = 1;
+ break;
+
+ 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 == p_setsockopt(suid, socket4, SOL_SOCKET, SO_MARK, &fwmark, sizeof fwmark))
+ perror("fwmark ipv4");
+
+#ifdef IPV6
+ if (socket6 >= 0)
+ if(-1 == p_setsockopt(suid, socket6, SOL_SOCKET, SO_MARK, &fwmark, sizeof fwmark))
+ perror("fwmark ipv6");
+#endif
+
+ break;
+#endif
+
+ case 'g':
+ /* use IP list generation */
+ /* mutually exclusive with using file input or command line targets */
+ generate_flag = 1;
+ break;
+
+ case 'S':
+ if (inet_pton(AF_INET, optparse_state.optarg, &src_addr)) {
+ src_addr_set = 1;
+ break;
+ }
+#ifdef IPV6
+ if (inet_pton(AF_INET6, optparse_state.optarg, &src_addr6)) {
+ src_addr6_set = 1;
+ break;
+ }
+#endif
+ fprintf(stderr, "%s: can't parse source address: %s\n", prog, optparse_state.optarg);
+ exit(1);
+
+ case 'I':
+#ifdef SO_BINDTODEVICE
+ if (socket4 >= 0) {
+ if (p_setsockopt(suid, socket4, SOL_SOCKET, SO_BINDTODEVICE, optparse_state.optarg, strlen(optparse_state.optarg))) {
+ perror("binding to specific interface (SO_BINTODEVICE)");
+ exit(1);
+ }
+ }
+#ifdef IPV6
+ if (socket6 >= 0) {
+ if (p_setsockopt(suid, socket6, SOL_SOCKET, SO_BINDTODEVICE, optparse_state.optarg, strlen(optparse_state.optarg))) {
+ perror("binding to specific interface (SO_BINTODEVICE), IPV6");
+ exit(1);
+ }
+ }
+#endif
+#else
+ printf("%s: cant bind to a particular net interface since SO_BINDTODEVICE is not supported on your os.\n", prog);
+ exit(3);
+ ;
+#endif
+ break;
+
+ case 'T':
+ /* This option is ignored for compatibility reasons ("select timeout" is not meaningful anymore) */
+ break;
+
+ case 'O':
+ if (sscanf(optparse_state.optarg, "%i", &tos) == 1) {
+ if (socket4 >= 0) {
+ if (setsockopt(socket4, IPPROTO_IP, IP_TOS, &tos, sizeof(tos))) {
+ perror("setting type of service octet IP_TOS");
+ }
+ }
+#if defined(IPV6) && defined(IPV6_TCLASS)
+ if (socket6 >= 0) {
+ if (setsockopt(socket6, IPPROTO_IPV6, IPV6_TCLASS, &tos, sizeof(tos))) {
+ perror("setting type of service octet IPV6_TCLASS");
+ }
+ }
+#endif
+ }
+ else {
+ usage(1);
+ }
+ break;
+
+ case 'o':
+ outage_flag = 1;
+ break;
+
+ case '?':
+ fprintf(stderr, "%s: %s\n", argv[0], optparse_state.errmsg);
+ fprintf(stderr, "see 'fping -h' for usage information\n");
+ exit(1);
+ break;
+ }
+ }
+
+ /* permanently drop privileges */
+ if (suid != getuid() && setuid(getuid())) {
+ perror("fatal: failed to permanently drop privileges");
+ /* continuing would be a security hole */
+ exit(4);
+ }
+
+ /* validate various option settings */
+
+#ifndef IPV6
+ if (socket4 < 0) {
+ crash_and_burn("can't create socket (must run as root?)");
+ }
+#else
+ if ((socket4 < 0 && socket6 < 0) || (hints_ai_family == AF_INET6 && socket6 < 0)) {
+ crash_and_burn("can't create socket (must run as root?)");
+ }
+#endif
+
+ if (ttl > 255) {
+ fprintf(stderr, "%s: ttl %u out of range\n", prog, ttl);
+ exit(1);
+ }
+
+ if (unreachable_flag && alive_flag) {
+ fprintf(stderr, "%s: specify only one of a, u\n", prog);
+ exit(1);
+ }
+
+ if (count_flag && loop_flag) {
+ fprintf(stderr, "%s: specify only one of c, l\n", prog);
+ exit(1);
+ }
+
+ if (interval < (float)MIN_INTERVAL_MS * 1000000 && getuid()) {
+ fprintf(stderr, "%s: -i must be >= %g\n", prog, (float)MIN_INTERVAL_MS);
+ exit(1);
+ }
+
+ if (perhost_interval < (float)MIN_PERHOST_INTERVAL_MS * 1000000 && getuid()) {
+ fprintf(stderr, "%s: -p must be >= %g\n", prog, (float)MIN_PERHOST_INTERVAL_MS);
+ exit(1);
+ }
+
+ if (ping_data_size > MAX_PING_DATA) {
+ fprintf(stderr, "%s: data size %u not valid, must not be larger than %u\n",
+ prog, ping_data_size, (unsigned int)MAX_PING_DATA);
+ exit(1);
+ }
+
+ if ((backoff > MAX_BACKOFF_FACTOR) || (backoff < MIN_BACKOFF_FACTOR)) {
+ fprintf(stderr, "%s: backoff factor %.1f not valid, must be between %.1f and %.1f\n",
+ prog, backoff, MIN_BACKOFF_FACTOR, MAX_BACKOFF_FACTOR);
+ exit(1);
+ }
+
+ if (icmp_request_typ == 13 && size_flag != 0) {
+ fprintf(stderr, "%s: cannot change ICMP Timestamp size\n", prog);
+ exit(1);
+ }
+
+ if (count_flag) {
+ if (verbose_flag)
+ per_recv_flag = 1;
+
+ alive_flag = unreachable_flag = verbose_flag = 0;
+ }
+
+ if (loop_flag) {
+ if (!report_interval)
+ per_recv_flag = 1;
+
+ alive_flag = unreachable_flag = verbose_flag = 0;
+ }
+
+ if (alive_flag || unreachable_flag || min_reachable)
+ verbose_flag = 0;
+
+ trials = (count > retry + 1) ? count : retry + 1;
+
+ /* auto-tune default timeout for count/loop modes
+ * see also github #32 */
+ if (loop_flag || count_flag) {
+ if (!timeout_flag) {
+ timeout = perhost_interval;
+ if (timeout > (int64_t)AUTOTUNE_TIMEOUT_MAX * 1000000) {
+ timeout = (int64_t)AUTOTUNE_TIMEOUT_MAX * 1000000;
+ }
+ }
+ }
+
+#if defined(DEBUG) || defined(_DEBUG)
+ if (debugging & DBG_TRACE)
+ trace_flag = 1;
+
+ if (debugging & DBG_RANDOM_LOSE_FEW) {
+ randomly_lose_flag = 1;
+ lose_factor = 1; /* ie, 1/4 */
+ }
+
+ if (debugging & DBG_RANDOM_LOSE_MANY) {
+ randomly_lose_flag = 1;
+ lose_factor = 5; /* ie, 3/4 */
+ }
+
+ if (debugging & DBG_PRINT_PER_SYSTEM)
+ print_per_system_flag = 1;
+
+ if ((debugging & DBG_REPORT_ALL_RTTS) && !loop_flag)
+ report_all_rtts_flag = 1;
+
+ if (trace_flag) {
+ fprintf(stderr, "%s:\n count: %u, retry: %u, interval: %.0f ms\n",
+ prog, count, retry, interval / 1e6);
+ fprintf(stderr, " perhost_interval: %.0f ms, timeout: %.0f\n",
+ perhost_interval / 1e6, timeout / 1e6);
+ fprintf(stderr, " ping_data_size = %u, trials = %u\n",
+ ping_data_size, trials);
+
+ if (verbose_flag)
+ fprintf(stderr, " verbose_flag set\n");
+ if (multif_flag)
+ fprintf(stderr, " multif_flag set\n");
+ if (name_flag)
+ fprintf(stderr, " name_flag set\n");
+ if (addr_flag)
+ fprintf(stderr, " addr_flag set\n");
+ if (stats_flag)
+ fprintf(stderr, " stats_flag set\n");
+ if (unreachable_flag)
+ fprintf(stderr, " unreachable_flag set\n");
+ if (alive_flag)
+ fprintf(stderr, " alive_flag set\n");
+ if (elapsed_flag)
+ fprintf(stderr, " elapsed_flag set\n");
+ if (version_flag)
+ fprintf(stderr, " version_flag set\n");
+ if (count_flag)
+ fprintf(stderr, " count_flag set\n");
+ if (loop_flag)
+ fprintf(stderr, " loop_flag set\n");
+ if (backoff_flag)
+ fprintf(stderr, " backoff_flag set\n");
+ if (per_recv_flag)
+ fprintf(stderr, " per_recv_flag set\n");
+ if (report_all_rtts_flag)
+ fprintf(stderr, " report_all_rtts_flag set\n");
+ if (randomly_lose_flag)
+ fprintf(stderr, " randomly_lose_flag set\n");
+ if (print_per_system_flag)
+ fprintf(stderr, " print_per_system_flag set\n");
+ if (outage_flag)
+ fprintf(stderr, " outage_flag set\n");
+ if (netdata_flag)
+ fprintf(stderr, " netdata_flag set\n");
+ }
+#endif /* DEBUG || _DEBUG */
+
+ /* set the TTL, if the -H option was set (otherwise ttl will be = 0) */
+ if (ttl > 0) {
+ if (socket4 >= 0) {
+ if (setsockopt(socket4, IPPROTO_IP, IP_TTL, &ttl, sizeof(ttl))) {
+ perror("setting time to live");
+ }
+ }
+#ifdef IPV6
+ if (socket6 >= 0) {
+ if (setsockopt(socket6, IPPROTO_IPV6, IPV6_UNICAST_HOPS, &ttl, sizeof(ttl))) {
+ perror("setting time to live");
+ }
+ }
+#endif
+ }
+
+#if HAVE_SO_TIMESTAMPNS
+ {
+ int opt = 1;
+ if (socket4 >= 0) {
+ if (setsockopt(socket4, SOL_SOCKET, SO_TIMESTAMPNS, &opt, sizeof(opt))) {
+ if (setsockopt(socket4, SOL_SOCKET, SO_TIMESTAMP, &opt, sizeof(opt))) {
+ perror("setting SO_TIMESTAMPNS and SO_TIMESTAMP option");
+ }
+ }
+ }
+#ifdef IPV6
+ if (socket6 >= 0) {
+ if (setsockopt(socket6, SOL_SOCKET, SO_TIMESTAMPNS, &opt, sizeof(opt))) {
+ if (setsockopt(socket6, SOL_SOCKET, SO_TIMESTAMP, &opt, sizeof(opt))) {
+ perror("setting SO_TIMESTAMPNS and SO_TIMESTAMP option (IPv6)");
+ }
+ }
+ }
+#endif
+ }
+#endif
+
+ update_current_time();
+ start_time = current_time_ns;
+
+ /* handle host names supplied on command line or in a file */
+ /* if the generate_flag is on, then generate the IP list */
+
+ argv = &argv[optparse_state.optind];
+ argc -= optparse_state.optind;
+
+ /* calculate how many ping can be in-flight per host */
+ if (count_flag) {
+ event_storage_count = count;
+ }
+ else if (loop_flag) {
+ if (perhost_interval > timeout) {
+ event_storage_count = 1;
+ }
+ else {
+ event_storage_count = 1 + timeout / perhost_interval;
+ }
+ }
+ else {
+ event_storage_count = 1;
+ }
+
+ /* file and generate are mutually exclusive */
+ /* file and command line are mutually exclusive */
+ /* generate requires command line parameters beyond the switches */
+ if ((*argv && filename) || (filename && generate_flag) || (generate_flag && !*argv))
+ usage(1);
+
+ /* if no conditions are specified, then assume input from stdin */
+ if (!*argv && !filename && !generate_flag)
+ filename = "-";
+
+ if (*argv && !generate_flag) {
+ while (*argv) {
+ add_name(*argv);
+ ++argv;
+ }
+ }
+ else if (filename) {
+ FILE *ping_file;
+ char line[132];
+ char host[132];
+
+ if (strcmp(filename, "-") == 0)
+ ping_file = fdopen(0, "r");
+ else
+ ping_file = fopen(filename, "r");
+
+ if (!ping_file)
+ errno_crash_and_burn("fopen");
+
+ while (fgets(line, sizeof(line), ping_file)) {
+ if (sscanf(line, "%s", host) != 1)
+ continue;
+
+ if ((!*host) || (host[0] == '#')) /* magic to avoid comments */
+ continue;
+
+ add_name(host);
+ }
+
+ fclose(ping_file);
+ }
+ else if (*argv && generate_flag) {
+ if (argc == 1) {
+ /* one target: we expect a cidr range (n.n.n.n/m) */
+ add_cidr(argv[0]);
+ }
+ else if (argc == 2) {
+ add_range(argv[0], argv[1]);
+ }
+ else {
+ usage(1);
+ }
+ }
+ else {
+ usage(1);
+ }
+
+ if (!num_hosts) {
+ exit(num_noaddress ? 2 : 1);
+ }
+
+ if (socket4 >= 0 && (src_addr_set || socktype4 == SOCK_DGRAM)) {
+ socket_set_src_addr_ipv4(socket4, &src_addr, (socktype4 == SOCK_DGRAM) ? &ident4 : NULL);
+ }
+#ifdef IPV6
+ if (socket6 >= 0 && (src_addr6_set || socktype6 == SOCK_DGRAM)) {
+ socket_set_src_addr_ipv6(socket6, &src_addr6, (socktype6 == SOCK_DGRAM) ? &ident6 : NULL);
+ }
+#endif
+
+ /* allocate and initialize array to map host nr to host_entry */
+ {
+ struct event *cursor = event_queue_ping.first;
+ int i = 0;
+ table = (HOST_ENTRY **)calloc(num_hosts, sizeof(HOST_ENTRY *));
+ if (!table)
+ crash_and_burn("Can't malloc array of hosts");
+ /* initialize table of hosts. we know that we have ping events scheduled
+ * for each of them */
+ for (cursor = event_queue_ping.first; cursor; cursor = cursor->ev_next) {
+ table[i] = cursor->host;
+ cursor->host->i = i;
+ i++;
+ }
+ }
+
+ init_ping_buffer_ipv4(ping_data_size);
+#ifdef IPV6
+ init_ping_buffer_ipv6(ping_data_size);
+#endif
+
+#ifdef USE_SIGACTION
+ memset(&act, 0, sizeof(act));
+ act.sa_handler = signal_handler;
+ sigemptyset(&act.sa_mask);
+ sigaddset(&act.sa_mask, SIGINT);
+ sigaddset(&act.sa_mask, SIGQUIT);
+ act.sa_flags = SA_RESTART;
+ if (sigaction(SIGQUIT, &act, NULL) || sigaction(SIGINT, &act, NULL)) {
+ crash_and_burn("failure to set signal handler");
+ }
+#else
+ signal(SIGINT, signal_handler);
+ signal(SIGQUIT, signal_handler);
+#endif
+ setlinebuf(stdout);
+
+ if (report_interval) {
+ next_report_time = current_time_ns + report_interval;
+ }
+
+ last_send_time = 0;
+
+ seqmap_init();
+
+ /* main loop */
+ main_loop();
+
+/* Debug: CPU Performance */
+#if defined(DEBUG) || defined(_DEBUG)
+ perf_cpu_end = clock();
+ perf_cpu_time_used = ((double) (perf_cpu_end - perf_cpu_start)) / CLOCKS_PER_SEC;
+ printf("[DEBUG] CPU time used: %f sec", perf_cpu_time_used);
+#endif /* DEBUG || _DEBUG */
+
+ finish();
+
+ return 0;
+}
+
+static inline int64_t timespec_ns(struct timespec *a)
+{
+ return ((int64_t)a->tv_sec * 1000000000) + a->tv_nsec;
+}
+
+void add_cidr(char *addr)
+{
+ char *addr_end;
+ char *mask_str;
+ unsigned long mask;
+ unsigned long bitmask;
+ int ret;
+ struct addrinfo addr_hints;
+ struct addrinfo *addr_res;
+ unsigned long net_addr;
+ unsigned long net_last;
+
+ /* Split address from mask */
+ addr_end = strchr(addr, '/');
+ if (addr_end == NULL) {
+ usage(1);
+ }
+ *addr_end = '\0';
+ mask_str = addr_end + 1;
+ mask = atoi(mask_str);
+
+ /* parse address (IPv4 only) */
+ memset(&addr_hints, 0, sizeof(struct addrinfo));
+ addr_hints.ai_family = AF_UNSPEC;
+ addr_hints.ai_flags = AI_NUMERICHOST;
+ ret = getaddrinfo(addr, NULL, &addr_hints, &addr_res);
+ if (ret) {
+ fprintf(stderr, "%s, can't parse address %s: %s\n", prog, addr, gai_strerror(ret));
+ exit(1);
+ }
+ if (addr_res->ai_family != AF_INET) {
+ fprintf(stderr, "%s: -g works only with IPv4 addresses\n", prog);
+ exit(1);
+ }
+ net_addr = ntohl(((struct sockaddr_in *)addr_res->ai_addr)->sin_addr.s_addr);
+ freeaddrinfo(addr_res);
+
+ /* check mask */
+ if (mask < 1 || mask > 32) {
+ fprintf(stderr, "%s: netmask must be between 1 and 32 (is: %s)\n", prog, mask_str);
+ exit(1);
+ }
+
+ /* convert mask integer from 1 to 32 to a bitmask */
+ bitmask = ((unsigned long)0xFFFFFFFF) << (32 - mask);
+
+ /* calculate network range */
+ net_addr &= bitmask;
+ net_last = net_addr + ((unsigned long)0x1 << (32 - mask)) - 1;
+
+ /* exclude network and broadcast address for regular prefixes */
+ if (mask < 31) {
+ net_last--;
+ net_addr++;
+ }
+
+ /* add all hosts in that network (net_addr and net_last inclusive) */
+ add_addr_range_ipv4(net_addr, net_last);
+}
+
+void add_range(char *start, char *end)
+{
+ struct addrinfo addr_hints;
+ struct addrinfo *addr_res;
+ unsigned long start_long;
+ unsigned long end_long;
+ int ret;
+
+ /* parse start address (IPv4 only) */
+ memset(&addr_hints, 0, sizeof(struct addrinfo));
+ addr_hints.ai_family = AF_UNSPEC;
+ addr_hints.ai_flags = AI_NUMERICHOST;
+ ret = getaddrinfo(start, NULL, &addr_hints, &addr_res);
+ if (ret) {
+ fprintf(stderr, "%s: can't parse address %s: %s\n", prog, start, gai_strerror(ret));
+ exit(1);
+ }
+ if (addr_res->ai_family != AF_INET) {
+ freeaddrinfo(addr_res);
+ fprintf(stderr, "%s: -g works only with IPv4 addresses\n", prog);
+ exit(1);
+ }
+ start_long = ntohl(((struct sockaddr_in *)addr_res->ai_addr)->sin_addr.s_addr);
+
+ /* parse end address (IPv4 only) */
+ memset(&addr_hints, 0, sizeof(struct addrinfo));
+ addr_hints.ai_family = AF_UNSPEC;
+ addr_hints.ai_flags = AI_NUMERICHOST;
+ ret = getaddrinfo(end, NULL, &addr_hints, &addr_res);
+ if (ret) {
+ fprintf(stderr, "%s: can't parse address %s: %s\n", prog, end, gai_strerror(ret));
+ exit(1);
+ }
+ if (addr_res->ai_family != AF_INET) {
+ freeaddrinfo(addr_res);
+ fprintf(stderr, "%s: -g works only with IPv4 addresses\n", prog);
+ exit(1);
+ }
+ end_long = ntohl(((struct sockaddr_in *)addr_res->ai_addr)->sin_addr.s_addr);
+ freeaddrinfo(addr_res);
+
+ /* add IPv4 addresses from closed interval [start_long,end_long] */
+ add_addr_range_ipv4(start_long, end_long);
+}
+
+void add_addr_range_ipv4(unsigned long start_long, unsigned long end_long)
+{
+ /* check if generator limit is exceeded */
+ if (end_long >= start_long + MAX_GENERATE) {
+ fprintf(stderr, "%s: -g parameter generates too many addresses\n", prog);
+ exit(1);
+ }
+
+ /* generate */
+ for (; start_long <= end_long; start_long++) {
+ struct in_addr in_addr_tmp;
+ char buffer[20];
+ in_addr_tmp.s_addr = htonl(start_long);
+ inet_ntop(AF_INET, &in_addr_tmp, buffer, sizeof(buffer));
+ add_name(buffer);
+ }
+}
+
+void main_loop()
+{
+ int64_t lt;
+ int64_t wait_time_ns;
+ struct event *event;
+ struct host_entry *h;
+
+ while (event_queue_ping.first || event_queue_timeout.first) {
+ dbg_printf("%s", "# main_loop\n");
+
+ /* timeout event ? */
+ if (event_queue_timeout.first && event_queue_timeout.first->ev_time - current_time_ns <= 0) {
+ event = ev_dequeue(&event_queue_timeout);
+ h = event->host;
+
+ dbg_printf("%s [%d]: timeout event\n", h->host, event->ping_index);
+
+ stats_add(h, event->ping_index, 0, -1);
+
+ if (per_recv_flag) {
+ if (timestamp_flag) {
+ print_timestamp_format(current_time_ns, timestamp_format_flag);
+ }
+ printf("%-*s : [%d], timed out",
+ max_hostname_len, h->host, event->ping_index);
+ if (h->num_recv > 0) {
+ printf(" (%s avg, ", sprint_tm(h->total_time / h->num_recv));
+ }
+ else {
+ printf(" (NaN avg, ");
+ }
+ if (h->num_recv <= h->num_sent) {
+ printf("%d%% loss)",
+ ((h->num_sent - h->num_recv) * 100) / h->num_sent);
+ }
+ else {
+ printf("%d%% return)",
+ (h->num_recv_total * 100) / h->num_sent);
+ }
+ printf("\n");
+ }
+
+ /* do we need to send a retry? */
+ if (!loop_flag && !count_flag) {
+ if (h->num_sent < retry + 1) {
+ if (backoff_flag) {
+ h->timeout *= backoff;
+ }
+ send_ping(h, event->ping_index);
+ }
+ }
+
+ /* note: we process first timeout events, because we might need to
+ * wait to process ping events, while we for sure never need to
+ * wait for timeout events.
+ */
+ continue;
+ }
+
+ /* ping event ? */
+ if (event_queue_ping.first && event_queue_ping.first->ev_time - current_time_ns <= 0) {
+ /* Make sure that we don't ping more than once every "interval" */
+ lt = current_time_ns - last_send_time;
+ if (lt < interval)
+ goto wait_for_reply;
+
+ /* Dequeue the event */
+ event = ev_dequeue(&event_queue_ping);
+ h = event->host;
+
+ dbg_printf("%s [%d]: ping event\n", h->host, event->ping_index);
+
+ /* Send the ping */
+ send_ping(h, event->ping_index);
+
+ /* Loop and count mode: schedule next ping */
+ if (loop_flag || (count_flag && event->ping_index + 1 < count)) {
+ host_add_ping_event(h, event->ping_index + 1, event->ev_time + perhost_interval);
+ }
+ }
+
+ wait_for_reply:
+
+ /* When is the next ping next event? */
+ wait_time_ns = -1;
+ if (event_queue_ping.first) {
+ wait_time_ns = event_queue_ping.first->ev_time - current_time_ns;
+ if (wait_time_ns < 0)
+ wait_time_ns = 0;
+ /* make sure that we wait enough, so that the inter-ping delay is
+ * bigger than 'interval' */
+ if (wait_time_ns < interval) {
+ lt = current_time_ns - last_send_time;
+ if (lt < interval) {
+ wait_time_ns = interval - lt;
+ }
+ }
+
+ dbg_printf("next ping event in %.0f ms (%s)\n", wait_time_ns / 1e6, event_queue_ping.first->host->host);
+ }
+
+ /* When is the next timeout event? */
+ if (event_queue_timeout.first) {
+ int64_t wait_time_timeout = event_queue_timeout.first->ev_time - current_time_ns;
+ if (wait_time_ns < 0 || wait_time_timeout < wait_time_ns) {
+ wait_time_ns = wait_time_timeout;
+ if (wait_time_ns < 0) {
+ wait_time_ns = 0;
+ }
+ }
+
+ dbg_printf("next timeout event in %.0f ms (%s)\n", wait_time_timeout / 1e6, event_queue_timeout.first->host->host);
+ }
+
+ /* When is the next report due? */
+ if (report_interval && (loop_flag || count_flag)) {
+ int64_t wait_time_next_report = next_report_time - current_time_ns;
+ if (wait_time_next_report < wait_time_ns) {
+ wait_time_ns = wait_time_next_report;
+ if (wait_time_ns < 0) {
+ wait_time_ns = 0;
+ }
+ }
+
+ dbg_printf("next report event in %0.f ms\n", wait_time_next_report / 1e6);
+ }
+
+ /* if wait_time is still -1, it means that we are waiting for nothing... */
+ if (wait_time_ns == -1) {
+ break;
+ }
+
+ /* end of loop was requested by interrupt signal handler */
+ if (finish_requested) {
+ break;
+ }
+
+ /* Receive replies */
+ /* (this is what sleeps during each loop iteration) */
+ dbg_printf("waiting up to %.0f ms\n", wait_time_ns / 1e6);
+ if (wait_for_reply(wait_time_ns)) {
+ while (wait_for_reply(0))
+ ; /* process other replies in the queue */
+ }
+
+ update_current_time();
+
+ if (status_snapshot) {
+ status_snapshot = 0;
+ print_per_system_splits();
+ }
+
+ /* Print report */
+ if (report_interval && (loop_flag || count_flag) && (current_time_ns >= next_report_time)) {
+ if (netdata_flag)
+ print_netdata();
+ else
+ print_per_system_splits();
+
+ while (current_time_ns >= next_report_time) {
+ next_report_time += report_interval;
+ }
+ }
+ }
+}
+
+/************************************************************
+
+ Function: signal_handler
+
+*************************************************************
+
+ Inputs: int signum
+
+ Description:
+
+ SIGQUIT signal handler - set flag and return
+ SIGINT signal handler - set flag and return
+
+************************************************************/
+
+void signal_handler(int signum)
+{
+ switch (signum) {
+ case SIGINT:
+ finish_requested = 1;
+ break;
+
+ case SIGQUIT:
+ status_snapshot = 1;
+ break;
+ }
+}
+
+/************************************************************
+
+ Function: update_current_time
+
+*************************************************************/
+
+void update_current_time()
+{
+ clock_gettime(CLOCKID, ¤t_time);
+ current_time_ns = timespec_ns(¤t_time);
+}
+
+/************************************************************
+
+ Function: finish
+
+*************************************************************
+
+ Inputs: void (none)
+
+ Description:
+
+ Main program clean up and exit point
+
+************************************************************/
+
+void finish()
+{
+ int i;
+ HOST_ENTRY *h;
+
+ update_current_time();
+ end_time = current_time_ns;
+
+ /* tot up unreachables */
+ for (i = 0; i < num_hosts; i++) {
+ h = table[i];
+
+ if (!h->num_recv) {
+ num_unreachable++;
+
+ if (verbose_flag || unreachable_flag) {
+ printf("%s", h->host);
+
+ if (verbose_flag)
+ printf(" is unreachable");
+
+ printf("\n");
+ }
+ }
+ }
+
+ if (count_flag || loop_flag)
+ print_per_system_stats();
+#if defined(DEBUG) || defined(_DEBUG)
+ else if (print_per_system_flag)
+ print_per_system_stats();
+#endif /* DEBUG || _DEBUG */
+
+ if (stats_flag)
+ print_global_stats();
+
+ if (min_reachable) {
+ if ((num_hosts - num_unreachable) >= min_reachable) {
+ printf("Enough hosts reachable (required: %d, reachable: %d)\n", min_reachable, num_hosts - num_unreachable);
+ exit(0);
+ }
+ else {
+ printf("Not enough hosts reachable (required: %d, reachable: %d)\n", min_reachable, num_hosts - num_unreachable);
+ exit(1);
+ }
+ }
+
+ if (num_noaddress)
+ exit(2);
+ else if (num_alive != num_hosts)
+ exit(1);
+
+ exit(0);
+}
+
+/************************************************************
+
+ Function: print_per_system_stats
+
+*************************************************************
+
+ Inputs: void (none)
+
+ Description:
+
+
+************************************************************/
+
+void print_per_system_stats(void)
+{
+ int i, j, avg, outage_ms;
+ HOST_ENTRY *h;
+ int64_t resp;
+
+ if (verbose_flag || per_recv_flag)
+ fprintf(stderr, "\n");
+
+ for (i = 0; i < num_hosts; i++) {
+ h = table[i];
+ fprintf(stderr, "%-*s :", max_hostname_len, h->host);
+
+ if (report_all_rtts_flag) {
+ for (j = 0; j < h->num_sent; j++) {
+ if ((resp = h->resp_times[j]) >= 0)
+ fprintf(stderr, " %s", sprint_tm(resp));
+ else
+ fprintf(stderr, " -");
+ }
+
+ fprintf(stderr, "\n");
+ }
+ else {
+ if (h->num_recv <= h->num_sent) {
+ fprintf(stderr, " xmt/rcv/%%loss = %d/%d/%d%%",
+ h->num_sent, h->num_recv, h->num_sent > 0 ? ((h->num_sent - h->num_recv) * 100) / h->num_sent : 0);
+
+ if (outage_flag) {
+ /* Time outage total */
+ outage_ms = (h->num_sent - h->num_recv) * perhost_interval / 1e6;
+ fprintf(stderr, ", outage(ms) = %d", outage_ms);
+ }
+ }
+ else {
+ fprintf(stderr, " xmt/rcv/%%return = %d/%d/%d%%",
+ h->num_sent, h->num_recv,
+ h->num_sent > 0 ? ((h->num_recv * 100) / h->num_sent) : 0);
+ }
+
+ if (h->num_recv) {
+ avg = h->total_time / h->num_recv;
+ fprintf(stderr, ", min/avg/max = %s", sprint_tm(h->min_reply));
+ fprintf(stderr, "/%s", sprint_tm(avg));
+ fprintf(stderr, "/%s", sprint_tm(h->max_reply));
+ }
+
+ fprintf(stderr, "\n");
+ }
+ }
+}
+
+/************************************************************
+
+ Function: print_netdata
+
+*************************************************************
+
+ Inputs: void (none)
+
+ Description:
+
+
+************************************************************/
+
+void print_netdata(void)
+{
+ static int sent_charts = 0;
+
+ int i;
+ int64_t avg;
+ HOST_ENTRY *h;
+
+ for (i = 0; i < num_hosts; i++) {
+ h = table[i];
+
+ if (!sent_charts) {
+ printf("CHART fping.%s_packets '' 'FPing Packets' packets '%s' fping.packets line 110020 %.0f\n", h->name, h->host, report_interval / 1e9);
+ printf("DIMENSION xmt sent absolute 1 1\n");
+ printf("DIMENSION rcv received absolute 1 1\n");
+ }
+
+ printf("BEGIN fping.%s_packets\n", h->name);
+ printf("SET xmt = %d\n", h->num_sent_i);
+ printf("SET rcv = %d\n", h->num_recv_i);
+ printf("END\n");
+
+ if (!sent_charts) {
+ printf("CHART fping.%s_quality '' 'FPing Quality' percentage '%s' fping.quality area 110010 %.0f\n", h->name, h->host, report_interval / 1e9);
+ printf("DIMENSION returned '' absolute 1 1\n");
+ /* printf("DIMENSION lost '' absolute 1 1\n"); */
+ }
+
+ printf("BEGIN fping.%s_quality\n", h->name);
+ /*
+ if( h->num_recv_i <= h->num_sent_i )
+ printf("SET lost = %d\n", h->num_sent_i > 0 ? ( ( h->num_sent_i - h->num_recv_i ) * 100 ) / h->num_sent_i : 0 );
+ else
+ printf("SET lost = 0\n");
+*/
+
+ printf("SET returned = %d\n", h->num_sent_i > 0 ? ((h->num_recv_i * 100) / h->num_sent_i) : 0);
+ printf("END\n");
+
+ if (!sent_charts) {
+ printf("CHART fping.%s_latency '' 'FPing Latency' ms '%s' fping.latency area 110000 %.0f\n", h->name, h->host, report_interval / 1e9);
+ printf("DIMENSION min minimum absolute 1 1000000\n");
+ printf("DIMENSION max maximum absolute 1 1000000\n");
+ printf("DIMENSION avg average absolute 1 1000000\n");
+ }
+
+ printf("BEGIN fping.%s_latency\n", h->name);
+ if (h->num_recv_i) {
+ avg = h->total_time_i / h->num_recv_i;
+ printf("SET min = %" PRId64 "\n", h->min_reply_i);
+ printf("SET avg = %" PRId64 "\n", avg);
+ printf("SET max = %" PRId64 "\n", h->max_reply_i);
+ }
+ printf("END\n");
+
+ stats_reset_interval(h);
+ }
+
+ sent_charts = 1;
+}
+
+/************************************************************
+
+ Function: print_per_system_splits
+
+*************************************************************
+
+ Inputs: void (none)
+
+ Description:
+
+
+************************************************************/
+
+void print_per_system_splits(void)
+{
+ int i, avg, outage_ms_i;
+ HOST_ENTRY *h;
+ struct tm *curr_tm;
+
+ if (verbose_flag || per_recv_flag)
+ fprintf(stderr, "\n");
+
+ update_current_time();
+ curr_tm = localtime((time_t *)¤t_time.tv_sec);
+ fprintf(stderr, "[%2.2d:%2.2d:%2.2d]\n", curr_tm->tm_hour,
+ curr_tm->tm_min, curr_tm->tm_sec);
+
+ for (i = 0; i < num_hosts; i++) {
+ h = table[i];
+ fprintf(stderr, "%-*s :", max_hostname_len, h->host);
+
+ if (h->num_recv_i <= h->num_sent_i) {
+ fprintf(stderr, " xmt/rcv/%%loss = %d/%d/%d%%",
+ h->num_sent_i, h->num_recv_i, h->num_sent_i > 0 ? ((h->num_sent_i - h->num_recv_i) * 100) / h->num_sent_i : 0);
+
+ if (outage_flag) {
+ /* Time outage */
+ outage_ms_i = (h->num_sent_i - h->num_recv_i) * perhost_interval / 1e6;
+ fprintf(stderr, ", outage(ms) = %d", outage_ms_i);
+ }
+ }
+ else {
+ fprintf(stderr, " xmt/rcv/%%return = %d/%d/%d%%",
+ h->num_sent_i, h->num_recv_i, h->num_sent_i > 0 ? ((h->num_recv_i * 100) / h->num_sent_i) : 0);
+ }
+
+ if (h->num_recv_i) {
+ avg = h->total_time_i / h->num_recv_i;
+ fprintf(stderr, ", min/avg/max = %s", sprint_tm(h->min_reply_i));
+ fprintf(stderr, "/%s", sprint_tm(avg));
+ fprintf(stderr, "/%s", sprint_tm(h->max_reply_i));
+ }
+
+ fprintf(stderr, "\n");
+ if (!cumulative_stats_flag) {
+ stats_reset_interval(h);
+ }
+ }
+}
+
+/************************************************************
+
+ Function: print_global_stats
+
+*************************************************************
+
+ Inputs: void (none)
+
+ Description:
+
+
+************************************************************/
+
+void print_global_stats(void)
+{
+ fprintf(stderr, "\n");
+ fprintf(stderr, " %7d targets\n", num_hosts);
+ fprintf(stderr, " %7d alive\n", num_alive);
+ fprintf(stderr, " %7d unreachable\n", num_unreachable);
+ fprintf(stderr, " %7d unknown addresses\n", num_noaddress);
+ fprintf(stderr, "\n");
+ fprintf(stderr, " %7d timeouts (waiting for response)\n", num_timeout);
+ fprintf(stderr, " %7d ICMP Echos sent\n", num_pingsent);
+ fprintf(stderr, " %7d ICMP Echo Replies received\n", num_pingreceived);
+ fprintf(stderr, " %7d other ICMP received\n", num_othericmprcvd);
+ fprintf(stderr, "\n");
+
+ if (total_replies == 0) {
+ min_reply = 0;
+ max_reply = 0;
+ total_replies = 1;
+ sum_replies = 0;
+ }
+
+ fprintf(stderr, " %s ms (min round trip time)\n", sprint_tm(min_reply));
+ fprintf(stderr, " %s ms (avg round trip time)\n",
+ sprint_tm(sum_replies / total_replies));
+ fprintf(stderr, " %s ms (max round trip time)\n", sprint_tm(max_reply));
+ fprintf(stderr, " %12.3f sec (elapsed real time)\n",
+ (end_time - start_time) / 1e9);
+ fprintf(stderr, "\n");
+}
+
+/************************************************************
+
+ Function: send_ping
+
+*************************************************************
+
+ Inputs: int s, HOST_ENTRY *h
+
+ Description:
+
+ Compose and transmit an ICMP_ECHO REQUEST packet. The IP packet
+ will be added on by the kernel. The ID field is our UNIX process ID,
+ and the sequence number is an index into an array of outstanding
+ ping requests. The sequence number will later be used to quickly
+ figure out who the ping reply came from.
+
+************************************************************/
+
+int send_ping(HOST_ENTRY *h, int index)
+{
+ int n;
+ int myseq;
+ int ret = 1;
+ uint8_t proto = ICMP_ECHO;
+
+ update_current_time();
+ h->last_send_time = current_time_ns;
+ myseq = seqmap_add(h->i, index, current_time_ns);
+
+ dbg_printf("%s [%d]: send ping\n", h->host, index);
+
+ if (h->saddr.ss_family == AF_INET && socket4 >= 0) {
+ if(icmp_request_typ == 13)
+ proto = ICMP_TSTAMP;
+ n = socket_sendto_ping_ipv4(socket4, (struct sockaddr *)&h->saddr, h->saddr_len, myseq, ident4, proto);
+ }
+#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, ident6);
+ }
+#endif
+ else {
+ return 0;
+ }
+
+ /* error sending? */
+ if (
+ (n < 0)
+#if defined(EHOSTDOWN)
+ && errno != EHOSTDOWN
+#endif
+ ) {
+ if (verbose_flag) {
+ print_warning("%s: error while sending ping: %s\n", h->host, strerror(errno));
+ }
+ else {
+ dbg_printf("%s: error while sending ping: %s\n", h->host, strerror(errno));
+ }
+
+ h->num_sent++;
+ h->num_sent_i++;
+ if (!loop_flag)
+ h->resp_times[index] = RESP_ERROR;
+
+ ret = 0;
+ }
+ else {
+ /* schedule timeout */
+ host_add_timeout_event(h, index, current_time_ns + h->timeout);
+
+ /* mark this trial as outstanding */
+ if (!loop_flag) {
+ h->resp_times[index] = RESP_WAITING;
+ }
+ }
+
+ num_pingsent++;
+ last_send_time = h->last_send_time;
+
+ return (ret);
+}
+
+int socket_can_read(struct timeval *timeout)
+{
+ int nfound;
+ fd_set readset;
+ int socketmax;
+
+#ifndef IPV6
+ socketmax = socket4;
+#else
+ socketmax = socket4 > socket6 ? socket4 : socket6;
+#endif
+
+select_again:
+ FD_ZERO(&readset);
+ if (socket4 >= 0)
+ FD_SET(socket4, &readset);
+#ifdef IPV6
+ if (socket6 >= 0)
+ FD_SET(socket6, &readset);
+#endif
+
+ nfound = select(socketmax + 1, &readset, NULL, NULL, timeout);
+ if (nfound < 0) {
+ if (errno == EINTR) {
+ /* interrupted system call: redo the select */
+ goto select_again;
+ }
+ else {
+ perror("select");
+ }
+ }
+
+ if (nfound > 0) {
+ if (socket4 >= 0 && FD_ISSET(socket4, &readset)) {
+ return socket4;
+ }
+#ifdef IPV6
+ if (socket6 >= 0 && FD_ISSET(socket6, &readset)) {
+ return socket6;
+ }
+#endif
+ }
+
+ return -1;
+}
+
+int receive_packet(int64_t wait_time,
+#if HAVE_SO_TIMESTAMPNS
+ int64_t *reply_timestamp,
+#else
+ int64_t *reply_timestamp __attribute__((unused)),
+#endif
+ struct sockaddr *reply_src_addr,
+ size_t reply_src_addr_len,
+ char *reply_buf,
+ size_t reply_buf_len)
+{
+ struct timeval to;
+ int s = 0;
+ int recv_len;
+ static unsigned char msg_control[40];
+ struct iovec msg_iov = {
+ reply_buf,
+ reply_buf_len
+ };
+ struct msghdr recv_msghdr = {0};
+ recv_msghdr.msg_name = reply_src_addr;
+ recv_msghdr.msg_namelen = reply_src_addr_len;
+ recv_msghdr.msg_iov = &msg_iov;
+ recv_msghdr.msg_iovlen = 1;
+ recv_msghdr.msg_control = &msg_control;
+ recv_msghdr.msg_controllen = sizeof(msg_control);
+#if HAVE_SO_TIMESTAMPNS
+ struct cmsghdr *cmsg;
+#endif
+
+ /* Wait for a socket to become ready */
+ if (wait_time) {
+ to.tv_sec = wait_time / UINT64_C(1000000000);
+ to.tv_usec = (wait_time % UINT64_C(1000000000)) / 1000 + 1;
+ }
+ else {
+ to.tv_sec = 0;
+ to.tv_usec = 0;
+ }
+ s = socket_can_read(&to);
+ if (s == -1) {
+ return 0; /* timeout */
+ }
+
+ recv_len = recvmsg(s, &recv_msghdr, MSG_TRUNC);
+ if (recv_len <= 0) {
+ return 0;
+ }
+
+#if HAVE_SO_TIMESTAMPNS
+ /* ancilliary data */
+ {
+ struct timespec reply_timestamp_ts;
+ for (cmsg = CMSG_FIRSTHDR(&recv_msghdr);
+ cmsg != NULL;
+ cmsg = CMSG_NXTHDR(&recv_msghdr, cmsg)) {
+ if (cmsg->cmsg_level == SOL_SOCKET && cmsg->cmsg_type == SCM_TIMESTAMPNS) {
+ memcpy(&reply_timestamp_ts, CMSG_DATA(cmsg), sizeof(reply_timestamp_ts));
+ *reply_timestamp = timespec_ns(&reply_timestamp_ts);
+ }
+ }
+ }
+#endif
+
+#if defined(DEBUG) || defined(_DEBUG)
+ if (randomly_lose_flag) {
+ if ((random() & 0x07) <= lose_factor)
+ return 0;
+ }
+#endif
+
+ return recv_len;
+}
+
+/* stats_add: update host statistics for a single packet that was received (or timed out)
+ * h: host entry to update
+ * index: if in count mode: index number for this ping packet (-1 otherwise)
+ * success: 1 if response received, 0 otherwise
+ * latency: response time, in ns
+ */
+void stats_add(HOST_ENTRY *h, int index, int success, int64_t latency)
+{
+ /* sent count - we update only on receive/timeout, so that we don't get
+ * weird loss percentage, just because a packet was note recived yet.
+ */
+ h->num_sent++;
+ h->num_sent_i++;
+
+ if (!success) {
+ if (!loop_flag && index >= 0) {
+ h->resp_times[index] = RESP_TIMEOUT;
+ }
+ num_timeout++;
+ return;
+ }
+
+ /* received count */
+ h->num_recv++;
+ h->num_recv_i++;
+
+ /* maximum */
+ if (!h->max_reply || latency > h->max_reply) {
+ h->max_reply = latency;
+ }
+ if (!h->max_reply_i || latency > h->max_reply_i) {
+ h->max_reply_i = latency;
+ }
+
+ /* minimum */
+ if (!h->min_reply || latency < h->min_reply) {
+ h->min_reply = latency;
+ }
+ if (!h->min_reply_i || latency < h->min_reply_i) {
+ h->min_reply_i = latency;
+ }
+
+ /* total time (for average) */
+ h->total_time += latency;
+ h->total_time_i += latency;
+
+ /* response time per-packet (count mode) */
+ if (!loop_flag && index >= 0) {
+ h->resp_times[index] = latency;
+ }
+}
+
+/* stats_reset_interval: reset interval statistics
+ * h: host entry to update
+ */
+void stats_reset_interval(HOST_ENTRY *h)
+{
+ h->num_sent_i = 0;
+ h->num_recv_i = 0;
+ h->max_reply_i = 0;
+ h->min_reply_i = 0;
+ h->total_time_i = 0;
+}
+
+int decode_icmp_ipv4(
+ struct sockaddr *response_addr,
+ size_t response_addr_len,
+ char *reply_buf,
+ size_t reply_buf_len,
+ unsigned short *id,
+ unsigned short *seq,
+ int *ip_header_tos,
+ int *ip_header_ttl,
+ uint32_t *ip_header_otime_ms,
+ uint32_t *ip_header_rtime_ms,
+ uint32_t *ip_header_ttime_ms)
+{
+ struct icmp *icp;
+ int hlen = 0;
+
+ if (!using_sock_dgram4) {
+ struct ip *ip = (struct ip *)reply_buf;
+ *ip_header_tos = ip->ip_tos;
+ *ip_header_ttl = ip->ip_ttl;
+
+#if defined(__alpha__) && __STDC__ && !defined(__GLIBC__) && !defined(__NetBSD__) && !defined(__OpenBSD__)
+ /* 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;
+#endif
+ }
+
+ if (reply_buf_len < hlen + ICMP_MINLEN) {
+ /* too short */
+ if (verbose_flag) {
+ char buf[INET6_ADDRSTRLEN];
+ getnameinfo(response_addr, response_addr_len, buf, INET6_ADDRSTRLEN, NULL, 0, NI_NUMERICHOST);
+ printf("received packet too short for ICMP (%d bytes from %s)\n", (int)reply_buf_len, buf);
+ }
+ return -1;
+ }
+
+ icp = (struct icmp *)(reply_buf + hlen);
+
+ if ((icmp_request_typ == 0 && icp->icmp_type != ICMP_ECHOREPLY) ||
+ (icmp_request_typ == 13 && icp->icmp_type != ICMP_TSTAMPREPLY)) {
+ /* Handle other ICMP packets */
+ struct icmp *sent_icmp;
+ SEQMAP_VALUE *seqmap_value;
+ char addr_ascii[INET6_ADDRSTRLEN];
+ HOST_ENTRY *h;
+
+ /* reply icmp packet (hlen + ICMP_MINLEN) followed by "sent packet" (ip + icmp headers) */
+ if (reply_buf_len < hlen + ICMP_MINLEN + sizeof(struct ip) + ICMP_MINLEN) {
+ /* discard ICMP message if we can't tell that it was caused by us (i.e. if the "sent packet" is not included). */
+ return -1;
+ }
+
+ sent_icmp = (struct icmp *)(reply_buf + hlen + ICMP_MINLEN + sizeof(struct ip));
+
+ if ((icmp_request_typ == 0 && sent_icmp->icmp_type != ICMP_ECHO) ||
+ (icmp_request_typ == 13 && sent_icmp->icmp_type != ICMP_TSTAMP) ||
+ sent_icmp->icmp_id != ident4) {
+ /* not caused by us */
+ return -1;
+ }
+
+ seqmap_value = seqmap_fetch(ntohs(sent_icmp->icmp_seq), current_time_ns);
+ if (seqmap_value == NULL) {
+ return -1;
+ }
+
+ getnameinfo(response_addr, response_addr_len, addr_ascii, INET6_ADDRSTRLEN, NULL, 0, NI_NUMERICHOST);
+
+ switch (icp->icmp_type) {
+ case ICMP_UNREACH:
+ h = table[seqmap_value->host_nr];
+ if (icp->icmp_code > ICMP_UNREACH_MAXTYPE) {
+ print_warning("ICMP Unreachable (Invalid Code) from %s for ICMP Echo sent to %s",
+ addr_ascii, h->host);
+ }
+ else {
+ print_warning("%s from %s for ICMP Echo sent to %s",
+ icmp_unreach_str[icp->icmp_code], addr_ascii, h->host);
+ }
+
+ print_warning("\n");
+ num_othericmprcvd++;
+ break;
+
+ case ICMP_SOURCEQUENCH:
+ case ICMP_REDIRECT:
+ case ICMP_TIMXCEED:
+ case ICMP_PARAMPROB:
+ h = table[seqmap_value->host_nr];
+ if (icp->icmp_type <= ICMP_TYPE_STR_MAX) {
+ print_warning("%s from %s for ICMP Echo sent to %s",
+ icmp_type_str[icp->icmp_type], addr_ascii, h->host);
+ }
+ else {
+ print_warning("ICMP %d from %s for ICMP Echo sent to %s",
+ icp->icmp_type, addr_ascii, h->host);
+ }
+ print_warning("\n");
+ num_othericmprcvd++;
+ break;
+ }
+
+ return -1;
+ }
+
+ *id = icp->icmp_id;
+ *seq = ntohs(icp->icmp_seq);
+ if(icp->icmp_type == ICMP_TSTAMPREPLY) {
+
+ /* Check that reply_buf_len is sufficiently big to contain the timestamps */
+ if (reply_buf_len < hlen + ICMP_MINLEN + ICMP_TIMESTAMP_DATA_SIZE) {
+ if (verbose_flag) {
+ char buf[INET6_ADDRSTRLEN];
+ getnameinfo(response_addr, response_addr_len, buf, INET6_ADDRSTRLEN, NULL, 0, NI_NUMERICHOST);
+ printf("received packet too short for ICMP Timestamp Reply (%d bytes from %s)\n", (int)reply_buf_len, buf);
+ }
+ return -1;
+ }
+
+ *ip_header_otime_ms = ntohl(icp->icmp_dun.id_ts.its_otime);
+ *ip_header_rtime_ms = ntohl(icp->icmp_dun.id_ts.its_rtime);
+ *ip_header_ttime_ms = ntohl(icp->icmp_dun.id_ts.its_ttime);
+ }
+
+ return hlen;
+}
+
+#ifdef IPV6
+int decode_icmp_ipv6(
+ struct sockaddr *response_addr,
+ size_t response_addr_len,
+ char *reply_buf,
+ size_t reply_buf_len,
+ unsigned short *id,
+ unsigned short *seq)
+{
+ struct icmp6_hdr *icp;
+
+ if (reply_buf_len < sizeof(struct icmp6_hdr)) {
+ if (verbose_flag) {
+ char buf[INET6_ADDRSTRLEN];
+ getnameinfo(response_addr, response_addr_len, buf, INET6_ADDRSTRLEN, NULL, 0, NI_NUMERICHOST);
+ printf("received packet too short for ICMP (%d bytes from %s)\n", (int)reply_buf_len, buf);
+ }
+ return 0; /* too short */
+ }
+
+ icp = (struct icmp6_hdr *)reply_buf;
+
+ if (icp->icmp6_type != ICMP6_ECHO_REPLY) {
+ /* Handle other ICMP packets */
+ struct icmp6_hdr *sent_icmp;
+ SEQMAP_VALUE *seqmap_value;
+ char addr_ascii[INET6_ADDRSTRLEN];
+ HOST_ENTRY *h;
+
+ /* reply icmp packet (ICMP_MINLEN) followed by "sent packet" (ip + icmp headers) */
+ if (reply_buf_len < ICMP_MINLEN + sizeof(struct ip) + ICMP_MINLEN) {
+ /* discard ICMP message if we can't tell that it was caused by us (i.e. if the "sent packet" is not included). */
+ return 0;
+ }
+
+ sent_icmp = (struct icmp6_hdr *)(reply_buf + sizeof(struct icmp6_hdr) + sizeof(struct ip));
+
+ if (sent_icmp->icmp6_type != ICMP_ECHO || sent_icmp->icmp6_id != ident6) {
+ /* not caused by us */
+ return 0;
+ }
+
+ seqmap_value = seqmap_fetch(ntohs(sent_icmp->icmp6_seq), current_time_ns);
+ if (seqmap_value == NULL) {
+ return 0;
+ }
+
+ getnameinfo(response_addr, response_addr_len, addr_ascii, INET6_ADDRSTRLEN, NULL, 0, NI_NUMERICHOST);
+
+ switch (icp->icmp6_type) {
+ case ICMP_UNREACH:
+ h = table[seqmap_value->host_nr];
+ if (icp->icmp6_code > ICMP_UNREACH_MAXTYPE) {
+ print_warning("ICMP Unreachable (Invalid Code) from %s for ICMP Echo sent to %s",
+ addr_ascii, h->host);
+ }
+ else {
+ print_warning("%s from %s for ICMP Echo sent to %s",
+ icmp_unreach_str[icp->icmp6_code], addr_ascii, h->host);
+ }
+
+ print_warning("\n");
+ num_othericmprcvd++;
+ break;
+
+ case ICMP_SOURCEQUENCH:
+ case ICMP_REDIRECT:
+ case ICMP_TIMXCEED:
+ case ICMP_PARAMPROB:
+ h = table[seqmap_value->host_nr];
+ if (icp->icmp6_type <= ICMP_TYPE_STR_MAX) {
+ print_warning("%s from %s for ICMP Echo sent to %s",
+ icmp_type_str[icp->icmp6_type], addr_ascii, h->host);
+ }
+ else {
+ print_warning("ICMP %d from %s for ICMP Echo sent to %s",
+ icp->icmp6_type, addr_ascii, h->host);
+ }
+ print_warning("\n");
+ num_othericmprcvd++;
+ break;
+ }
+
+ return 0;
+ }
+
+ *id = icp->icmp6_id;
+ *seq = ntohs(icp->icmp6_seq);
+
+ return 1;
+}
+#endif
+
+int wait_for_reply(int64_t wait_time)
+{
+ int result;
+ static char buffer[RECV_BUFSIZE];
+ struct sockaddr_storage response_addr;
+ int n, avg;
+ HOST_ENTRY *h;
+ int64_t this_reply;
+ int this_count;
+ int64_t recv_time = 0;
+ SEQMAP_VALUE *seqmap_value;
+ unsigned short id;
+ unsigned short seq;
+ int ip_header_tos = -1;
+ int ip_header_ttl = -1;
+ // ICMP Timestamp
+ uint32_t ip_header_otime_ms = 0x80000000U;
+ uint32_t ip_header_rtime_ms = 0x80000000U;
+ uint32_t ip_header_ttime_ms = 0x80000000U;
+
+ /* Receive packet */
+ result = receive_packet(wait_time, /* max. wait time, in ns */
+ &recv_time, /* reply_timestamp */
+ (struct sockaddr *)&response_addr, /* reply_src_addr */
+ sizeof(response_addr), /* reply_src_addr_len */
+ buffer, /* reply_buf */
+ sizeof(buffer) /* reply_buf_len */
+ );
+
+ if (result <= 0) {
+ return 0;
+ }
+
+ update_current_time();
+ if (recv_time == 0)
+ recv_time = current_time_ns;
+
+ /* Process ICMP packet and retrieve id/seq */
+ if (response_addr.ss_family == AF_INET) {
+ int ip_hlen = decode_icmp_ipv4(
+ (struct sockaddr *)&response_addr,
+ sizeof(response_addr),
+ buffer,
+ sizeof(buffer),
+ &id,
+ &seq,
+ &ip_header_tos,
+ &ip_header_ttl,
+ &ip_header_otime_ms,
+ &ip_header_rtime_ms,
+ &ip_header_ttime_ms);
+ if (ip_hlen < 0) {
+ return 1;
+ }
+ if (id != ident4) {
+ return 1; /* packet received, but not the one we are looking for! */
+ }
+ if (!using_sock_dgram4) {
+ /* do not include IP header in returned size, to be consistent with ping(8) and also
+ * with fping with IPv6 hosts */
+ result -= ip_hlen;
+ }
+ }
+#ifdef IPV6
+ else if (response_addr.ss_family == AF_INET6) {
+ if (!decode_icmp_ipv6(
+ (struct sockaddr *)&response_addr,
+ sizeof(response_addr),
+ buffer,
+ sizeof(buffer),
+ &id,
+ &seq)) {
+ return 1;
+ }
+ if (id != ident6) {
+ return 1; /* packet received, but not the one we are looking for! */
+ }
+ }
+#endif
+ else {
+ return 1;
+ }
+
+ seqmap_value = seqmap_fetch(seq, current_time_ns);
+ if (seqmap_value == NULL) {
+ return 1;
+ }
+
+ /* find corresponding host_entry */
+ n = seqmap_value->host_nr;
+ h = table[n];
+ this_count = seqmap_value->ping_count;
+ this_reply = recv_time - seqmap_value->ping_ts;
+
+ /* update stats that include invalid replies */
+ h->num_recv_total++;
+ num_pingreceived++;
+
+ dbg_printf("received [%d] from %s\n", this_count, h->host);
+
+ /* optionally require reply source equal to target address */
+ if (check_source_flag && addr_cmp((struct sockaddr *)&response_addr, (struct sockaddr *)&h->saddr)) {
+ dbg_printf("discarding reply from wrong source address\n");
+ return 1;
+ }
+
+ /* discard duplicates */
+ if (!loop_flag && h->resp_times[this_count] >= 0) {
+ if (!per_recv_flag) {
+ fprintf(stderr, "%s : duplicate for [%d], %d bytes, %s ms",
+ h->host, this_count, result, sprint_tm(this_reply));
+
+ if (addr_cmp((struct sockaddr *)&response_addr, (struct sockaddr *)&h->saddr)) {
+ char buf[INET6_ADDRSTRLEN];
+ getnameinfo((struct sockaddr *)&response_addr, sizeof(response_addr), buf, INET6_ADDRSTRLEN, NULL, 0, NI_NUMERICHOST);
+ fprintf(stderr, " [<- %s]", buf);
+ }
+ fprintf(stderr, "\n");
+ }
+ return 1;
+ }
+
+ /* discard reply if delay is larger than timeout
+ * (see also: github #32) */
+ if (this_reply > h->timeout) {
+ return 1;
+ }
+
+ /* update stats */
+ stats_add(h, this_count, 1, this_reply);
+ // TODO: move to stats_add?
+ if (!max_reply || this_reply > max_reply)
+ max_reply = this_reply;
+ if (!min_reply || this_reply < min_reply)
+ min_reply = this_reply;
+ sum_replies += this_reply;
+ total_replies++;
+
+ /* initialize timeout to initial timeout (without backoff) */
+ h->timeout = timeout;
+
+ /* remove timeout event */
+ struct event *timeout_event = host_get_timeout_event(h, this_count);
+ if (timeout_event) {
+ ev_remove(&event_queue_timeout, timeout_event);
+ }
+
+ /* print "is alive" */
+ if (h->num_recv == 1) {
+ num_alive++;
+ if (fast_reachable && num_alive >= min_reachable)
+ finish_requested = 1;
+
+ if (verbose_flag || alive_flag) {
+ printf("%s", h->host);
+
+ if (verbose_flag)
+ printf(" is alive");
+ }
+ }
+
+ /* print received ping (unless --quiet) */
+ if (per_recv_flag) {
+ if (timestamp_flag) {
+ print_timestamp_format(recv_time, timestamp_format_flag);
+ }
+ avg = h->total_time / h->num_recv;
+ printf("%-*s : [%d], %d bytes, %s ms",
+ max_hostname_len, h->host, this_count, result, sprint_tm(this_reply));
+ printf(" (%s avg, ", sprint_tm(avg));
+
+ if (h->num_recv <= h->num_sent) {
+ printf("%d%% loss)",
+ ((h->num_sent - h->num_recv) * 100) / h->num_sent);
+ }
+ else {
+ printf("%d%% return)",
+ (h->num_recv_total * 100) / h->num_sent);
+ }
+ }
+
+ if (verbose_flag || alive_flag || per_recv_flag) {
+
+ if (addr_cmp((struct sockaddr *)&response_addr, (struct sockaddr *)&h->saddr)) {
+ char buf[INET6_ADDRSTRLEN];
+ getnameinfo((struct sockaddr *)&response_addr, sizeof(response_addr), buf, INET6_ADDRSTRLEN, NULL, 0, NI_NUMERICHOST);
+ fprintf(stderr, " [<- %s]", buf);
+ }
+
+ if (icmp_request_typ == 13) {
+ printf("%s timestamps: Originate=%u Receive=%u Transmit=%u Localreceive=%u",
+ alive_flag ? "" : ",",
+ ip_header_otime_ms, ip_header_rtime_ms, ip_header_ttime_ms,
+ ms_since_midnight_utc(recv_time));
+ }
+
+ if(print_tos_flag) {
+ if(ip_header_tos != -1) {
+ printf(" (TOS %d)", ip_header_tos);
+ }
+ else {
+ printf(" (TOS unknown)");
+ }
+ }
+
+ if (print_ttl_flag) {
+ if(ip_header_ttl != -1) {
+ printf(" (TTL %d)", ip_header_ttl);
+ }
+ else {
+ printf(" (TTL unknown)");
+ }
+ }
+
+ if (elapsed_flag && !per_recv_flag)
+ printf(" (%s ms)", sprint_tm(this_reply));
+
+ printf("\n");
+ }
+
+ return 1;
+}
+
+/************************************************************
+
+ Function: add_name
+
+*************************************************************
+
+ Inputs: char* name
+
+ Description:
+
+ process input name for addition to target list
+ name can turn into multiple targets via multiple interfaces (-m)
+ or via NIS groups
+
+************************************************************/
+
+void add_name(char *name)
+{
+ struct addrinfo *res0, *res, hints;
+ int ret_ga;
+ char *printname;
+ char namebuf[256];
+ char addrbuf[256];
+
+ /* getaddrinfo */
+ memset(&hints, 0, sizeof(struct addrinfo));
+ hints.ai_flags = AI_UNUSABLE;
+ hints.ai_socktype = SOCK_RAW;
+ hints.ai_family = hints_ai_family;
+ if (hints_ai_family == AF_INET) {
+ hints.ai_protocol = IPPROTO_ICMP;
+ }
+#ifdef IPV6
+ else if (hints_ai_family == AF_INET6) {
+ hints.ai_protocol = IPPROTO_ICMPV6;
+ }
+#endif
+ else {
+ hints.ai_socktype = SOCK_STREAM;
+ hints.ai_protocol = 0;
+ }
+ ret_ga = getaddrinfo(name, NULL, &hints, &res0);
+ if (ret_ga) {
+ if (!quiet_flag)
+ print_warning("%s: %s\n", name, gai_strerror(ret_ga));
+ num_noaddress++;
+ return;
+ }
+
+ /* NOTE: we could/should loop with res on all addresses like this:
+ * for (res = res0; res; res = res->ai_next) {
+ * We don't do it yet, however, because is is an incompatible change
+ * (need to implement a separate option for this)
+ */
+ for (res = res0; res; res = res->ai_next) {
+ /* name_flag: addr -> name lookup requested) */
+ if (name_flag || rdns_flag) {
+ int do_rdns = rdns_flag ? 1 : 0;
+ if (name_flag) {
+ /* Was it a numerical address? Only then do a rdns-query */
+ struct addrinfo *nres;
+ hints.ai_flags = AI_NUMERICHOST;
+ if (getaddrinfo(name, NULL, &hints, &nres) == 0) {
+ do_rdns = 1;
+ freeaddrinfo(nres);
+ }
+ }
+
+ if (do_rdns && getnameinfo(res->ai_addr, res->ai_addrlen, namebuf, sizeof(namebuf) / sizeof(char), NULL, 0, 0) == 0) {
+ printname = namebuf;
+ }
+ else {
+ printname = name;
+ }
+ }
+ else {
+ printname = name;
+ }
+
+ /* addr_flag: name -> addr lookup requested */
+ if (addr_flag) {
+ int ret;
+ ret = getnameinfo(res->ai_addr, res->ai_addrlen, addrbuf,
+ sizeof(addrbuf) / sizeof(char), NULL, 0, NI_NUMERICHOST);
+ if (ret) {
+ if (!quiet_flag) {
+ print_warning("%s: can't forward-lookup address (%s)\n", name, gai_strerror(ret));
+ }
+ continue;
+ }
+
+ if (name_flag || rdns_flag) {
+ char nameaddrbuf[512 + 3];
+ snprintf(nameaddrbuf, sizeof(nameaddrbuf) / sizeof(char), "%s (%s)", printname, addrbuf);
+ add_addr(name, nameaddrbuf, res->ai_addr, res->ai_addrlen);
+ }
+ else {
+ add_addr(name, addrbuf, res->ai_addr, res->ai_addrlen);
+ }
+ }
+ else {
+ add_addr(name, printname, res->ai_addr, res->ai_addrlen);
+ }
+
+ if (!multif_flag) {
+ break;
+ }
+ }
+
+ freeaddrinfo(res0);
+}
+
+/************************************************************
+
+ Function: add_addr
+
+*************************************************************
+
+ Description:
+
+ add single address to list of hosts to be pinged
+
+************************************************************/
+
+void add_addr(char *name, char *host, struct sockaddr *ipaddr, socklen_t ipaddr_len)
+{
+ HOST_ENTRY *p;
+ int n;
+ int64_t *i;
+
+ p = (HOST_ENTRY *)calloc(1, sizeof(HOST_ENTRY));
+ if (!p)
+ crash_and_burn("can't allocate HOST_ENTRY");
+
+ p->name = strdup(name);
+ p->host = strdup(host);
+ memcpy(&p->saddr, ipaddr, ipaddr_len);
+ p->saddr_len = ipaddr_len;
+ p->timeout = timeout;
+ p->min_reply = 0;
+
+ if (netdata_flag) {
+ char *s = p->name;
+ while (*s) {
+ if (!isalnum(*s))
+ *s = '_';
+ s++;
+ }
+ }
+
+ if (strlen(p->host) > max_hostname_len)
+ max_hostname_len = strlen(p->host);
+
+ /* array for response time results */
+ if (!loop_flag) {
+ i = (int64_t *)malloc(trials * sizeof(int64_t));
+ if (!i)
+ crash_and_burn("can't allocate resp_times array");
+
+ for (n = 1; n < trials; n++)
+ i[n] = RESP_UNUSED;
+
+ p->resp_times = i;
+ }
+
+ /* allocate event storage */
+ p->event_storage_ping = (struct event *)calloc(event_storage_count, sizeof(struct event));
+ if (!p->event_storage_ping) {
+ errno_crash_and_burn("can't allocate event_storage_ping");
+ }
+ p->event_storage_timeout = (struct event *)calloc(event_storage_count, sizeof(struct event));
+ if (!p->event_storage_timeout) {
+ errno_crash_and_burn("can't allocate event_storage_timeout");
+ }
+
+ /* schedule first ping */
+ host_add_ping_event(p, 0, current_time_ns);
+
+ num_hosts++;
+}
+
+/************************************************************
+
+ Function: crash_and_burn
+
+*************************************************************
+
+ Inputs: char* message
+
+ Description:
+
+************************************************************/
+
+void crash_and_burn(char *message)
+{
+ fprintf(stderr, "%s: %s\n", prog, message);
+ exit(4);
+}
+
+/************************************************************
+
+ Function: errno_crash_and_burn
+
+*************************************************************
+
+ Inputs: char* message
+
+ Description:
+
+************************************************************/
+
+void errno_crash_and_burn(char *message)
+{
+ fprintf(stderr, "%s: %s : %s\n", prog, message, strerror(errno));
+ exit(4);
+}
+
+/************************************************************
+
+ Function: print_warning
+
+ Description: fprintf(stderr, ...), unless running with -q
+
+*************************************************************/
+
+void print_warning(char *format, ...)
+{
+ va_list args;
+ if (!quiet_flag) {
+ va_start(args, format);
+ vfprintf(stderr, format, args);
+ va_end(args);
+ }
+}
+
+/************************************************************
+
+ Function: sprint_tm
+
+*************************************************************
+
+ render nanosecond int64_t value into milliseconds string with three digits of
+ precision.
+
+************************************************************/
+
+const char *sprint_tm(int64_t ns)
+{
+ static char buf[10];
+ double t = (double)ns / 1e6;
+
+ if (t < 0.0) {
+ /* negative (unexpected) */
+ sprintf(buf, "%.2g", t);
+ }
+ else if (t < 1.0) {
+ /* <= 0.99 ms */
+ sprintf(buf, "%.3f", t);
+ }
+ else if (t < 10.0) {
+ /* 1.00 - 9.99 ms */
+ sprintf(buf, "%.2f", t);
+ }
+ else if (t < 100.0) {
+ /* 10.0 - 99.9 ms */
+ sprintf(buf, "%.1f", t);
+ }
+ else if (t < 1000000.0) {
+ /* 100 - 1'000'000 ms */
+ sprintf(buf, "%.0f", t);
+ }
+ else {
+ sprintf(buf, "%.3e", t);
+ }
+
+ return (buf);
+}
+
+/************************************************************
+
+ Function: addr_cmp
+
+*************************************************************/
+int addr_cmp(struct sockaddr *a, struct sockaddr *b)
+{
+ if (a->sa_family != b->sa_family) {
+ return a->sa_family - b->sa_family;
+ }
+ else {
+ if (a->sa_family == AF_INET) {
+ return ((struct sockaddr_in *)a)->sin_addr.s_addr - ((struct sockaddr_in *)b)->sin_addr.s_addr;
+ }
+ else if (a->sa_family == AF_INET6) {
+ return memcmp(&((struct sockaddr_in6 *)a)->sin6_addr,
+ &((struct sockaddr_in6 *)b)->sin6_addr,
+ sizeof(((struct sockaddr_in6 *)a)->sin6_addr));
+ }
+ }
+
+ return 0;
+}
+
+void host_add_ping_event(HOST_ENTRY *h, int index, int64_t ev_time)
+{
+ struct event *event = &h->event_storage_ping[index % event_storage_count];
+ event->host = h;
+ event->ping_index = index;
+ event->ev_time = ev_time;
+ ev_enqueue(&event_queue_ping, event);
+
+ dbg_printf("%s [%d]: add ping event in %.0f ms\n",
+ event->host->host, index, (ev_time - current_time_ns) / 1e6);
+}
+
+void host_add_timeout_event(HOST_ENTRY *h, int index, int64_t ev_time)
+{
+ struct event *event = &h->event_storage_timeout[index % event_storage_count];
+ event->host = h;
+ event->ping_index = index;
+ event->ev_time = ev_time;
+ ev_enqueue(&event_queue_timeout, event);
+
+ dbg_printf("%s [%d]: add timeout event in %.0f ms\n",
+ event->host->host, index, (ev_time - current_time_ns) / 1e6);
+}
+
+struct event *host_get_timeout_event(HOST_ENTRY *h, int index)
+{
+ return &h->event_storage_timeout[index % event_storage_count];
+}
+
+/************************************************************
+
+ Function: ev_enqueue
+
+ Enqueue an event
+
+ The queue is sorted by event->ev_time, so that queue->first always points to
+ the earliest event.
+
+ We start scanning the queue from the tail, because we assume
+ that new events mostly get inserted with a event time higher
+ than the others.
+
+*************************************************************/
+void ev_enqueue(struct event_queue *queue, struct event *event)
+{
+ struct event *i;
+ struct event *i_prev;
+
+ /* Empty list */
+ if (queue->last == NULL) {
+ event->ev_next = NULL;
+ event->ev_prev = NULL;
+ queue->first = event;
+ queue->last = event;
+ return;
+ }
+
+ /* Insert on tail? */
+ if (event->ev_time - queue->last->ev_time >= 0) {
+ event->ev_next = NULL;
+ event->ev_prev = queue->last;
+ queue->last->ev_next = event;
+ queue->last = event;
+ return;
+ }
+
+ /* Find insertion point */
+ i = queue->last;
+ while (1) {
+ i_prev = i->ev_prev;
+ if (i_prev == NULL || event->ev_time - i_prev->ev_time >= 0) {
+ event->ev_prev = i_prev;
+ event->ev_next = i;
+ i->ev_prev = event;
+ if (i_prev != NULL) {
+ i_prev->ev_next = event;
+ }
+ else {
+ queue->first = event;
+ }
+ return;
+ }
+ i = i_prev;
+ }
+}
+
+/************************************************************
+
+ Function: ev_dequeue
+
+*************************************************************/
+struct event *ev_dequeue(struct event_queue *queue)
+{
+ struct event *dequeued;
+
+ if (queue->first == NULL) {
+ return NULL;
+ }
+ dequeued = queue->first;
+ ev_remove(queue, dequeued);
+
+ return dequeued;
+}
+
+/************************************************************
+
+ Function: ev_remove
+
+*************************************************************/
+void ev_remove(struct event_queue *queue, struct event *event)
+{
+ if (queue->first == event) {
+ queue->first = event->ev_next;
+ }
+ if (queue->last == event) {
+ queue->last = event->ev_prev;
+ }
+ if (event->ev_prev) {
+ event->ev_prev->ev_next = event->ev_next;
+ }
+ if (event->ev_next) {
+ event->ev_next->ev_prev = event->ev_prev;
+ }
+ event->ev_prev = NULL;
+ event->ev_next = NULL;
+}
+
+/************************************************************
+
+ Function: print_human_readable_time from current_time_ns
+
+*************************************************************/
+void print_timestamp_format(int64_t current_time_ns, int timestamp_format)
+{
+ char time_buffer[100];
+ time_t current_time_s;
+ struct tm *local_time;
+
+ current_time_s = current_time_ns / 1000000000;
+ local_time = localtime(¤t_time_s);
+ switch(timestamp_format) {
+ case 1:
+ // timestamp-format ctime
+ strftime(time_buffer, sizeof(time_buffer), "%c", local_time);
+ printf("[%s] ", time_buffer);
+ break;
+ case 2:
+ // timestamp-format iso
+ strftime(time_buffer, sizeof(time_buffer), "%Y-%m-%dT%T%z", local_time);
+ printf("[%s] ", time_buffer);
+ break;
+ case 3:
+ // timestamp-format rfc3339
+ strftime(time_buffer, sizeof(time_buffer), "%Y-%m-%d %H:%M:%S", local_time);
+ printf("[%s] ", time_buffer);
+ break;
+ default:
+ printf("[%.5f] ", (double)current_time_ns / 1e9);
+ }
+}
+
+/************************************************************
+
+ Function: ms_since_midnight_utc
+
+*************************************************************
+
+ Input: int64_t: current UTC time in ns
+
+ Output: uint32_t: current time in ms since midnight UTC
+
+ Description:
+
+ Return ICMP Timestamp value corresponding to the given time value.
+ The given time value must be in UTC.
+
+*************************************************************/
+static uint32_t ms_since_midnight_utc(int64_t time_val)
+{
+ return (uint32_t)((time_val / 1000000) % (24 * 60 * 60 * 1000));
+}
+
+/************************************************************
+
+ Function: usage
+
+*************************************************************
+
+ Inputs: int: 0 if output on request, 1 if output because of wrong argument
+
+ Description:
+
+************************************************************/
+
+void usage(int is_error)
+{
+ FILE *out = is_error ? stderr : stdout;
+ fprintf(out, "Usage: %s [options] [targets...]\n", prog);
+ fprintf(out, "\n");
+ fprintf(out, "Probing options:\n");
+ fprintf(out, " -4, --ipv4 only ping IPv4 addresses\n");
+ fprintf(out, " -6, --ipv6 only ping IPv6 addresses\n");
+ fprintf(out, " -b, --size=BYTES amount of ping data to send, in bytes (default: %d)\n", DEFAULT_PING_DATA_SIZE);
+ fprintf(out, " -B, --backoff=N set exponential backoff factor to N (default: 1.5)\n");
+ fprintf(out, " -c, --count=N count mode: send N pings to each target and report stats\n");
+ fprintf(out, " -f, --file=FILE read list of targets from a file ( - means stdin)\n");
+ fprintf(out, " -g, --generate generate target list (only if no -f specified),\n");
+ fprintf(out, " limited to at most %d targets\n", MAX_GENERATE);
+ fprintf(out, " (give start and end IP in the target list, or a CIDR address)\n");
+ fprintf(out, " (ex. %s -g 192.168.1.0 192.168.1.255 or %s -g 192.168.1.0/24)\n", prog, prog);
+ fprintf(out, " -H, --ttl=N set the IP TTL value (Time To Live hops)\n");
+ fprintf(out, " -i, --interval=MSEC interval between sending ping packets (default: %.0f ms)\n", interval / 1e6);
+#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");
+ fprintf(out, " -M, --dontfrag set the Don't Fragment flag\n");
+ fprintf(out, " -O, --tos=N set the type of service (tos) flag on the ICMP packets\n");
+ fprintf(out, " -p, --period=MSEC interval between ping packets to one target (in ms)\n");
+ fprintf(out, " (in loop and count modes, default: %.0f ms)\n", perhost_interval / 1e6);
+ fprintf(out, " -r, --retry=N number of retries (default: %d)\n", DEFAULT_RETRY);
+ fprintf(out, " -R, --random random packet data (to foil link data compression)\n");
+ fprintf(out, " -S, --src=IP set source address\n");
+ fprintf(out, " -t, --timeout=MSEC individual target initial timeout (default: %.0f ms,\n", timeout / 1e6);
+ fprintf(out, " except with -l/-c/-C, where it's the -p period up to 2000 ms)\n");
+ fprintf(out, " --check-source discard replies not from target address\n");
+ fprintf(out, " --icmp-timestamp use ICMP Timestamp instead of ICMP Echo\n");
+ fprintf(out, "\n");
+ fprintf(out, "Output options:\n");
+ fprintf(out, " -a, --alive show targets that are alive\n");
+ fprintf(out, " -A, --addr show targets by address\n");
+ fprintf(out, " -C, --vcount=N same as -c, report results (not stats) in verbose format\n");
+ fprintf(out, " -d, --rdns show targets by name (force reverse-DNS lookup)\n");
+ fprintf(out, " -D, --timestamp print timestamp before each output line\n");
+ fprintf(out, " --timestamp-format=FORMAT show timestamp in the given format (-D required): ctime|iso|rfc3339\n");
+ fprintf(out, " -e, --elapsed show elapsed time on return packets\n");
+ fprintf(out, " -n, --name show targets by name (reverse-DNS lookup for target IPs)\n");
+ fprintf(out, " -N, --netdata output compatible for netdata (-l -Q are required)\n");
+ fprintf(out, " -o, --outage show the accumulated outage time (lost packets * packet interval)\n");
+ fprintf(out, " -q, --quiet quiet (don't show per-target/per-ping results)\n");
+ fprintf(out, " -Q, --squiet=SECS[,cumulative] same as -q, but add interval summary every SECS seconds,\n");
+ fprintf(out, " with 'cumulative', print stats since beginning\n");
+ fprintf(out, " -s, --stats print final stats\n");
+ fprintf(out, " -u, --unreach show targets that are unreachable\n");
+ fprintf(out, " -v, --version show version\n");
+ fprintf(out, " -x, --reachable=N shows if >=N hosts are reachable or not\n");
+ fprintf(out, " -X, --fast-reachable=N exits true immediately when N hosts are found\n");
+ fprintf(out, " --print-tos show received TOS value\n");
+ fprintf(out, " --print-ttl show IP TTL value\n");
+ exit(is_error);
+}
--- /dev/null
+#ifndef _FPING_H
+#define _FPING_H
+
+#define __APPLE_USE_RFC_3542 1
+
+#include <sys/socket.h>
+#include <sys/types.h>
+#include <netinet/in.h>
+
+/* this requires variadic macros, part of C99 */
+#if (defined(DEBUG) || defined(_DEBUG))
+extern int64_t current_time_ns;
+extern int trace_flag;
+#define dbg_printf(fmt, ...) do { if (trace_flag) { fprintf(stderr, "[%10.5f] ", (double)(current_time_ns / 1000)/1000000); fprintf(stderr, fmt, __VA_ARGS__); } } while (0)
+
+#else
+#define dbg_printf(fmt, ...)
+#endif
+
+
+/* fping.c */
+void crash_and_burn( char *message );
+void errno_crash_and_burn( char *message );
+int in_cksum( unsigned short *p, int n );
+extern int random_data_flag;
+
+/* socket.c */
+int open_ping_socket_ipv4(int *socktype);
+void init_ping_buffer_ipv4(size_t ping_data_size);
+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, uint8_t icmp_proto);
+#ifdef IPV6
+int open_ping_socket_ipv6(int *socktype);
+void init_ping_buffer_ipv6(size_t ping_data_size);
+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
+
+#endif
--- /dev/null
+
+/*
+ * Interval is the minimum amount of time between sending a ping packet to
+ * any host.
+ *
+ * Perhost_interval is the minimum amount of time between sending a ping
+ * packet to a particular responding host (when count is > 1)
+ *
+ * Timeout is the initial amount of time between sending a ping packet to
+ * a particular non-responding host.
+ *
+ * Retry is the number of ping packets to send to a non-responding host
+ * before giving up (in is-it-alive mode).
+ *
+ * Backoff factor is how much longer to wait on successive retries.
+ *
+ *
+ */
+
+/* constants */
+
+#ifndef DEFAULT_INTERVAL
+#define DEFAULT_INTERVAL 10 /* default time between packets (msec) */
+#endif
+
+#ifndef DEFAULT_PERHOST_INTERVAL /* default time between packets */
+#define DEFAULT_PERHOST_INTERVAL 1000 /* to a particular destination */
+#endif /* in counting/looping mode */
+
+#ifndef DEFAULT_TIMEOUT
+#define DEFAULT_TIMEOUT 500 /* individual host timeouts */
+#define AUTOTUNE_TIMEOUT_MAX 2000
+#endif
+
+
+#ifndef DEFAULT_RETRY
+#define DEFAULT_RETRY 3 /* number of times to retry a host */
+#endif
+
+#ifndef DEFAULT_SELECT_TIME
+#define DEFAULT_SELECT_TIME 10 /* default time to wait during select() */
+#endif
+
+#ifndef DEFAULT_BACKOFF_FACTOR
+#define DEFAULT_BACKOFF_FACTOR 1.5 /* exponential timeout factor */
+#endif
+#define MIN_BACKOFF_FACTOR 1.0 /* exponential timeout factor */
+#define MAX_BACKOFF_FACTOR 5.0 /* exponential timeout factor */
+
+#ifndef DNS_TIMEOUT
+#define DNS_TIMEOUT 1000 /* time in micro_sec for dns retry */
+#endif
--- /dev/null
+#include "optparse.h"
+
+#define MSG_INVALID "invalid option"
+#define MSG_MISSING "option requires an argument"
+#define MSG_TOOMANY "option takes no arguments"
+
+static int
+opterror(struct optparse *options, const char *message, const char *data)
+{
+ unsigned p = 0;
+ while (*message)
+ options->errmsg[p++] = *message++;
+ const char *sep = " -- '";
+ while (*sep)
+ options->errmsg[p++] = *sep++;
+ while (p < sizeof(options->errmsg) - 2 && *data)
+ options->errmsg[p++] = *data++;
+ options->errmsg[p++] = '\'';
+ options->errmsg[p++] = '\0';
+ return '?';
+}
+
+void optparse_init(struct optparse *options, char **argv)
+{
+ options->argv = argv;
+ options->permute = 1;
+ options->optind = 1;
+ options->subopt = 0;
+ options->optarg = 0;
+ options->errmsg[0] = '\0';
+}
+
+static inline int
+is_dashdash(const char *arg)
+{
+ return arg != 0 && arg[0] == '-' && arg[1] == '-' && arg[2] == '\0';
+}
+
+static inline int
+is_shortopt(const char *arg)
+{
+ return arg != 0 && arg[0] == '-' && arg[1] != '-' && arg[1] != '\0';
+}
+
+static inline int
+is_longopt(const char *arg)
+{
+ return arg != 0 && arg[0] == '-' && arg[1] == '-' && arg[2] != '\0';
+}
+
+static void
+permute(struct optparse *options, int index)
+{
+ char *nonoption = options->argv[index];
+ for (int i = index; i < options->optind - 1; i++)
+ options->argv[i] = options->argv[i + 1];
+ options->argv[options->optind - 1] = nonoption;
+}
+
+static int
+argtype(const char *optstring, char c)
+{
+ if (c == ':')
+ return -1;
+ for (; *optstring && c != *optstring; optstring++);
+ if (!*optstring)
+ return -1;
+ int count = OPTPARSE_NONE;
+ if (optstring[1] == ':')
+ count += optstring[2] == ':' ? 2 : 1;
+ return count;
+}
+
+int optparse(struct optparse *options, const char *optstring)
+{
+ options->errmsg[0] = '\0';
+ options->optopt = 0;
+ options->optarg = 0;
+ char *option = options->argv[options->optind];
+ if (option == 0) {
+ return -1;
+ } else if (is_dashdash(option)) {
+ options->optind++; /* consume "--" */
+ return -1;
+ } else if (!is_shortopt(option)) {
+ if (options->permute) {
+ int index = options->optind;
+ options->optind++;
+ int r = optparse(options, optstring);
+ permute(options, index);
+ options->optind--;
+ return r;
+ } else {
+ return -1;
+ }
+ }
+ option += options->subopt + 1;
+ options->optopt = option[0];
+ int type = argtype(optstring, option[0]);
+ char *next = options->argv[options->optind + 1];
+ switch (type) {
+ case -1: {
+ options->optind++;
+ char str[2] = {option[0]};
+ return opterror(options, MSG_INVALID, str);
+ }
+ case OPTPARSE_NONE:
+ if (option[1]) {
+ options->subopt++;
+ } else {
+ options->subopt = 0;
+ options->optind++;
+ }
+ return option[0];
+ case OPTPARSE_REQUIRED:
+ options->subopt = 0;
+ options->optind++;
+ if (option[1]) {
+ options->optarg = option + 1;
+ } else if (next != 0) {
+ options->optarg = next;
+ options->optind++;
+ } else {
+ options->optarg = 0;
+ char str[2] = {option[0]};
+ return opterror(options, MSG_MISSING, str);
+ }
+ return option[0];
+ case OPTPARSE_OPTIONAL:
+ options->subopt = 0;
+ options->optind++;
+ if (option[1])
+ options->optarg = option + 1;
+ else
+ options->optarg = 0;
+ return option[0];
+ }
+ return 0;
+}
+
+char *optparse_arg(struct optparse *options)
+{
+ options->subopt = 0;
+ char *option = options->argv[options->optind];
+ if (option != 0)
+ options->optind++;
+ return option;
+}
+
+static inline int
+longopts_end(const struct optparse_long *longopts, int i)
+{
+ return !longopts[i].longname && !longopts[i].shortname;
+}
+
+static void
+optstring_from_long(const struct optparse_long *longopts, char *optstring)
+{
+ char *p = optstring;
+ for (int i = 0; !longopts_end(longopts, i); i++) {
+ if (longopts[i].shortname) {
+ *p++ = longopts[i].shortname;
+ for (int a = 0; a < (int)longopts[i].argtype; a++)
+ *p++ = ':';
+ }
+ }
+ *p = '\0';
+}
+
+/* Unlike strcmp(), handles options containing "=". */
+static int
+longopts_match(const char *longname, const char *option)
+{
+ if (longname == 0)
+ return 0;
+ const char *a = option, *n = longname;
+ for (; *a && *n && *a != '='; a++, n++)
+ if (*a != *n)
+ return 0;
+ return *n == '\0' && (*a == '\0' || *a == '=');
+}
+
+/* Return the part after "=", or NULL. */
+static char *
+longopts_arg(char *option)
+{
+ for (; *option && *option != '='; option++);
+ if (*option == '=')
+ return option + 1;
+ else
+ return 0;
+}
+
+static int
+long_fallback(struct optparse *options,
+ const struct optparse_long *longopts,
+ int *longindex)
+{
+ char optstring[96 * 3 + 1]; /* 96 ASCII printable characters */
+ optstring_from_long(longopts, optstring);
+ int result = optparse(options, optstring);
+ if (longindex != 0) {
+ *longindex = -1;
+ if (result != -1)
+ for (int i = 0; !longopts_end(longopts, i); i++)
+ if (longopts[i].shortname == options->optopt)
+ *longindex = i;
+ }
+ return result;
+}
+
+int
+optparse_long(struct optparse *options,
+ const struct optparse_long *longopts,
+ int *longindex)
+{
+ char *option = options->argv[options->optind];
+ if (option == 0) {
+ return -1;
+ } else if (is_dashdash(option)) {
+ options->optind++; /* consume "--" */
+ return -1;
+ } else if (is_shortopt(option)) {
+ return long_fallback(options, longopts, longindex);
+ } else if (!is_longopt(option)) {
+ if (options->permute) {
+ int index = options->optind;
+ options->optind++;
+ int r = optparse_long(options, longopts, longindex);
+ permute(options, index);
+ options->optind--;
+ return r;
+ } else {
+ return -1;
+ }
+ }
+
+ /* Parse as long option. */
+ options->errmsg[0] = '\0';
+ options->optopt = 0;
+ options->optlongname = 0;
+ options->optarg = 0;
+ option += 2; /* skip "--" */
+ options->optind++;
+ for (int i = 0; !longopts_end(longopts, i); i++) {
+ const char *name = longopts[i].longname;
+ if (longopts_match(name, option)) {
+ options->optlongname = option;
+ if (longindex)
+ *longindex = i;
+ options->optopt = longopts[i].shortname;
+ char *arg = longopts_arg(option);
+ if (longopts[i].argtype == OPTPARSE_NONE && arg != 0) {
+ return opterror(options, MSG_TOOMANY, name);
+ } if (arg != 0) {
+ options->optarg = arg;
+ } else if (longopts[i].argtype == OPTPARSE_REQUIRED) {
+ options->optarg = options->argv[options->optind++];
+ if (options->optarg == 0)
+ return opterror(options, MSG_MISSING, name);
+ }
+ return options->optopt;
+ }
+ }
+ return opterror(options, MSG_INVALID, option);
+}
--- /dev/null
+#ifndef OPTPARSE_H
+#define OPTPARSE_H
+
+/**
+ * Optparse -- portable, reentrant, embeddable, getopt-like option parser
+ *
+ * The POSIX getopt() option parser has three fatal flaws. These flaws
+ * are solved by Optparse.
+ *
+ * 1) Parser state is stored entirely in global variables, some of
+ * which are static and inaccessible. This means only one thread can
+ * use getopt(). It also means it's not possible to recursively parse
+ * nested sub-arguments while in the middle of argument parsing.
+ * Optparse fixes this by storing all state on a local struct.
+ *
+ * 2) The POSIX standard provides no way to properly reset the parser.
+ * This means for portable code that getopt() is only good for one
+ * run, over one argv with one optstring. It also means subcommand
+ * options cannot be processed with getopt(). Most implementations
+ * provide a method to reset the parser, but it's not portable.
+ * Optparse provides an optparse_arg() function for stepping over
+ * subcommands and continuing parsing of options with another
+ * optstring. The Optparse struct itself can be passed around to
+ * subcommand handlers for additional subcommand option parsing. A
+ * full reset can be achieved by with an additional optparse_init().
+ *
+ * 3) Error messages are printed to stderr. This can be disabled with
+ * opterr, but the messages themselves are still inaccessible.
+ * Optparse solves this by writing an error message in its errmsg
+ * field. The downside to Optparse is that this error message will
+ * always be in English rather than the current locale.
+ *
+ * Optparse should be familiar with anyone accustomed to getopt(), and
+ * it could be a nearly drop-in replacement. The optstring is the same
+ * and the fields have the same names as the getopt() global variables
+ * (optarg, optind, optopt).
+ *
+ * Optparse also supports GNU-style long options with optparse_long().
+ * The interface is slightly different and simpler than getopt_long().
+ *
+ * By default, argv is permuted as it is parsed, moving non-option
+ * arguments to the end. This can be disabled by setting the `permute`
+ * field to 0 after initialization.
+ */
+
+struct optparse {
+ char **argv;
+ int permute;
+ int optind;
+ int optopt;
+ char *optlongname;
+ char *optarg;
+ char errmsg[64];
+ int subopt;
+};
+
+enum optparse_argtype { OPTPARSE_NONE, OPTPARSE_REQUIRED, OPTPARSE_OPTIONAL };
+
+struct optparse_long {
+ const char *longname;
+ int shortname;
+ enum optparse_argtype argtype;
+};
+
+/**
+ * Initializes the parser state.
+ */
+void optparse_init(struct optparse *options, char **argv);
+
+/**
+ * Read the next option in the argv array.
+ * @param optstring a getopt()-formatted option string.
+ * @return the next option character, -1 for done, or '?' for error
+ *
+ * Just like getopt(), a character followed by no colons means no
+ * argument. One colon means the option has a required argument. Two
+ * colons means the option takes an optional argument.
+ */
+int optparse(struct optparse *options, const char *optstring);
+
+/**
+ * Handles GNU-style long options in addition to getopt() options.
+ * This works a lot like GNU's getopt_long(). The last option in
+ * longopts must be all zeros, marking the end of the array. The
+ * longindex argument may be NULL.
+ */
+int
+optparse_long(struct optparse *options,
+ const struct optparse_long *longopts,
+ int *longindex);
+
+/**
+ * Used for stepping over non-option arguments.
+ * @return the next non-option argument, or NULL for no more arguments
+ *
+ * Argument parsing can continue with optparse() after using this
+ * function. That would be used to parse the options for the
+ * subcommand returned by optparse_arg(). This function allows you to
+ * ignore the value of optind.
+ */
+char *optparse_arg(struct optparse *options);
+
+#endif
--- /dev/null
+/*
+ * fping: fast-ping, file-ping, favorite-ping, funky-ping
+ *
+ * Ping a list of target hosts in a round robin fashion.
+ * A better ping overall.
+ *
+ * fping website: http://www.fping.org
+ *
+ * Current maintainer of fping: David Schweikert
+ * Please send suggestions and patches to: david@schweikert.ch
+ *
+ *
+ * Original author: Roland Schemers <schemers@stanford.edu>
+ * IPv6 Support: Jeroen Massar <jeroen@unfix.org / jeroen@ipng.nl>
+ * Improved main loop: David Schweikert <david@schweikert.ch>
+ * Debian Merge, TOS settings: Tobi Oetiker <tobi@oetiker.ch>
+ * Bugfixes, byte order & senseful seq.-numbers: Stephan Fuhrmann (stephan.fuhrmann AT 1und1.de)
+ *
+ *
+ * Redistribution and use in source and binary forms are permitted
+ * provided that the above copyright notice and this paragraph are
+ * duplicated in all such forms and that any documentation,
+ * advertising materials, and other materials related to such
+ * distribution and use acknowledge that the software was developed
+ * by Stanford University. The name of the University may not be used
+ * to endorse or promote products derived from this software without
+ * specific prior written permission.
+ * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED
+ * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE.
+ *
+ * seqmap.c: implementation of a mapping between sequence number and (host, ping_nr)
+ * we can't just use ping_nr*host_count + host_nr, because it can
+ * overflow the 16 bit of the icmp header field. See also:
+ * https://github.com/schweikert/fping/issues/48
+ */
+
+#include "config.h"
+#include "seqmap.h"
+#include "limits.h"
+#include "options.h"
+#include "fping.h"
+
+#include <inttypes.h>
+#include <stdio.h>
+#include <stdlib.h>
+
+/* description of the data structure used:
+ *
+ * - we assume that no more than SEQMAP_MAXSEQ (65535) pings are sent in
+ * the timeout interval (SEQMAP_TIMEOUT_IN_NS)
+ * - we store the values in an array with SEQMAP_MAXSEQ elements
+ * - current sequence number % SEQMAP_MAXSEQ gives the current index
+ * - when entering a value, we check that the current entry is expired
+ */
+
+static SEQMAP_VALUE* seqmap_map = NULL;
+static unsigned int seqmap_next_id = 0;
+
+#define SEQMAP_TIMEOUT_IN_NS INT64_C(10000000000)
+#define SEQMAP_UNASSIGNED_HOST_NR UINT_MAX
+
+void seqmap_init()
+{
+ seqmap_map = calloc(SEQMAP_MAXSEQ, sizeof(SEQMAP_VALUE));
+ if (seqmap_map == NULL) {
+ perror("malloc error (can't allocate seqmap_map)");
+ }
+}
+
+unsigned int seqmap_add(unsigned int host_nr, unsigned int ping_count, int64_t timestamp)
+{
+ unsigned int current_id;
+ SEQMAP_VALUE* next_value;
+
+ if (!seqmap_map) {
+ fprintf(stderr, "fping internal error: seqmap not initialized.\n");
+ exit(4);
+ }
+
+ /* check if expired (note that unused seqmap values will have fields set to
+ * 0, so will be seen as expired */
+ next_value = &seqmap_map[seqmap_next_id];
+ if (next_value->ping_ts != 0 && timestamp - next_value->ping_ts < SEQMAP_TIMEOUT_IN_NS) {
+ fprintf(stderr, "fping error: not enough sequence numbers available! (expire_timeout=%" PRId64 ", host_nr=%d, ping_count=%d, seqmap_next_id=%d)\n",
+ SEQMAP_TIMEOUT_IN_NS, host_nr, ping_count, seqmap_next_id);
+ exit(4);
+ }
+
+ /* store the value */
+ next_value->host_nr = host_nr;
+ next_value->ping_count = ping_count;
+ next_value->ping_ts = timestamp;
+
+ /* increase next id */
+ current_id = seqmap_next_id;
+ seqmap_next_id = (seqmap_next_id + 1) % SEQMAP_MAXSEQ;
+
+ dbg_printf("seqmap_add(host: %d, index: %d) -> %d\n", host_nr, ping_count, current_id);
+
+ return current_id;
+}
+
+SEQMAP_VALUE* seqmap_fetch(unsigned int id, int64_t now)
+{
+ SEQMAP_VALUE* value;
+
+ if (id >= SEQMAP_MAXSEQ) {
+ return NULL;
+ }
+
+ value = &seqmap_map[id];
+
+ /* verify that value is not expired */
+ if (now - value->ping_ts >= SEQMAP_TIMEOUT_IN_NS) {
+ dbg_printf("seqmap_fetch(%d) -> host: %d, index: %d -> DISCARDED %ld\n", id, value->host_nr, value->ping_count,
+ now - value->ping_ts);
+ return NULL;
+ }
+
+ dbg_printf("seqmap_fetch(%d) -> host: %d, index: %d\n", id, value->host_nr, value->ping_count);
+
+ return value;
+}
--- /dev/null
+#ifndef SEQMAP_H
+#define SEQMAP_H
+
+#include <sys/time.h>
+#include <stdint.h>
+
+typedef struct seqmap_value
+{
+ unsigned int host_nr;
+ unsigned int ping_count;
+ int64_t ping_ts;
+
+} SEQMAP_VALUE;
+
+#define SEQMAP_MAXSEQ 65535
+
+void seqmap_init();
+unsigned int seqmap_add(unsigned int host_nr, unsigned int ping_count, int64_t now);
+SEQMAP_VALUE *seqmap_fetch(unsigned int id, int64_t now);
+
+#endif
--- /dev/null
+/*
+ * fping: fast-ping, file-ping, favorite-ping, funky-ping
+ *
+ * Ping a list of target hosts in a round robin fashion.
+ * A better ping overall.
+ *
+ * fping website: http://www.fping.org
+ *
+ * Current maintainer of fping: David Schweikert
+ * Please send suggestions and patches to: david@schweikert.ch
+ *
+ *
+ * Original author: Roland Schemers <schemers@stanford.edu>
+ * IPv6 Support: Jeroen Massar <jeroen@unfix.org / jeroen@ipng.nl>
+ * Improved main loop: David Schweikert <david@schweikert.ch>
+ * Debian Merge, TOS settings: Tobi Oetiker <tobi@oetiker.ch>
+ * Bugfixes, byte order & senseful seq.-numbers: Stephan Fuhrmann (stephan.fuhrmann AT 1und1.de)
+ *
+ *
+ * Redistribution and use in source and binary forms are permitted
+ * provided that the above copyright notice and this paragraph are
+ * duplicated in all such forms and that any documentation,
+ * advertising materials, and other materials related to such
+ * distribution and use acknowledge that the software was developed
+ * by Stanford University. The name of the University may not be used
+ * to endorse or promote products derived from this software without
+ * specific prior written permission.
+ * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED
+ * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE.
+ */
+
+#include "config.h"
+#include "fping.h"
+
+#include <fcntl.h>
+#include <netdb.h>
+#include <netinet/in.h>
+#include <netinet/in_systm.h>
+#include <netinet/ip.h>
+#include <netinet/ip_icmp.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/socket.h>
+#include <time.h>
+
+char* ping_buffer_ipv4 = 0;
+size_t ping_pkt_size_ipv4;
+
+int open_ping_socket_ipv4(int *socktype)
+{
+ struct protoent* proto;
+ int s;
+
+ /* confirm that ICMP is available on this machine */
+ if ((proto = getprotobyname("icmp")) == NULL)
+ crash_and_burn("icmp: unknown protocol");
+
+ /* create raw socket for ICMP calls (ping) */
+ *socktype = SOCK_RAW;
+ s = socket(AF_INET, *socktype, proto->p_proto);
+ if (s < 0) {
+ /* try non-privileged icmp (works on Mac OSX without privileges, for example) */
+ *socktype = SOCK_DGRAM;
+ s = socket(AF_INET, *socktype, proto->p_proto);
+ if (s < 0) {
+ return -1;
+ }
+ }
+
+ /* Make sure that we use non-blocking IO */
+ {
+ int flags;
+
+ if ((flags = fcntl(s, F_GETFL, 0)) < 0)
+ perror("fcntl");
+
+ if (fcntl(s, F_SETFL, flags | O_NONBLOCK) < 0)
+ perror("fcntl");
+ }
+
+ return s;
+}
+
+void init_ping_buffer_ipv4(size_t ping_data_size)
+{
+ /* allocate ping buffer */
+ ping_pkt_size_ipv4 = ping_data_size + ICMP_MINLEN;
+ ping_buffer_ipv4 = (char*)calloc(1, ping_pkt_size_ipv4);
+ if (!ping_buffer_ipv4)
+ crash_and_burn("can't malloc ping packet");
+}
+
+void socket_set_src_addr_ipv4(int s, struct in_addr* src_addr, int *ident)
+{
+ struct sockaddr_in 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, len) < 0)
+ errno_crash_and_burn("cannot bind source address");
+
+ if (ident) {
+ 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)
+{
+ unsigned long sum;
+
+ /* initialize sum to zero and loop until length (in words) is 0 */
+ for (sum = 0; length > 1; length -= 2) /* sizeof() returns number of bytes, we're interested in number of words */
+ sum += *buffer++; /* add 1 word of buffer to sum and proceed to the next */
+
+ /* we may have an extra byte */
+ if (length == 1)
+ sum += (char)*buffer;
+
+ sum = (sum >> 16) + (sum & 0xFFFF); /* add high 16 to low 16 */
+ sum += (sum >> 16); /* add carry */
+ return ~sum;
+}
+
+int socket_sendto_ping_ipv4(int s, struct sockaddr* saddr, socklen_t saddr_len, uint16_t icmp_seq_nr, uint16_t icmp_id_nr, uint8_t icmp_proto)
+{
+ struct icmp* icp;
+ struct timespec tsorig;
+ long tsorig_ms;
+ int n;
+
+ icp = (struct icmp*)ping_buffer_ipv4;
+
+ icp->icmp_type = icmp_proto;
+ if(icmp_proto == ICMP_TSTAMP) {
+ clock_gettime(CLOCK_REALTIME, &tsorig);
+ tsorig_ms = (tsorig.tv_sec % (24*60*60)) * 1000 + tsorig.tv_nsec / 1000000;
+ icp->icmp_otime = htonl(tsorig_ms);
+ icp->icmp_rtime = 0;
+ icp->icmp_ttime = 0;
+ }
+ icp->icmp_code = 0;
+ icp->icmp_cksum = 0;
+ icp->icmp_seq = htons(icmp_seq_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) {
+ ping_buffer_ipv4[n] = random() & 0xFF;
+ }
+ }
+
+ icp->icmp_cksum = calcsum((unsigned short*)icp, ping_pkt_size_ipv4);
+
+ n = sendto(s, icp, ping_pkt_size_ipv4, 0, saddr, saddr_len);
+
+ return n;
+}
--- /dev/null
+/*
+ * fping: fast-ping, file-ping, favorite-ping, funky-ping
+ *
+ * Ping a list of target hosts in a round robin fashion.
+ * A better ping overall.
+ *
+ * fping website: http://www.fping.org
+ *
+ * Current maintainer of fping: David Schweikert
+ * Please send suggestions and patches to: david@schweikert.ch
+ *
+ *
+ * Original author: Roland Schemers <schemers@stanford.edu>
+ * IPv6 Support: Jeroen Massar <jeroen@unfix.org / jeroen@ipng.nl>
+ * Improved main loop: David Schweikert <david@schweikert.ch>
+ * Debian Merge, TOS settings: Tobi Oetiker <tobi@oetiker.ch>
+ * Bugfixes, byte order & senseful seq.-numbers: Stephan Fuhrmann (stephan.fuhrmann AT 1und1.de)
+ *
+ *
+ * Redistribution and use in source and binary forms are permitted
+ * provided that the above copyright notice and this paragraph are
+ * duplicated in all such forms and that any documentation,
+ * advertising materials, and other materials related to such
+ * distribution and use acknowledge that the software was developed
+ * by Stanford University. The name of the University may not be used
+ * to endorse or promote products derived from this software without
+ * specific prior written permission.
+ * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED
+ * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE.
+ */
+
+#include "config.h"
+#include "fping.h"
+
+#include <fcntl.h>
+#include <netdb.h>
+#include <netinet/in.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/socket.h>
+
+#include <netinet/icmp6.h>
+
+char* ping_buffer_ipv6 = 0;
+size_t ping_pkt_size_ipv6;
+
+int open_ping_socket_ipv6(int *socktype)
+{
+ struct protoent* proto;
+ int s;
+
+ /* confirm that ICMP6 is available on this machine */
+ if ((proto = getprotobyname("ipv6-icmp")) == NULL)
+ crash_and_burn("ipv6-icmp: unknown protocol");
+
+ /* create raw socket for ICMP6 calls (ping) */
+ *socktype = SOCK_RAW;
+ s = socket(AF_INET6, *socktype, proto->p_proto);
+ if (s < 0) {
+ /* try non-privileged icmp6 (works on Mac OSX without privileges, for example) */
+ *socktype = SOCK_DGRAM;
+ s = socket(AF_INET6, *socktype, proto->p_proto);
+ if (s < 0) {
+ return -1;
+ }
+ }
+
+ /* Make sure that we use non-blocking IO */
+ {
+ int flags;
+
+ if ((flags = fcntl(s, F_GETFL, 0)) < 0)
+ perror("fcntl");
+
+ if (fcntl(s, F_SETFL, flags | O_NONBLOCK) < 0)
+ perror("fcntl");
+ }
+
+ return s;
+}
+
+void init_ping_buffer_ipv6(size_t ping_data_size)
+{
+ /* allocate ping buffer */
+ ping_pkt_size_ipv6 = ping_data_size + sizeof(struct icmp6_hdr);
+ ping_buffer_ipv6 = (char*)calloc(1, ping_pkt_size_ipv6);
+ if (!ping_buffer_ipv6)
+ crash_and_burn("can't malloc ping packet");
+}
+
+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");
+
+ if (ident) {
+ memset(&sa, 0, len);
+ if (getsockname(s, (struct sockaddr *)&sa, &len) < 0)
+ errno_crash_and_burn("can't get ICMP6 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)
+{
+ struct icmp6_hdr* icp;
+ int n;
+
+ icp = (struct icmp6_hdr*)ping_buffer_ipv6;
+ icp->icmp6_type = ICMP6_ECHO_REQUEST;
+ icp->icmp6_code = 0;
+ icp->icmp6_seq = htons(icmp_seq_nr);
+ icp->icmp6_id = icmp_id_nr;
+
+ if (random_data_flag) {
+ for (n = sizeof(struct icmp6_hdr); n < ping_pkt_size_ipv6; ++n) {
+ ping_buffer_ipv6[n] = random() & 0xFF;
+ }
+ }
+
+ icp->icmp6_cksum = 0; /* The IPv6 stack calculates the checksum for us... */
+
+ n = sendto(s, icp, ping_pkt_size_ipv6, 0, saddr, saddr_len);
+
+ return n;
+}