From: octo Date: Mon, 8 May 2006 14:16:34 +0000 (+0000) Subject: Created standard subversion directories. X-Git-Tag: liboping-0.1.0~1 X-Git-Url: https://git.tokkee.org/?p=liboping.git;a=commitdiff_plain;h=6995e2cfd517af456904db26b7781b4953feeacc Created standard subversion directories. Imported everything from the collectd repository. --- 6995e2cfd517af456904db26b7781b4953feeacc diff --git a/AUTHORS b/AUTHORS new file mode 100644 index 0000000..e69de29 diff --git a/ChangeLog b/ChangeLog new file mode 100644 index 0000000..bc96f7a --- /dev/null +++ b/ChangeLog @@ -0,0 +1,2 @@ +2006-05-08, Version 0.1.0 + * Initial release. diff --git a/Makefile.am b/Makefile.am new file mode 100644 index 0000000..4c53dab --- /dev/null +++ b/Makefile.am @@ -0,0 +1,7 @@ +SUBDIRS = libltdl src +INCLUDES = $(LTDLINCL) + +EXTRA_DIST = debian + +dist-hook: + find $(distdir) -type d -name '.svn' | xargs rm -rf diff --git a/NEWS b/NEWS new file mode 100644 index 0000000..e69de29 diff --git a/README b/README new file mode 100644 index 0000000..7d2e8d4 --- /dev/null +++ b/README @@ -0,0 +1,28 @@ + liboping - Library to ping IPv4 and IPv6 hosts in parallel +============================================================ +http://verplant.org/liboping/ + +About +----- + + liboping was inspired by ping, libping and fping: It differs from these + existing solutions in that it can `ping' multiple hosts in parallel using + IPv4 or IPv6 transparently. Other design principles were an object oriented + interface, simplicity and extensibility. + + +Features +-------- + + * Support for multiple hosts. + + * Support for IPv4 and IPv6. + + * Object oriented interface. + + +Author +------ + + Florian octo Forster + diff --git a/ToDo b/ToDo new file mode 100644 index 0000000..e69de29 diff --git a/configure.ac b/configure.ac new file mode 100644 index 0000000..ab94506 --- /dev/null +++ b/configure.ac @@ -0,0 +1,143 @@ +AC_INIT(liboping, 0.1.0) +AC_CONFIG_SRCDIR(src/liboping.c) +AC_CONFIG_HEADERS(src/config.h) +AM_INIT_AUTOMAKE(dist-bzip2) +AC_LANG(C) + +AC_PREFIX_DEFAULT("/opt/oping") + +# +# Check for programs/utilities +# +AC_PROG_CC +AC_PROG_CPP +AC_PROG_INSTALL +AC_PROG_LN_S +AC_PROG_MAKE_SET +AM_CONDITIONAL(COMPILER_IS_GCC, test "x$GCC" = "xyes") + +# +# configure libtool +# +AC_LIBLTDL_CONVENIENCE +AC_SUBST(LTDLINCL) +AC_SUBST(LIBLTDL) +AC_LIBTOOL_DLOPEN +AC_PROG_LIBTOOL +#AC_PROG_RANLIB +AC_CONFIG_SUBDIRS(libltdl src) + +# +# Checks for header files. +# +AC_HEADER_STDC +AC_CHECK_HEADERS(unistd.h) +AC_CHECK_HEADERS(math.h) +AC_CHECK_HEADERS(fcntl.h) +AC_CHECK_HEADERS(sys/types.h) +AC_CHECK_HEADERS(sys/stat.h) +AC_HEADER_TIME +AC_CHECK_HEADERS(sys/socket.h) +AC_CHECK_HEADERS(netdb.h) +AC_CHECK_HEADERS(signal.h) + +# This sucks, but what can I do..? +AC_CHECK_HEADERS(netinet/in_systm.h, [], [], +[#if HAVE_STDINT_H +# include +#endif +]) +AC_CHECK_HEADERS(netinet/in.h, [], [], +[#if HAVE_STDINT_H +# include +#endif +#if HAVE_NETINET_IN_SYSTM_H +# include +#endif +]) +AC_CHECK_HEADERS(netinet/ip.h, [], [], +[#if HAVE_STDINT_H +# include +#endif +#if HAVE_NETINET_IN_SYSTM_H +# include +#endif +#if HAVE_NETINET_IN_H +# include +#endif +]) +AC_CHECK_HEADERS(netinet/ip_icmp.h, [], [], +[#if HAVE_STDINT_H +# include +#endif +#if HAVE_NETINET_IN_SYSTM_H +# include +#endif +#if HAVE_NETINET_IN_H +# include +#endif +#if HAVE_NETINET_IP_H +# include +#endif +]) +AC_CHECK_HEADERS(netinet/ip_var.h, [], [], +[#if HAVE_STDINT_H +# include +#endif +#if HAVE_NETINET_IN_SYSTM_H +# include +#endif +#if HAVE_NETINET_IN_H +# include +#endif +#if HAVE_NETINET_IP_H +# include +#endif +]) +AC_CHECK_HEADERS(netinet/ip6.h, [], [], +[#if HAVE_STDINT_H +# include +#endif +#if HAVE_SYS_TYPES_H +# include +#endif +#if HAVE_NETINET_IN_SYSTM_H +# include +#endif +#if HAVE_NETINET_IN_H +# include +#endif +]) +AC_CHECK_HEADERS(netinet/icmp6.h, [], [], +[#if HAVE_STDINT_H +# include +#endif +#if HAVE_SYS_TYPES_H +# include +#endif +#if HAVE_NETINET_IN_SYSTM_H +# include +#endif +#if HAVE_NETINET_IN_H +# include +#endif +#if HAVE_NETINET_IP6_H +# include +#endif +]) + +socket_needs_socket="no" +AC_CHECK_FUNCS(socket, [], + AC_CHECK_LIB(socket, socket, + [socket_needs_socket="yes"], + AC_MSG_ERROR(cannot find socket))) +AM_CONDITIONAL(BUILD_WITH_LIBSOCKET, test "x$socket_needs_socket" = "xyes") + +nanosleep_needs_rt="no" +AC_CHECK_FUNCS(nanosleep, [], + AC_CHECK_LIB(rt, nanosleep, + [nanosleep_needs_rt="yes"], + AC_MSG_ERROR(cannot find nanosleep))) +AM_CONDITIONAL(BUILD_WITH_LIBRT, test "x$nanosleep_needs_rt" = "xyes") + +AC_OUTPUT(Makefile src/Makefile src/mans/Makefile) diff --git a/debian/README b/debian/README new file mode 100644 index 0000000..252cfc2 --- /dev/null +++ b/debian/README @@ -0,0 +1,6 @@ +The Debian Package liboping +---------------------------- + +Comments regarding the Package + +Florian Forster , Sun, 30 Apr 2006 18:46:29 +0200 diff --git a/debian/changelog b/debian/changelog new file mode 100644 index 0000000..e5f3994 --- /dev/null +++ b/debian/changelog @@ -0,0 +1,5 @@ +liboping (0.1.0) unstable; urgency=low + + * Initial Release. + + -- Florian Forster Sun, 30 Apr 2006 18:46:29 +0200 diff --git a/debian/compat b/debian/compat new file mode 100644 index 0000000..b8626c4 --- /dev/null +++ b/debian/compat @@ -0,0 +1 @@ +4 diff --git a/debian/control b/debian/control new file mode 100644 index 0000000..de3d2c9 --- /dev/null +++ b/debian/control @@ -0,0 +1,36 @@ +Source: liboping +Section: libs +Priority: optional +Maintainer: Florian Forster +Build-Depends: debhelper (>= 4.0.0), autotools-dev +Standards-Version: 3.6.2 + +Package: liboping +Section: libs +Architecture: any +Depends: ${shlibs:Depends} +Description: Library to send ICMPv4/ICMPv6 echo packets to multiple hosts + liboping was inspired by ping, libping and fping: It differs from these + existing solutions in that it can `ping' multiple hosts in parallel using IPv4 + or IPv6 transparently. Other design principles were an object oriented + interface, simplicity and extensibility. + +Package: liboping-dev +Section: libdevel +Architecture: any +Depends: liboping (= ${Source-Version}) +Description: Development files for the liboping library + liboping was inspired by ping, libping and fping: It differs from these + existing solutions in that it can `ping' multiple hosts in parallel using IPv4 + or IPv6 transparently. Other design principles were an object oriented + interface, simplicity and extensibility. + +Package: oping +Section: net +Architecture: any +Depends: liboping (= ${Source-Version}), ${shlibs:Depends} +Description: Sends ICMPv4/ICMPv6 echo packets to multiple remote hosts + liboping was inspired by ping, libping and fping: It differs from these + existing solutions in that it can `ping' multiple hosts in parallel using IPv4 + or IPv6 transparently. Other design principles were an object oriented + interface, simplicity and extensibility. diff --git a/debian/copyright b/debian/copyright new file mode 100644 index 0000000..1cf8421 --- /dev/null +++ b/debian/copyright @@ -0,0 +1,25 @@ +This is liboping, written and maintained by Florian Forster +on Sun, 30 Apr 2006 18:46:29 +0200. + +The original source can always be found at: + http://verplant.org/ + +Copyright (C) 2006 Florian Forster + +License: + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License, version 2, as published + by the Free Software Foundation. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this package; if not, write to the Free Software + Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + +On Debian systems, the complete text of the GNU General +Public License can be found in `/usr/share/common-licenses/GPL'. diff --git a/debian/liboping-dev.dirs b/debian/liboping-dev.dirs new file mode 100644 index 0000000..c7147dc --- /dev/null +++ b/debian/liboping-dev.dirs @@ -0,0 +1,3 @@ +usr/lib +usr/include +usr/share/man diff --git a/debian/liboping-dev.docs b/debian/liboping-dev.docs new file mode 100644 index 0000000..dc63960 --- /dev/null +++ b/debian/liboping-dev.docs @@ -0,0 +1,6 @@ +AUTHORS +COPYING +ChangeLog +INSTALL +NEWS +README diff --git a/debian/liboping-dev.manpages b/debian/liboping-dev.manpages new file mode 100644 index 0000000..c1e9e83 --- /dev/null +++ b/debian/liboping-dev.manpages @@ -0,0 +1,8 @@ +src/mans/liboping.3 +src/mans/ping_construct.3 +src/mans/ping_get_error.3 +src/mans/ping_host_add.3 +src/mans/ping_iterator_get.3 +src/mans/ping_iterator_get_context.3 +src/mans/ping_iterator_get_info.3 +src/mans/ping_send.3 diff --git a/debian/liboping.dirs b/debian/liboping.dirs new file mode 100644 index 0000000..6845771 --- /dev/null +++ b/debian/liboping.dirs @@ -0,0 +1 @@ +usr/lib diff --git a/debian/liboping.docs b/debian/liboping.docs new file mode 100644 index 0000000..dc63960 --- /dev/null +++ b/debian/liboping.docs @@ -0,0 +1,6 @@ +AUTHORS +COPYING +ChangeLog +INSTALL +NEWS +README diff --git a/debian/oping.dirs b/debian/oping.dirs new file mode 100644 index 0000000..e772481 --- /dev/null +++ b/debian/oping.dirs @@ -0,0 +1 @@ +usr/bin diff --git a/debian/oping.docs b/debian/oping.docs new file mode 100644 index 0000000..dc63960 --- /dev/null +++ b/debian/oping.docs @@ -0,0 +1,6 @@ +AUTHORS +COPYING +ChangeLog +INSTALL +NEWS +README diff --git a/debian/oping.manpages b/debian/oping.manpages new file mode 100644 index 0000000..06d379e --- /dev/null +++ b/debian/oping.manpages @@ -0,0 +1 @@ +src/mans/oping.8 diff --git a/debian/rules b/debian/rules new file mode 100755 index 0000000..d5280eb --- /dev/null +++ b/debian/rules @@ -0,0 +1,120 @@ +#!/usr/bin/make -f +# -*- makefile -*- +# Sample debian/rules that uses debhelper. +# +# This file was originally written by Joey Hess and Craig Small. +# As a special exception, when this file is copied by dh-make into a +# dh-make output file, you may use that output file without restriction. +# This special exception was added by Craig Small in version 0.37 of dh-make. +# +# Modified to make a template file for a multi-binary package with separated +# build-arch and build-indep targets by Bill Allombert 2001 + +# Uncomment this to turn on verbose mode. +#export DH_VERBOSE=1 + +# This has to be exported to make some magic below work. +export DH_OPTIONS + +# These are used for cross-compiling and for saving the configure script +# from having to guess our platform (since we know it already) +DEB_HOST_GNU_TYPE ?= $(shell dpkg-architecture -qDEB_HOST_GNU_TYPE) +DEB_BUILD_GNU_TYPE ?= $(shell dpkg-architecture -qDEB_BUILD_GNU_TYPE) + + +CFLAGS = -Wall -g + +ifneq (,$(findstring noopt,$(DEB_BUILD_OPTIONS))) + CFLAGS += -O0 +else + CFLAGS += -O2 +endif + +version=0.1.0 +major=0 + +config.status: configure + dh_testdir + CFLAGS="$(CFLAGS) -Wl,-z,defs" ./configure --host=$(DEB_HOST_GNU_TYPE) --build=$(DEB_BUILD_GNU_TYPE) --prefix=/usr --mandir=\$${prefix}/share/man --infodir=\$${prefix}/share/info + + +#Architecture +build: build-arch + +build-arch: build-arch-stamp + +build-arch-stamp: config.status + $(MAKE) + touch build-arch-stamp + +clean: + dh_testdir + dh_testroot + rm -f build-arch-stamp + + # Add here commands to clean up after the build process. + -$(MAKE) distclean +ifneq "$(wildcard /usr/share/misc/config.sub)" "" + cp -f /usr/share/misc/config.sub config.sub +endif +ifneq "$(wildcard /usr/share/misc/config.guess)" "" + cp -f /usr/share/misc/config.guess config.guess +endif + rm -rf $(CURDIR)/debian/tmp || true + + dh_clean + +install: install-arch + +install-arch: + dh_testdir + dh_testroot + dh_clean -k -s + dh_installdirs --all + + $(MAKE) install DESTDIR=$(CURDIR)/debian/tmp + mv -v $(CURDIR)/debian/tmp/usr/lib/*.so $(CURDIR)/debian/liboping/usr/lib/ + mv -v $(CURDIR)/debian/tmp/usr/lib/*.{a,la} $(CURDIR)/debian/liboping-dev/usr/lib/ + mv -v $(CURDIR)/debian/tmp/usr/include/*.h $(CURDIR)/debian/liboping-dev/usr/include/ + mv -v $(CURDIR)/debian/tmp/usr/bin/* $(CURDIR)/debian/oping/usr/bin/ + + dh_install -s + +# Must not depend on anything. This is to be called by +# binary-arch/binary-indep +# in another 'make' thread. +binary-common: + dh_testdir + dh_testroot + dh_installchangelogs ChangeLog + dh_installdocs + dh_installexamples +# dh_installmenu +# dh_installdebconf +# dh_installlogrotate +# dh_installemacsen +# dh_installpam +# dh_installmime +# dh_installinit +# dh_installcron +# dh_installinfo + dh_installman + dh_link + dh_strip + dh_compress + dh_fixperms +# dh_perl +# dh_python + dh_makeshlibs + dh_installdeb + dh_shlibdeps + dh_gencontrol + dh_md5sums + dh_builddeb + +# Build architecture dependant packages using the common target. +binary-arch: build-arch install-arch + $(MAKE) -f debian/rules DH_OPTIONS=-a binary-common + +binary: binary-arch +.PHONY: build clean binary-arch binary install install-arch diff --git a/src/Makefile.am b/src/Makefile.am new file mode 100644 index 0000000..1b440d4 --- /dev/null +++ b/src/Makefile.am @@ -0,0 +1,26 @@ +AUTOMAKE_OPTIONS = foreign no-dependencies + +SUBDIRS = mans + +if COMPILER_IS_GCC +AM_CFLAGS = -Wall -Werror +endif + +include_HEADERS = oping.h +lib_LTLIBRARIES = liboping.la + +#liboping_la_CFLAGS = +liboping_la_LDFLAGS = -avoid-version +if BUILD_WITH_LIBSOCKET +liboping_la_LDFLAGS += -lsocket +endif +liboping_la_SOURCES = oping.h liboping.c + +bin_PROGRAMS = oping + +oping_SOURCES = oping.c +oping_LDADD = liboping.la +oping_LDFLAGS = -lm +if BUILD_WITH_LIBRT +oping_LDFLAGS += -lrt +endif diff --git a/src/liboping.c b/src/liboping.c new file mode 100644 index 0000000..9e9d6c2 --- /dev/null +++ b/src/liboping.c @@ -0,0 +1,1172 @@ +/** + * Object oriented C module to send ICMP and ICMPv6 `echo's. + * Copyright (C) 2006 Florian octo Forster + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#if HAVE_CONFIG_H +# include +#endif + +#if STDC_HEADERS +# include +# include +# include +# include +# include +#else +# error "You don't have the standard C99 header files installed" +#endif /* STDC_HEADERS */ + +#if HAVE_UNISTD_H +# include +#endif + +#ifndef __USE_BSD +# define __USE_BSD +#endif + +#if HAVE_FCNTL_H +# include +#endif +#if HAVE_SYS_TYPES_H +# include +#endif +#if HAVE_SYS_STAT_H +# include +#endif + +#if TIME_WITH_SYS_TIME +# include +# include +#else +# if HAVE_SYS_TIME_H +# include +# else +# include +# endif +#endif + +#if HAVE_SYS_SOCKET_H +# include +#endif +#if HAVE_NETDB_H +# include +#endif + +#if HAVE_NETINET_IN_SYSTM_H +# include +#endif +#if HAVE_NETINET_IN_H +# include +#endif +#if HAVE_NETINET_IP_H +# include +#endif +#if HAVE_NETINET_IP_ICMP_H +# include +#endif +#ifdef HAVE_NETINET_IP_VAR_H +# include +#endif +#if HAVE_NETINET_IP6_H +# include +#endif +#if HAVE_NETINET_ICMP6_H +# include +#endif + +#include "oping.h" + +#if DEBUG +# define dprintf(...) printf ("%s[%4i]: %-20s: ", __FILE__, __LINE__, __FUNCTION__); printf (__VA_ARGS__) +#else +# define dprintf(...) /**/ +#endif + +#define PING_ERRMSG_LEN 256 + +#define PING_DATA "Florian Forster http://verplant.org/" + +struct pinghost +{ + char *hostname; + struct sockaddr_storage *addr; + socklen_t addrlen; + int addrfamily; + int fd; + int ident; + int sequence; + struct timeval *timer; + double latency; + + void *context; + + struct pinghost *next; +}; + +struct pingobj +{ + double timeout; + int ttl; + int addrfamily; + + char errmsg[PING_ERRMSG_LEN]; + + pinghost_t *head; +}; + +/* + * private (static) functions + */ +static void ping_set_error (pingobj_t *obj, const char *function, + const char *message) +{ + snprintf (obj->errmsg, PING_ERRMSG_LEN, "%s: %s", function, message); + obj->errmsg[PING_ERRMSG_LEN - 1] = '\0'; +} + +static int ping_timeval_add (struct timeval *tv1, struct timeval *tv2, + struct timeval *res) +{ + res->tv_sec = tv1->tv_sec + tv2->tv_sec; + res->tv_usec = tv1->tv_usec + tv2->tv_usec; + + while (res->tv_usec > 1000000) + { + res->tv_usec -= 1000000; + res->tv_sec++; + } + + return (0); +} + +static int ping_timeval_sub (struct timeval *tv1, struct timeval *tv2, + struct timeval *res) +{ + + if ((tv1->tv_sec < tv2->tv_sec) + || ((tv1->tv_sec == tv2->tv_sec) + && (tv1->tv_usec < tv2->tv_usec))) + return (-1); + + res->tv_sec = tv1->tv_sec - tv2->tv_sec; + res->tv_usec = tv1->tv_usec - tv2->tv_usec; + + assert ((res->tv_sec > 0) || ((res->tv_sec == 0) && (res->tv_usec > 0))); + + while (res->tv_usec < 0) + { + res->tv_usec += 1000000; + res->tv_sec--; + } + + return (0); +} + +static uint16_t ping_icmp4_checksum (char *buf, size_t len) +{ + uint32_t sum = 0; + uint16_t ret = 0; + + uint16_t *ptr; + + for (ptr = (uint16_t *) buf; len > 1; ptr++, len -= 2) + sum += *ptr; + + if (len == 1) + { + *(char *) &ret = *(char *) ptr; + sum += ret; + } + + /* Do this twice to get all possible carries.. */ + sum = (sum >> 16) + (sum & 0xFFFF); + sum = (sum >> 16) + (sum & 0xFFFF); + + ret = ~sum; + + return (ret); +} + +static pinghost_t *ping_receive_ipv4 (pinghost_t *ph, char *buffer, size_t buffer_len) +{ + struct ip *ip_hdr; + struct icmp *icmp_hdr; + + size_t ip_hdr_len; + + uint16_t recv_checksum; + uint16_t calc_checksum; + + uint16_t ident; + uint16_t seq; + + pinghost_t *ptr; + + if (buffer_len < sizeof (struct ip)) + return (NULL); + + ip_hdr = (struct ip *) buffer; + ip_hdr_len = ip_hdr->ip_hl << 2; + + if (buffer_len < ip_hdr_len) + return (NULL); + + buffer += ip_hdr_len; + buffer_len -= ip_hdr_len; + + if (buffer_len < sizeof (struct icmp)) + return (NULL); + + icmp_hdr = (struct icmp *) buffer; + buffer += sizeof (struct icmp); + buffer_len -= sizeof (struct icmp); + + if (icmp_hdr->icmp_type != ICMP_ECHOREPLY) + { + dprintf ("Unexpected ICMP type: %i\n", icmp_hdr->icmp_type); + return (NULL); + } + + recv_checksum = icmp_hdr->icmp_cksum; + icmp_hdr->icmp_cksum = 0; + calc_checksum = ping_icmp4_checksum ((char *) icmp_hdr, + sizeof (struct icmp) + buffer_len); + + if (recv_checksum != calc_checksum) + { + dprintf ("Checksum missmatch: Got 0x%04x, calculated 0x%04x\n", + recv_checksum, calc_checksum); + return (NULL); + } + + ident = ntohs (icmp_hdr->icmp_id); + seq = ntohs (icmp_hdr->icmp_seq); + + for (ptr = ph; ptr != NULL; ptr = ptr->next) + { + dprintf ("hostname = %s, ident = 0x%04x, seq = %i\n", + ptr->hostname, ptr->ident, ptr->sequence - 1); + + if (ptr->addrfamily != AF_INET) + continue; + + if (!timerisset (ptr->timer)) + continue; + + if (ptr->ident != ident) + continue; + + if ((ptr->sequence - 1) != seq) + continue; + + dprintf ("Match found: hostname = %s, ident = 0x%04x, seq = %i\n", + ptr->hostname, ident, seq); + + break; + } + + if (ptr == NULL) + { + dprintf ("No match found for ident = 0x%04x, seq = %i\n", + ident, seq); + } + + return (ptr); +} + +static pinghost_t *ping_receive_ipv6 (pinghost_t *ph, char *buffer, size_t buffer_len) +{ + struct icmp6_hdr *icmp_hdr; + + uint16_t ident; + uint16_t seq; + + pinghost_t *ptr; + + if (buffer_len < sizeof (struct icmp6_hdr)) + return (NULL); + + icmp_hdr = (struct icmp6_hdr *) buffer; + buffer += sizeof (struct icmp); + buffer_len -= sizeof (struct icmp); + + if (icmp_hdr->icmp6_type != ICMP6_ECHO_REPLY) + { + dprintf ("Unexpected ICMP type: %02x\n", icmp_hdr->icmp6_type); + return (NULL); + } + + if (icmp_hdr->icmp6_code != 0) + { + dprintf ("Unexpected ICMP code: %02x\n", icmp_hdr->icmp6_code); + return (NULL); + } + + ident = ntohs (icmp_hdr->icmp6_id); + seq = ntohs (icmp_hdr->icmp6_seq); + + for (ptr = ph; ptr != NULL; ptr = ptr->next) + { + dprintf ("hostname = %s, ident = 0x%04x, seq = %i\n", + ptr->hostname, ptr->ident, ptr->sequence - 1); + + if (ptr->addrfamily != AF_INET6) + continue; + + if (!timerisset (ptr->timer)) + continue; + + if (ptr->ident != ident) + continue; + + if ((ptr->sequence - 1) != seq) + continue; + + dprintf ("Match found: hostname = %s, ident = 0x%04x, seq = %i\n", + ptr->hostname, ident, seq); + + break; + } + + if (ptr == NULL) + { + dprintf ("No match found for ident = 0x%04x, seq = %i\n", + ident, seq); + } + + return (ptr); +} + +static int ping_receive_one (int fd, pinghost_t *ph, struct timeval *now) +{ + char buffer[4096]; + size_t buffer_len; + + struct timeval diff; + + pinghost_t *host = NULL; + + struct sockaddr_storage sa; + socklen_t sa_len; + + sa_len = sizeof (sa); + + buffer_len = recvfrom (fd, buffer, sizeof (buffer), 0, + (struct sockaddr *) &sa, &sa_len); + if (buffer_len == -1) + { + dprintf ("recvfrom: %s\n", strerror (errno)); + return (-1); + } + + dprintf ("Read %i bytes from fd = %i\n", buffer_len, fd); + + if (sa.ss_family == AF_INET) + { + if ((host = ping_receive_ipv4 (ph, buffer, buffer_len)) == NULL) + return (-1); + } + else if (sa.ss_family == AF_INET6) + { + if ((host = ping_receive_ipv6 (ph, buffer, buffer_len)) == NULL) + return (-1); + } + + dprintf ("rcvd: %12i.%06i\n", + (int) now->tv_sec, + (int) now->tv_usec); + dprintf ("sent: %12i.%06i\n", + (int) host->timer->tv_sec, + (int) host->timer->tv_usec); + + if (ping_timeval_sub (now, host->timer, &diff) < 0) + { + timerclear (host->timer); + return (-1); + } + + dprintf ("diff: %12i.%06i\n", + (int) diff.tv_sec, + (int) diff.tv_usec); + + host->latency = ((double) diff.tv_usec) / 1000.0; + host->latency += ((double) diff.tv_sec) * 1000.0; + + timerclear (host->timer); + + return (0); +} + +static int ping_receive_all (pingobj_t *obj) +{ + fd_set readfds; + int num_readfds; + int max_readfds; + + pinghost_t *ph; + pinghost_t *ptr; + + struct timeval endtime; + struct timeval nowtime; + struct timeval timeout; + int status; + + int ret; + + ph = obj->head; + ret = 0; + + for (ptr = ph; ptr != NULL; ptr = ptr->next) + ptr->latency = -1.0; + + if (gettimeofday (&nowtime, NULL) == -1) + { + ping_set_error (obj, "gettimeofday", strerror (errno)); + return (-1); + } + + /* Set up timeout */ + timeout.tv_sec = (time_t) obj->timeout; + timeout.tv_usec = (suseconds_t) (1000000 * (obj->timeout - ((double) timeout.tv_sec))); + + dprintf ("Set timeout to %i.%06i seconds\n", + (int) timeout.tv_sec, + (int) timeout.tv_usec); + + ping_timeval_add (&nowtime, &timeout, &endtime); + + while (1) + { + FD_ZERO (&readfds); + num_readfds = 0; + max_readfds = -1; + + for (ptr = ph; ptr != NULL; ptr = ptr->next) + { + if (!timerisset (ptr->timer)) + continue; + + FD_SET (ptr->fd, &readfds); + num_readfds++; + + if (max_readfds < ptr->fd) + max_readfds = ptr->fd; + } + + if (num_readfds == 0) + break; + + if (gettimeofday (&nowtime, NULL) == -1) + { + ping_set_error (obj, "gettimeofday", strerror (errno)); + return (-1); + } + + if (ping_timeval_sub (&endtime, &nowtime, &timeout) == -1) + break; + + dprintf ("Waiting on %i sockets for %i.%06i seconds\n", num_readfds, + (int) timeout.tv_sec, + (int) timeout.tv_usec); + + status = select (max_readfds + 1, &readfds, NULL, NULL, &timeout); + + if (gettimeofday (&nowtime, NULL) == -1) + { + ping_set_error (obj, "gettimeofday", strerror (errno)); + return (-1); + } + + if ((status == -1) && (errno == EINTR)) + { + dprintf ("select was interrupted by signal..\n"); + continue; + } + else if (status < 0) + { + dprintf ("select: %s\n", strerror (errno)); + break; + } + else if (status == 0) + { + dprintf ("select timed out\n"); + break; + } + + for (ptr = ph; ptr != NULL; ptr = ptr->next) + { + if (FD_ISSET (ptr->fd, &readfds)) + if (ping_receive_one (ptr->fd, ph, &nowtime) == 0) + ret++; + } + } /* while (1) */ + + return (ret); +} + +/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * + * Sending functions: * + * * + * ping_send_all * + * +-> ping_send_one_ipv4 * + * `-> ping_send_one_ipv6 * + * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ +static ssize_t ping_sendto (pingobj_t *obj, pinghost_t *ph, + const void *buf, size_t buflen) +{ + ssize_t ret; + + if (gettimeofday (ph->timer, NULL) == -1) + { + timerclear (ph->timer); + return (-1); + } + + ret = sendto (ph->fd, buf, buflen, 0, + (struct sockaddr *) ph->addr, ph->addrlen); + + if (ret < 0) + ping_set_error (obj, "sendto", strerror (errno)); + + return (ret); +} + +static int ping_send_one_ipv4 (pingobj_t *obj, pinghost_t *ph) +{ + struct icmp *icmp4; + int status; + + char buf[4096]; + int buflen; + + char *data; + int datalen; + + dprintf ("ph->hostname = %s\n", ph->hostname); + + memset (buf, '\0', sizeof (buf)); + icmp4 = (struct icmp *) buf; + data = (char *) (icmp4 + 1); + + icmp4->icmp_type = ICMP_ECHO; + icmp4->icmp_code = 0; + icmp4->icmp_cksum = 0; + icmp4->icmp_id = htons (ph->ident); + icmp4->icmp_seq = htons (ph->sequence); + + strcpy (data, PING_DATA); + datalen = strlen (data); + + buflen = datalen + sizeof (struct icmp); + + icmp4->icmp_cksum = ping_icmp4_checksum (buf, buflen); + + dprintf ("Sending ICMPv4 package with ID 0x%04x\n", ph->ident); + + status = ping_sendto (obj, ph, buf, buflen); + if (status < 0) + { + perror ("ping_sendto"); + return (-1); + } + + dprintf ("sendto: status = %i\n", status); + + return (0); +} + +static int ping_send_one_ipv6 (pingobj_t *obj, pinghost_t *ph) +{ + struct icmp6_hdr *icmp6; + int status; + + char buf[4096]; + int buflen; + + char *data; + int datalen; + + dprintf ("ph->hostname = %s\n", ph->hostname); + + memset (buf, '\0', sizeof (buf)); + icmp6 = (struct icmp6_hdr *) buf; + data = (char *) (icmp6 + 1); + + icmp6->icmp6_type = ICMP6_ECHO_REQUEST; + icmp6->icmp6_code = 0; + /* The checksum will be calculated by the TCP/IP stack. */ + icmp6->icmp6_cksum = 0; + icmp6->icmp6_id = htons (ph->ident); + icmp6->icmp6_seq = htons (ph->sequence); + + strcpy (data, PING_DATA); + datalen = strlen (data); + + buflen = datalen + sizeof (struct icmp6_hdr); + + dprintf ("Sending ICMPv6 package with ID 0x%04x\n", ph->ident); + + status = ping_sendto (obj, ph, buf, buflen); + if (status < 0) + { + perror ("ping_sendto"); + return (-1); + } + + dprintf ("sendto: status = %i\n", status); + + return (0); +} + +static int ping_send_all (pingobj_t *obj) +{ + pinghost_t *ph; + pinghost_t *ptr; + + int ret; + + ret = 0; + ph = obj->head; + + for (ptr = ph; ptr != NULL; ptr = ptr->next) + { + /* start timer.. The GNU `ping6' starts the timer before + * sending the packet, so I will do that too */ + if (gettimeofday (ptr->timer, NULL) == -1) + { + dprintf ("gettimeofday: %s\n", strerror (errno)); + timerclear (ptr->timer); + ret--; + continue; + } + else + { + dprintf ("timer set for hostname = %s\n", ptr->hostname); + } + + if (ptr->addrfamily == AF_INET6) + { + dprintf ("Sending ICMPv6 echo request to `%s'\n", ptr->hostname); + if (ping_send_one_ipv6 (obj, ptr) != 0) + { + timerclear (ptr->timer); + ret--; + continue; + } + } + else if (ptr->addrfamily == AF_INET) + { + dprintf ("Sending ICMPv4 echo request to `%s'\n", ptr->hostname); + if (ping_send_one_ipv4 (obj, ptr) != 0) + { + timerclear (ptr->timer); + ret--; + continue; + } + } + else /* this should not happen */ + { + dprintf ("Unknown address family: %i\n", ptr->addrfamily); + timerclear (ptr->timer); + ret--; + continue; + } + + ptr->sequence++; + } + + return (ret); +} + +/* + * Set the TTL of a socket protocol independently. + */ +static int ping_set_ttl (pinghost_t *ph, int ttl) +{ + int ret = -2; + + if (ph->addrfamily == AF_INET) + { + ret = setsockopt (ph->fd, IPPROTO_IP, IP_TTL, &ttl, sizeof (ttl)); + } + else if (ph->addrfamily == AF_INET6) + { + ret = setsockopt (ph->fd, IPPROTO_IPV6, IPV6_UNICAST_HOPS, &ttl, sizeof (ttl)); + } + + return (ret); +} + +static int ping_get_ident (void) +{ + int fd; + static int did_seed = 0; + + int retval; + + if (did_seed == 0) + { + if ((fd = open ("/dev/urandom", O_RDONLY)) != -1) + { + unsigned int seed; + + if (read (fd, &seed, sizeof (seed)) != -1) + { + did_seed = 1; + dprintf ("Random seed: %i\n", seed); + srandom (seed); + } + + close (fd); + } + else + { + dprintf ("open (/dev/urandom): %s\n", strerror (errno)); + } + } + + retval = (int) random (); + + dprintf ("Random number: %i\n", retval); + + return (retval); +} + +static pinghost_t *ping_alloc (void) +{ + pinghost_t *ph; + size_t ph_size; + + ph_size = sizeof (pinghost_t) + + sizeof (struct sockaddr_storage) + + sizeof (struct timeval); + + ph = (pinghost_t *) malloc (ph_size); + if (ph == NULL) + return (NULL); + + memset (ph, '\0', ph_size); + + ph->timer = (struct timeval *) (ph + 1); + ph->addr = (struct sockaddr_storage *) (ph->timer + 1); + + ph->addrlen = sizeof (struct sockaddr_storage); + ph->latency = -1.0; + ph->ident = ping_get_ident () & 0xFFFF; + + return (ph); +} + +static void ping_free (pinghost_t *ph) +{ + if (ph->hostname != NULL) + free (ph->hostname); + + free (ph); +} + +/* + * public methods + */ +const char *ping_get_error (pingobj_t *obj) +{ + return (obj->errmsg); +} + +pingobj_t *ping_construct (void) +{ + pingobj_t *obj; + + if ((obj = (pingobj_t *) malloc (sizeof (pingobj_t))) == NULL) + return (NULL); + memset (obj, '\0', sizeof (pingobj_t)); + + obj->timeout = PING_DEF_TIMEOUT; + obj->ttl = PING_DEF_TTL; + obj->addrfamily = PING_DEF_AF; + + return (obj); +} + +void ping_destroy (pingobj_t *obj) +{ + pinghost_t *current; + pinghost_t *next; + + current = obj->head; + next = NULL; + + while (current != NULL) + { + next = current->next; + ping_free (current); + current = next; + } + + free (obj); + + return; +} + +int ping_setopt (pingobj_t *obj, int option, void *value) +{ + int ret = 0; + + switch (option) + { + case PING_OPT_TIMEOUT: + obj->timeout = *((double *) value); + if (obj->timeout < 0.0) + { + obj->timeout = PING_DEF_TIMEOUT; + ret = -1; + } + break; + + case PING_OPT_TTL: + obj->ttl = *((int *) value); + if ((obj->ttl < 1) || (obj->ttl > 255)) + { + obj->ttl = PING_DEF_TTL; + ret = -1; + } + break; + + case PING_OPT_AF: + obj->addrfamily = *((int *) value); + if ((obj->addrfamily != AF_UNSPEC) + && (obj->addrfamily != AF_INET) + && (obj->addrfamily != AF_INET6)) + { + obj->addrfamily = PING_DEF_AF; + ret = -1; + } + break; + + default: + ret = -2; + } /* switch (option) */ + + return (ret); +} /* int ping_setopt */ + + +int ping_send (pingobj_t *obj) +{ + int ret; + + if (ping_send_all (obj) < 0) + return (-1); + + if ((ret = ping_receive_all (obj)) < 0) + return (-2); + + return (ret); +} + +static pinghost_t *ping_host_search (pinghost_t *ph, const char *host) +{ + while (ph != NULL) + { + if (strcasecmp (ph->hostname, host) == 0) + break; + + ph = ph->next; + } + + return (ph); +} + +int ping_host_add (pingobj_t *obj, const char *host) +{ + pinghost_t *ph; + + struct sockaddr_storage sockaddr; + socklen_t sockaddr_len; + + struct addrinfo ai_hints; + struct addrinfo *ai_list, *ai_ptr; + int ai_return; + + dprintf ("host = %s\n", host); + + if (ping_host_search (obj->head, host) != NULL) + return (0); + + memset (&ai_hints, '\0', sizeof (ai_hints)); + ai_hints.ai_flags = 0; +#ifdef AI_ADDRCONFIG + ai_hints.ai_flags |= AI_ADDRCONFIG; +#endif + ai_hints.ai_family = obj->addrfamily; + ai_hints.ai_socktype = SOCK_RAW; + + if ((ph = ping_alloc ()) == NULL) + { + dprintf ("Out of memory!\n"); + return (-1); + } + + if ((ph->hostname = strdup (host)) == NULL) + { + dprintf ("Out of memory!\n"); + ping_set_error (obj, "strdup", strerror (errno)); + ping_free (ph); + return (-1); + } + + if ((ai_return = getaddrinfo (host, NULL, &ai_hints, &ai_list)) != 0) + { + dprintf ("getaddrinfo failed\n"); + ping_set_error (obj, "getaddrinfo", + (ai_return == EAI_SYSTEM) + ? strerror (errno) + : gai_strerror (ai_return)); + ping_free (ph); + return (-1); + } + + if (ai_list == NULL) + ping_set_error (obj, "getaddrinfo", "No hosts returned"); + + for (ai_ptr = ai_list; ai_ptr != NULL; ai_ptr = ai_ptr->ai_next) + { + ph->fd = -1; + + sockaddr_len = sizeof (sockaddr); + memset (&sockaddr, '\0', sockaddr_len); + + if (ai_ptr->ai_family == AF_INET) + { + struct sockaddr_in *si; + + si = (struct sockaddr_in *) &sockaddr; + si->sin_family = AF_INET; + si->sin_port = htons (ph->ident); + si->sin_addr.s_addr = htonl (INADDR_ANY); + + ai_ptr->ai_socktype = SOCK_RAW; + ai_ptr->ai_protocol = IPPROTO_ICMP; + } + else if (ai_ptr->ai_family == AF_INET6) + { + struct sockaddr_in6 *si; + + si = (struct sockaddr_in6 *) &sockaddr; + si->sin6_family = AF_INET6; + si->sin6_port = htons (ph->ident); + si->sin6_addr = in6addr_any; + + ai_ptr->ai_socktype = SOCK_RAW; + ai_ptr->ai_protocol = IPPROTO_ICMPV6; + } + else + { + char errmsg[PING_ERRMSG_LEN]; + + snprintf (errmsg, PING_ERRMSG_LEN, "Unknown `ai_family': %i", ai_ptr->ai_family); + errmsg[PING_ERRMSG_LEN - 1] = '\0'; + + dprintf (errmsg); + ping_set_error (obj, "getaddrinfo", errmsg); + continue; + } + + /* TODO: Move this to a static function `ping_open_socket' and + * call it whenever the socket dies. */ + ph->fd = socket (ai_ptr->ai_family, ai_ptr->ai_socktype, ai_ptr->ai_protocol); + if (ph->fd == -1) + { + dprintf ("socket: %s\n", strerror (errno)); + ping_set_error (obj, "socket", strerror (errno)); + continue; + } + + if (bind (ph->fd, (struct sockaddr *) &sockaddr, sockaddr_len) == -1) + { + dprintf ("bind: %s\n", strerror (errno)); + ping_set_error (obj, "bind", strerror (errno)); + close (ph->fd); + ph->fd = -1; + continue; + } + + assert (sizeof (struct sockaddr_storage) >= ai_ptr->ai_addrlen); + memset (ph->addr, '\0', sizeof (struct sockaddr_storage)); + memcpy (ph->addr, ai_ptr->ai_addr, ai_ptr->ai_addrlen); + ph->addrlen = ai_ptr->ai_addrlen; + ph->addrfamily = ai_ptr->ai_family; + + break; + } + + freeaddrinfo (ai_list); + + if (ph->fd < 0) + { + free (ph->hostname); + free (ph); + return (-1); + } + + ph->next = obj->head; + obj->head = ph; + + ping_set_ttl (ph, obj->ttl); + + return (0); +} + +int ping_host_remove (pingobj_t *obj, const char *host) +{ + pinghost_t *pre, *cur; + + pre = NULL; + cur = obj->head; + + while (cur != NULL) + { + if (strcasecmp (host, cur->hostname)) + break; + + pre = cur; + cur = cur->next; + } + + if (cur == NULL) + { + ping_set_error (obj, "ping_host_remove", "Host not found"); + return (-1); + } + + if (pre == NULL) + obj->head = cur->next; + else + pre->next = cur->next; + + if (cur->fd >= 0) + close (cur->fd); + + ping_free (cur); + + return (0); +} + +pingobj_iter_t *ping_iterator_get (pingobj_t *obj) +{ + return ((pingobj_iter_t *) obj->head); +} + +pingobj_iter_t *ping_iterator_next (pingobj_iter_t *iter) +{ + return ((pingobj_iter_t *) iter->next); +} + +int ping_iterator_get_info (pingobj_iter_t *iter, int info, + void *buffer, size_t *buffer_len) +{ + int ret = EINVAL; + + size_t orig_buffer_len = *buffer_len; + + switch (info) + { + case PING_INFO_HOSTNAME: + ret = ENOMEM; + *buffer_len = strlen (iter->hostname); + if (orig_buffer_len <= *buffer_len) + break; + /* Since (orig_buffer_len > *buffer_len) `strncpy' + * will copy `*buffer_len' and pad the rest of + * `buffer' with null-bytes */ + strncpy (buffer, iter->hostname, orig_buffer_len); + ret = 0; + break; + + case PING_INFO_ADDRESS: + ret = getnameinfo ((struct sockaddr *) iter->addr, + iter->addrlen, + (char *) buffer, + *buffer_len, + NULL, 0, + NI_NUMERICHOST); + if (ret != 0) + { + if ((ret == EAI_MEMORY) +#ifdef EAI_OVERFLOW + || (ret == EAI_OVERFLOW) +#endif + ) + ret = ENOMEM; + else if (ret == EAI_SYSTEM) + /* XXX: Not thread-safe! */ + ret = errno; + else + ret = EINVAL; + } + break; + + case PING_INFO_FAMILY: + ret = ENOMEM; + *buffer_len = sizeof (int); + if (orig_buffer_len < sizeof (int)) + break; + *((int *) buffer) = iter->addrfamily; + ret = 0; + break; + + case PING_INFO_LATENCY: + ret = ENOMEM; + *buffer_len = sizeof (double); + if (orig_buffer_len < sizeof (double)) + break; + *((double *) buffer) = iter->latency; + ret = 0; + break; + + case PING_INFO_SEQUENCE: + ret = ENOMEM; + *buffer_len = sizeof (uint16_t); + if (orig_buffer_len < sizeof (uint16_t)) + break; + *((uint16_t *) buffer) = (uint16_t) iter->sequence; + ret = 0; + break; + + case PING_INFO_IDENT: + ret = ENOMEM; + *buffer_len = sizeof (uint16_t); + if (orig_buffer_len < sizeof (uint16_t)) + break; + *((uint16_t *) buffer) = (uint16_t) iter->ident; + ret = 0; + break; + } + + return (ret); +} + +void *ping_iterator_get_context (pingobj_iter_t *iter) +{ + return (iter->context); +} + +void ping_iterator_set_context (pingobj_iter_t *iter, void *context) +{ + iter->context = context; +} diff --git a/src/mans/Makefile.am b/src/mans/Makefile.am new file mode 100644 index 0000000..9e398a9 --- /dev/null +++ b/src/mans/Makefile.am @@ -0,0 +1,28 @@ +man_PODS = liboping.pod ping_construct.pod ping_setopt.pod ping_host_add.pod \ + ping_send.pod ping_get_error.pod ping_iterator_get.pod \ + ping_iterator_get_info.pod ping_iterator_get_context.pod oping.pod +man_MANS = liboping.3 ping_construct.3 ping_setopt.3 ping_host_add.3 \ + ping_send.3 ping_get_error.3 ping_iterator_get.3 \ + ping_iterator_get_info.3 ping_iterator_get_context.3 oping.8 + +EXTRA_DIST = $(man_MANS) $(man_PODS) + +#liboping_3_SOURCES = liboping.pod +#ping_construct_3_SOURCES = ping_construct.pod +#ping_setopt_3_SOURCES = ping_setopt.pod +#ping_host_add_3_SOURCES = ping_host_add.pod +#ping_send_3_SOURCES = ping_send.pod +#ping_get_error_3_SOURCES = ping_get_error.pod +#ping_iterator_get_3_SOURCES = ping_iterator_get.pod +#ping_iterator_get_info_3_SOURCES = ping_iterator_get_info.pod +#ping_iterator_get_context_3_SOURCES = ping_iterator_get_context.pod +#oping_8_SOURCES = oping.pod + +.pod.1: + pod2man --section=1 --release=$(VERSION) --center=$(PACKAGE) $< >$@ + +.pod.3: + pod2man --section=3 --release=$(VERSION) --center=$(PACKAGE) $< >$@ + +.pod.8: + pod2man --section=8 --release=$(VERSION) --center=$(PACKAGE) $< >$@ diff --git a/src/mans/liboping.pod b/src/mans/liboping.pod new file mode 100644 index 0000000..496ad35 --- /dev/null +++ b/src/mans/liboping.pod @@ -0,0 +1,86 @@ +=head1 NAME + +liboping - Library to send ICMPv4/ICMPv6 echo packets to multiple hosts + +=head1 DESCRIPTION + +This is an overview of liboping, a C library to send ICMP ECHO_REQUEST packets +to remote hosts and measure the time it takes for replies to be received. This +method, often simply called "ping", is a common way to measure network latency +and/or host reachability. + +The goals of this library are to provide the above functionality in a platform +and protocol independent manner. The interface is simple, object oriented and +(hopefully) ANSI-C compliant. + +=head1 GENERAL USAGE + +There are two main types that are used by applications. Both are "opaque +types", meaning they are structures that are B completely defined in the +header file, so you cannot access the structures' members. You don't need to, +don't do it. These structures are object to change without notice. + +=over 4 + +=item C + +A ping-object. You can set specific options for this object, add and remove +hosts to/from it and send ICMP packets to all associated hosts. This is often +called a "handle". + +=item C + +An iterator over the hosts associated with a C object. This iterator +can be used to query more information about a host, for example the hostname, +the measured latency or the current ICMP sequence. + +=back + +Upon startup you usually create one or more C objects and add hosts +to it using the C method (see below). You periodically send +"echo requests" using the C method, iterate over all hosts using +C and C. For each host you call +C to read the current latency and do something with it. + +If an error occurs you can use C so get information on what +failed. + +=head1 LINKING WITH LIBOPING + +Depending on you platform you don't need any extra libraries (e.g. GNU/Linux) +or C (using C<-lsocket>) if the C function is not in the +C-library. The latter is the case for the Solaris operating system. + +=head1 SYMBOL NAMES + +All "official" function or method names are prefixed with "ping_". Don't use +any other functions or methods. Although no such functions should exist. + +=head1 THREAD SAFETY + +liboping has been designed to be as thread safe a possible. However, this has +not been tested and may need some additional work. Use at your own risk and +please report back any problems or success messages. Thank you :) + +=head1 SEE ALSO + +L, +L, +L, +L, +L, +L, +L, +L + +=head1 LICENSE + +liboping is licensed under the GPLv2. No other version of the license is +applicable. + +=head1 AUTHOR + +liboping is written by Florian octo Forster Eocto at verplant.orgE. +It's homepage can be found at L. + +(c) 2005, 2006 by Florian octo Forster. diff --git a/src/mans/oping.pod b/src/mans/oping.pod new file mode 100644 index 0000000..e6ef195 --- /dev/null +++ b/src/mans/oping.pod @@ -0,0 +1,58 @@ +=head1 NAME + +oping - send ICMP ECHO_REQUEST to network hosts + +=head1 SYNOPSIS + +B [B<-4> | B<-6>] [B<-c> I] [B<-i> I] I [I [I ...]] + +=head1 DESCRIPTION + +oping uses ICMPv4 or ICMPv6 ECHO_REQUEST packets to measure a hosts +reachability and the network latency. In contrast to the original L +utility B can send ICMP packets to multiple hosts in parallel and wait +for all ECHO_RESPONSE packets to arrive. In contrast to the +B utility (URL is listed in L<"SEE ALSO">) B can use both, IPv4 +and IPv6 transparently and side by side. + +=head1 OPTIONS + +=over 4 + +=item B<-4> + +Force the use of IPv4. + +=item B<-6> + +Force the use of IPv6 + +=item B<-c> I + +Send (and receive) I ICMP packets, then stop and exit. + +=item B<-i> I + +Send one ICMP packet (per host) each I seconds. This can be a +floating-point number to specify sub-second precision. + +=back + +=head1 BUGS + +=over 4 + +=item The TTL cannot be set + +=back + +=head1 SEE ALSO + +L, L, L + +=head1 AUTHOR + +liboping is written by Florian octo Forster Eocto at verplant.orgE. +It's homepage can be found at L. + +(c) 2005, 2006 by Florian octo Forster. diff --git a/src/mans/ping_construct.pod b/src/mans/ping_construct.pod new file mode 100644 index 0000000..4fadf70 --- /dev/null +++ b/src/mans/ping_construct.pod @@ -0,0 +1,38 @@ +=head1 NAME + +ping_construct - Constructor for the liboping class + +=head1 SYNOPSIS + + #include + + pingobj_t *ping_construct (void); + void ping_destroy (pingobj_t *obj); + +=head1 DESCRIPTION + +The B constructor allocates the memory neccessary for a +liboping object, initializes that memory and returns a pointer to it. + +The B iterates over all hosts associated with the liboping object +I, closes the sockets, removes the hosts and frees I's memory. + +=head1 RETURN VALUE + +The B constructor returns a pointer to the allocated memory or NULL if no memory could be allocated. + +=head1 SEE ALSO + +L, +L, +L, +L, +L, +L + +=head1 AUTHOR + +liboping is written by Florian octo Forster Eocto at verplant.orgE. +It's homepage can be found at L. + +(c) 2005, 2006 by Florian octo Forster. diff --git a/src/mans/ping_get_error.pod b/src/mans/ping_get_error.pod new file mode 100644 index 0000000..4f67511 --- /dev/null +++ b/src/mans/ping_get_error.pod @@ -0,0 +1,30 @@ +=head1 NAME + +ping_get_error - Return the last error message + +=head1 SYNOPSIS + + #include + + const char *ping_get_error (pingobj_t *obj); + +=head1 DESCRIPTION + +The B method returns an error message indicating the last error +encountered. B + +=head1 RETURN VALUE + +A C string representing the last error or an empty string if no error +was encountered yet. + +=head1 SEE ALSO + +L + +=head1 AUTHOR + +liboping is written by Florian octo Forster Eocto at verplant.orgE. +It's homepage can be found at L. + +(c) 2005, 2006 by Florian octo Forster. diff --git a/src/mans/ping_host_add.pod b/src/mans/ping_host_add.pod new file mode 100644 index 0000000..c768be3 --- /dev/null +++ b/src/mans/ping_host_add.pod @@ -0,0 +1,49 @@ +=head1 NAME + +ping_host_add - Add a host to a liboping object + +=head1 SYNOPSIS + + #include + + int ping_host_add (pingobj_t *obj, const char *host); + int ping_host_remove (pingobj_t *obj, const char *host); + +=head1 DESCRIPTION + +The B method tries to resolve the I argument, open a +socket and associate everything with the liboping object I. + +The I argument is a pointer to an liboping object, as returned by +L. + +The I parameter is a '\0' terminated string which is interpreted as a +hostname or an IP address. Depending on the address family setting, set with +L, the hostname is resolved to an IPv4 or IPv6 address. + +The B method looks for I within I and remove it if +found. It will close the socket and deallocate the memory, too. + +=head1 RETURN VALUE + +If B succeeds it returns zero. If an error occurs a value less +than zero is returned and the last error is saved internally. You can receive +the error message using L. + +B returns zero upon success and less than zero if it failed. +Currently the only reason for failure is that the host isn't found, but this is +subject to change. Use L to receive the error message. + +=head1 SEE ALSO + +L, +L, +L, +L + +=head1 AUTHOR + +liboping is written by Florian octo Forster Eocto at verplant.orgE. +It's homepage can be found at L. + +(c) 2005, 2006 by Florian octo Forster. diff --git a/src/mans/ping_iterator_get.pod b/src/mans/ping_iterator_get.pod new file mode 100644 index 0000000..ae73539 --- /dev/null +++ b/src/mans/ping_iterator_get.pod @@ -0,0 +1,45 @@ +=head1 NAME + +ping_iterator_get, ping_iterator_next - Iterate over all hosts of a liboping object + +=head1 SYNOPSIS + + #include + + pingobj_iter_t *ping_iterator_get (pingobj_t *obj); + pingobj_iter_t *ping_iterator_next (pingobj_iter_t *iter) + +=head1 DESCRIPTION + +These two functions can be used to iterate over all hosts associated with a +liboping object. You can use these methods as follows: + + pingobj_iter_t *iter; + + for (iter = ping_iterator_get (obj); + iter != NULL; + iter = ping_iterator_next (iter)) + { + ...; + } + +To get usable information from an iterator object (which is also an opaque data +type, just like the liboping object itself) use L and +L. + +=head1 RETURN VALUE + +The B returns an iterator for I or NULL if no host is +associated with I. + +The B returns an iterator for the host following I or +NULL if the last host has been reached. + +=head1 SEE ALSO + +L, +L, +L, +L + +=head1 AUTHOR diff --git a/src/mans/ping_iterator_get_context.pod b/src/mans/ping_iterator_get_context.pod new file mode 100644 index 0000000..2ada86a --- /dev/null +++ b/src/mans/ping_iterator_get_context.pod @@ -0,0 +1,45 @@ +=head1 NAME + +ping_iterator_get_context, ping_iterator_set_context - Store host-dependent data + +=head1 SYNOPSIS + + #include + + void *ping_iterator_get_context (pingobj_iter_t *iter); + void ping_iterator_set_context (pingobj_iter_t *iter, void *context); + +=head1 DESCRIPTION + +B can be used to store host-specific data within the +liboping structures. This data can be received again by calling +B. The data itself is never touched by liboping. If +you call ping_host_remove (see L) or ping_destroy (see +L) and the context is not NULL liboping will assume you know +what you're doing and simply ignore the fact this might be a memory leak. + +The I argument is an iterator object as returned by +L and ping_iterator_next. + +The I argument of B is a pointer to +anything and may be NULL. + +=head1 RETURN VALUE + +B returns the same pointer previously passed to +B or NULL if B has never +been called before. + +=head1 SEE ALSO + +L, +L, +L, +L + +=head1 AUTHOR + +liboping is written by Florian octo Forster Eocto at verplant.orgE. +It's homepage can be found at L. + +(c) 2005, 2006 by Florian octo Forster. diff --git a/src/mans/ping_iterator_get_info.pod b/src/mans/ping_iterator_get_info.pod new file mode 100644 index 0000000..d84c111 --- /dev/null +++ b/src/mans/ping_iterator_get_info.pod @@ -0,0 +1,96 @@ +=head1 NAME + +ping_iterator_get_info - Constructor for the liboping class + +=head1 SYNOPSIS + + #include + + int ping_iterator_get_info (pingobj_iter_t *iter, + int info, + void *buffer, + size_t *buffer_len); + +=head1 DESCRIPTION + +The B method can be used on an host iterator to return +various information about the current host. + +The I argument is an iterator as returned by L or +L. + +The I argument specifies the type of information returned. Use the +following defines: + +=over 4 + +=item B + +Return the hostname of the host the iterator points to. Since the name is +looked up using the socket address this may differ from the hostname passed to +L. The hostname is actually looked up every time you call +this method, no cache is involved within liboping. + +It is recommended to include C and allocate B bytes of +buffer. + +=item B + +Return the address used in ASCII (i.e. human readable) format. The address is +looked up every time you call this method. 40 bytes should be sufficient for +the buffer (16 octets in hex format, seven colons and one null byte), but more +won't hurt. + +=item B + +Returns the address family of the host. The buffer should be ig enough to hold +an integer. The value is either B or B. + +=item B + +Return the last measured latency or less than zero if the timeout occured +before a echo response was received. The buffer should be big enough to hold a +double value. + +=item B + +Return the last sequence number sent. This number is increased regardless of +echo responses being received or not. The buffer should hold an integer. + +=item B + +Return the ident that is put into every ICMP packet sent to this host. Per +convention this usually is the PID of the sending process, but since liboping +can handle several hosts in parallel it uses a (pseudo-)random number here. The +buffer should be big enough to hold an integer value. + +=back + +The I argument is a pointer to an appropriately sized area of memory +where the result of the call will be stored. The I value is used as +input and output: When calling B it reports the size of +the memory region pointed to by I. The method will write the number of +bytes actually written to the memory into I before returning. + +=head1 RETURN VALUE + +B returns zero if it succeeds. + +B is returned if the value passed as I is unknown. Both, +I and I, will be left untouched in this case. + +If the requested information didn't fit into I then the size that would +have been needed is written into I; I itself is left +untouched. The return value is B in this case. + +=head1 SEE ALSO + +L, +L + +=head1 AUTHOR + +liboping is written by Florian octo Forster Eocto at verplant.orgE. +It's homepage can be found at L. + +(c) 2005, 2006 by Florian octo Forster. diff --git a/src/mans/ping_send.pod b/src/mans/ping_send.pod new file mode 100644 index 0000000..b149cfe --- /dev/null +++ b/src/mans/ping_send.pod @@ -0,0 +1,42 @@ +=head1 NAME + +ping_send - Send ICMP echo requests to all associated hosts and wait for ICMP echo responses to arrive + +=head1 SYNOPSIS + + #include + + int ping_send (pingobj_t *obj); + +=head1 DESCRIPTION + +The B method is the actual workhorse of this library. It crafts ICMP +packets for the hosts associated with I and sends them via the +corresponding sockets. It then waits for echo responses and receives them, +writing latency information for each host. The method returns after all echo +replies have been read or the timeout (set with L) is reached. + +After this function returns you will most likely iterate over all hosts using +L and ping_iterator_next (described in the same manpage) +and call L on each host. + +=head1 RETURN VALUE + +B returns the value of echo replies received or a value less than +zero if an error occured. Use L to receive an error message. + +=head1 SEE ALSO + +L, +L, +L, +L, +L, +L + +=head1 AUTHOR + +liboping is written by Florian octo Forster Eocto at verplant.orgE. +It's homepage can be found at L. + +(c) 2005, 2006 by Florian octo Forster. diff --git a/src/mans/ping_setopt.pod b/src/mans/ping_setopt.pod new file mode 100644 index 0000000..7f64b04 --- /dev/null +++ b/src/mans/ping_setopt.pod @@ -0,0 +1,63 @@ +=head1 NAME + +ping_setopt - Set options for a liboping object + +=head1 SYNOPSIS + + #include + + int ping_setopt (pingobj_t *obj, int opt, void *val); + +=head1 DESCRIPTION + +The B method sets options that effect all hosts associated with +the object I and hosts that are yet to be added to the object. + +The I argument is a pointer to an liboping object, as returned by +L. + +The I argument specifies the option to set. Use one of the following +constants: + +=over 4 + +=item B + +The time to wait for a "echo reply" to be received; in seconds. In this case +the memory pointed to by I is interpreted as a double value and must be +greater than zero. The default is B. + +=item B + +The value written into the time-to-live (= TTL) field of generated ICMP +packets. The memory pointed to by I is interpreted as an integer. Valid +values are 1 through 255. Default is B. + +=item B + +The address family to use. The memory pointed to by I is interpreted as an +integer and must be either B, B, or B. This +option only effects hosts that are being added B this option has been +set. Default is B. + +=back + +The I argument is a pointer to the new value. It must not be NULL. It is +dereferences depending on the value of the I argument, see above. The +memory pointed to by I is not changed. + +=head1 RETURN VALUE + +B returns zero upon success or less than zero upon failure. + +=head1 SEE ALSO + +L, +L + +=head1 AUTHOR + +liboping is written by Florian octo Forster Eocto at verplant.orgE. +It's homepage can be found at L. + +(c) 2005, 2006 by Florian octo Forster. diff --git a/src/oping.c b/src/oping.c new file mode 100644 index 0000000..cc115f2 --- /dev/null +++ b/src/oping.c @@ -0,0 +1,403 @@ +/** + * Object oriented C module to send ICMP and ICMPv6 `echo's. + * Copyright (C) 2006 Florian octo Forster + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#if HAVE_CONFIG_H +# include +#endif + +#if STDC_HEADERS +# include +# include +# include +# include +# include +#else +# error "You don't have the standard C99 header files installed" +#endif /* STDC_HEADERS */ + +#if HAVE_MATH_H +# include +#endif + +#if TIME_WITH_SYS_TIME +# include +# include +#else +# if HAVE_SYS_TIME_H +# include +# else +# include +# endif +#endif + +#if HAVE_NETDB_H +# include /* NI_MAXHOST */ +#endif + +#if HAVE_SIGNAL_H +# include +#endif + +#include "oping.h" + +typedef struct ping_context +{ + char host[NI_MAXHOST]; + char addr[NI_MAXHOST]; + + int req_sent; + int req_rcvd; + + double latency_min; + double latency_max; + double latency_total; +} ping_context_t; + +static double opt_interval = 1.0; +static int opt_addrfamily = PING_DEF_AF; +static int opt_count = -1; + +void sigint_handler (int signal) +{ + /* Exit the loop */ + opt_count = 0; +} + +ping_context_t *context_create (void) +{ + ping_context_t *ret; + + if ((ret = malloc (sizeof (ping_context_t))) == NULL) + return (NULL); + + memset (ret, '\0', sizeof (ping_context_t)); + + ret->latency_min = -1.0; + ret->latency_max = -1.0; + ret->latency_total = 0.0; + + return (ret); +} + +void context_destroy (ping_context_t *context) +{ + free (context); +} + +void usage_exit (const char *name) +{ + fprintf (stderr, "Usage: %s [-46] [-c count] [-i interval] host [host [host ...]]\n", + name); + exit (1); +} + +int read_options (int argc, char **argv) +{ + int optchar; + + while (1) + { + optchar = getopt (argc, argv, "46c:i:"); + + if (optchar == -1) + break; + + switch (optchar) + { + case '4': + case '6': + opt_addrfamily = (optchar == '4') ? AF_INET : AF_INET6; + break; + + case 'c': + { + int new_count; + new_count = atoi (optarg); + if (new_count > 0) + opt_count = new_count; + } + break; + + case 'i': + { + double new_interval; + new_interval = atof (optarg); + if (new_interval >= 0.2) + opt_interval = new_interval; + } + break; + + default: + usage_exit (argv[0]); + } + } + + return (optind); +} + +void print_host (pingobj_iter_t *iter) +{ + double latency; + uint16_t sequence; + size_t buffer_len; + ping_context_t *context; + + buffer_len = sizeof (latency); + ping_iterator_get_info (iter, PING_INFO_LATENCY, + &latency, &buffer_len); + + buffer_len = sizeof (sequence); + ping_iterator_get_info (iter, PING_INFO_SEQUENCE, + &sequence, &buffer_len); + + context = (ping_context_t *) ping_iterator_get_context (iter); + + context->req_sent++; + if (latency > 0.0) + { + context->req_rcvd++; + context->latency_total += latency; + + if ((context->latency_max < 0.0) || (context->latency_max < latency)) + context->latency_max = latency; + if ((context->latency_min < 0.0) || (context->latency_min > latency)) + context->latency_min = latency; + + printf ("echo reply from %s (%s): icmp_seq=%u time=%.1f ms\n", + context->host, context->addr, + (unsigned int) sequence, latency); + } + else + { + printf ("echo reply from %s (%s): icmp_seq=%u timeout\n", + context->host, context->addr, + (unsigned int) sequence); + } +} + +void time_normalize (struct timespec *ts) +{ + while (ts->tv_nsec < 0) + { + if (ts->tv_sec == 0) + { + ts->tv_nsec = 0; + return; + } + + ts->tv_sec -= 1; + ts->tv_nsec += 1000000000; + } + + while (ts->tv_nsec >= 1000000000) + { + ts->tv_sec += 1; + ts->tv_nsec -= 1000000000; + } +} + +void time_calc (struct timespec *ts_dest, + const struct timespec *ts_int, + const struct timeval *tv_begin, + const struct timeval *tv_end) +{ + ts_dest->tv_sec = tv_begin->tv_sec + ts_int->tv_sec; + ts_dest->tv_nsec = (tv_begin->tv_usec * 1000) + ts_int->tv_nsec; + time_normalize (ts_dest); + + /* Assure that `(begin + interval) > end'. + * This may seem overly complicated, but `tv_sec' is of type `time_t' + * which may be `unsigned. *sigh* */ + if ((tv_end->tv_sec > ts_dest->tv_sec) + || ((tv_end->tv_sec == ts_dest->tv_sec) + && ((tv_end->tv_usec * 1000) > ts_dest->tv_nsec))) + { + ts_dest->tv_sec = 0; + ts_dest->tv_nsec = 0; + return; + } + + ts_dest->tv_sec = ts_dest->tv_sec - tv_end->tv_sec; + ts_dest->tv_nsec = ts_dest->tv_nsec - (tv_end->tv_usec * 1000); + time_normalize (ts_dest); +} + +int main (int argc, char **argv) +{ + pingobj_t *ping; + pingobj_iter_t *iter; + + struct sigaction sigint_action; + + struct timeval tv_begin; + struct timeval tv_end; + struct timespec ts_wait; + struct timespec ts_int; + + int optind; + int i; + + if (geteuid () != 0) + { + fprintf (stderr, "Need superuser privileges to open a RAW socket. Sorry.\n"); + return (1); + } + + optind = read_options (argc, argv); + + if (optind >= argc) + usage_exit (argv[0]); + + if ((ping = ping_construct ()) == NULL) + { + fprintf (stderr, "ping_construct failed\n"); + return (1); + } + + { + double temp_sec; + double temp_nsec; + + temp_nsec = modf (opt_interval, &temp_sec); + ts_int.tv_sec = (time_t) temp_sec; + ts_int.tv_nsec = (long) (temp_nsec * 1000000000L); + + /* printf ("ts_int = %i.%09li\n", (int) ts_int.tv_sec, ts_int.tv_nsec); */ + } + + if (opt_addrfamily != PING_DEF_AF) + ping_setopt (ping, PING_OPT_AF, (void *) &opt_addrfamily); + + for (i = optind; i < argc; i++) + { + if (ping_host_add (ping, argv[i]) > 0) + { + fprintf (stderr, "ping_host_add (%s) failed\n", argv[i]); + continue; + } + } + + for (iter = ping_iterator_get (ping); + iter != NULL; + iter = ping_iterator_next (iter)) + { + ping_context_t *context; + size_t buffer_size; + + context = context_create (); + + buffer_size = sizeof (context->host); + ping_iterator_get_info (iter, PING_INFO_HOSTNAME, context->host, &buffer_size); + + buffer_size = sizeof (context->addr); + ping_iterator_get_info (iter, PING_INFO_ADDRESS, context->addr, &buffer_size); + + ping_iterator_set_context (iter, (void *) context); + } + + memset (&sigint_action, '\0', sizeof (sigint_action)); + sigint_action.sa_handler = sigint_handler; + if (sigaction (SIGINT, &sigint_action, NULL) < 0) + { + perror ("sigaction"); + return (1); + } + + while (opt_count != 0) + { + int status; + + if (gettimeofday (&tv_begin, NULL) < 0) + { + perror ("gettimeofday"); + return (1); + } + + if (ping_send (ping) < 0) + { + fprintf (stderr, "ping_send failed\n"); + return (1); + } + + for (iter = ping_iterator_get (ping); + iter != NULL; + iter = ping_iterator_next (iter)) + { + print_host (iter); + } + fflush (stdout); + + /* Don't sleep in the last iteration */ + if (opt_count == 1) + break; + + if (gettimeofday (&tv_end, NULL) < 0) + { + perror ("gettimeofday"); + return (1); + } + + time_calc (&ts_wait, &ts_int, &tv_begin, &tv_end); + + /* printf ("Sleeping for %i.%09li seconds\n", (int) ts_wait.tv_sec, ts_wait.tv_nsec); */ + while ((status = nanosleep (&ts_wait, &ts_wait)) != 0) + { + if (errno != EINTR) + { + perror ("nanosleep"); + break; + } + else if (opt_count == 0) + { + /* sigint */ + break; + } + } + + if (opt_count > 0) + opt_count--; + } /* while (opt_count != 0) */ + + for (iter = ping_iterator_get (ping); + iter != NULL; + iter = ping_iterator_next (iter)) + { + ping_context_t *context; + + context = ping_iterator_get_context (iter); + + printf ("\n--- %s ping statistics ---\n" + "%i packets transmitted, %i received, %.2f%% packet loss, time %.1fms\n" + "rtt min/avg/max/mdev = %.3f/%.3f/%.3f/%.3f ms\n", + context->host, context->req_sent, context->req_rcvd, + 100.0 * (context->req_sent - context->req_rcvd) / ((double) context->req_sent), + context->latency_total, + context->latency_min, + context->latency_total / ((double) context->req_rcvd), + context->latency_max, + 0.00); + + ping_iterator_set_context (iter, NULL); + free (context); + } + + ping_destroy (ping); + + return (0); +} diff --git a/src/oping.h b/src/oping.h new file mode 100644 index 0000000..5214947 --- /dev/null +++ b/src/oping.h @@ -0,0 +1,86 @@ +/** + * Object oriented C module to send ICMP and ICMPv6 `echo's. + * Copyright (C) 2006 Florian octo Forster + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#ifndef OCTO_PING_H +#define OCTO_PING_H 1 + +#if HAVE_CONFIG_H +# include +#endif + +#if HAVE_STDLIB_H +# include +#endif +#if HAVE_UNISTD_H +# include +#endif +#if HAVE_SYS_SOCKET_H +# include +#endif + +/* + * Type definitions + */ +struct pinghost; +typedef struct pinghost pinghost_t; + +typedef pinghost_t pingobj_iter_t; + +struct pingobj; +typedef struct pingobj pingobj_t; + +#define PING_OPT_TIMEOUT 0x01 +#define PING_OPT_TTL 0x02 +#define PING_OPT_AF 0x04 + +#define PING_DEF_TIMEOUT 1.0 +#define PING_DEF_TTL 255 +#define PING_DEF_AF AF_UNSPEC + +/* + * Method definitions + */ +pingobj_t *ping_construct (void); +void ping_destroy (pingobj_t *obj); + +int ping_setopt (pingobj_t *obj, int option, void *value); + +int ping_send (pingobj_t *obj); + +int ping_host_add (pingobj_t *obj, const char *host); +int ping_host_remove (pingobj_t *obj, const char *host); + +pingobj_iter_t *ping_iterator_get (pingobj_t *obj); +pingobj_iter_t *ping_iterator_next (pingobj_iter_t *iter); + +#define PING_INFO_HOSTNAME 1 +#define PING_INFO_ADDRESS 2 +#define PING_INFO_FAMILY 3 +#define PING_INFO_LATENCY 4 +#define PING_INFO_SEQUENCE 5 +#define PING_INFO_IDENT 6 +int ping_iterator_get_info (pingobj_iter_t *iter, int info, + void *buffer, size_t *buffer_len); + +const char *ping_get_error (pingobj_t *obj); + +void *ping_iterator_get_context (pingobj_iter_t *iter); +void ping_iterator_set_context (pingobj_iter_t *iter, void *context); + +#endif /* OCTO_PING_H */