From f1cff71f2afb3b9c485458dea28a7535c77259f3 Mon Sep 17 00:00:00 2001 From: octo Date: Fri, 28 Apr 2006 20:46:09 +0000 Subject: [PATCH] Moved the program files into a `src' subdirectory. --- Makefile.am | 12 - src/Makefile.am | 356 +--------------- src/liboping.c | 1061 +++++++++++++++++++++++++++++++++++++++++++++++ src/liboping.h | 77 ++++ 4 files changed, 1144 insertions(+), 362 deletions(-) delete mode 100644 Makefile.am create mode 100644 src/liboping.c create mode 100644 src/liboping.h diff --git a/Makefile.am b/Makefile.am deleted file mode 100644 index 30beb639..00000000 --- a/Makefile.am +++ /dev/null @@ -1,12 +0,0 @@ -SUBDIRS = libltdl src -INCLUDES = $(LTDLINCL) - -EXTRA_DIST = collectd.spec contrib debian - -dist-hook: - find $(distdir) -type d -name '.svn' | xargs rm -rf - -install-exec-hook: - $(mkinstalldirs) $(DESTDIR)$(sysconfdir) - $(mkinstalldirs) $(DESTDIR)$(localstatedir)/run - $(mkinstalldirs) $(DESTDIR)$(localstatedir)/lib/$(PACKAGE_NAME) diff --git a/src/Makefile.am b/src/Makefile.am index b281232c..8958aedc 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -1,358 +1,14 @@ -SUBDIRS = libconfig -if BUILD_MODULE_PING -SUBDIRS += liboping -endif +AUTOMAKE_OPTIONS = foreign no-dependencies if COMPILER_IS_GCC AM_CFLAGS = -Wall -Werror endif -sbin_PROGRAMS = collectd - -collectd_SOURCES = collectd.c collectd.h \ - utils_debug.c utils_debug.h \ - utils_mount.c utils_mount.h \ - common.c common.h \ - network.c network.h \ - plugin.c plugin.h \ - configfile.c configfile.h -collectd_CPPFLAGS = $(LTDLINCL) -collectd_CPPFLAGS += -DCONFIGFILE='"${sysconfdir}/${PACKAGE_NAME}.conf"' -collectd_CPPFLAGS += -DPKGLOCALSTATEDIR='"${localstatedir}/lib/${PACKAGE_NAME}"' -if BUILD_FEATURE_DAEMON -collectd_CPPFLAGS += -DPIDFILE='"${localstatedir}/run/${PACKAGE_NAME}.pid"' -endif -collectd_CPPFLAGS += -DPLUGINDIR='"${pkglibdir}"' -if BUILD_FEATURE_DEBUG -collectd_CPPFLAGS += -DLOGFILE='"${localstatedir}/log/${PACKAGE_NAME}/collectd.log"' -endif +noinst_LTLIBRARIES = liboping.la -# Link to these libraries.. -collectd_LDFLAGS = -export-dynamic -if BUILD_WITH_RRDTOOL -collectd_LDFLAGS += -lm -lrrd -endif -if BUILD_WITH_LIBRT -collectd_LDFLAGS += -lrt -endif +#liboping_la_CFLAGS = +liboping_la_LDFLAGS = -avoid-version if BUILD_WITH_LIBSOCKET -collectd_LDFLAGS += -lsocket -endif -if BUILD_WITH_LIBRESOLV -collectd_LDFLAGS += -lresolv -endif -if BUILD_WITH_LIBKSTAT -collectd_LDFLAGS += -lkstat -endif -if BUILD_WITH_LIBDEVINFO -collectd_LDFLAGS += -ldevinfo -endif - -if BUILD_WITH_LIBSTATGRAB -if BUILD_WITH_LIBKVM -collectd_LDFLAGS += -lkvm -endif -if BUILD_WITH_LIBDEVSTAT -collectd_LDFLAGS += -ldevstat -endif -collectd_LDFLAGS += -lstatgrab -endif - - -collectd_LDADD = $(LIBLTDL) libconfig/libconfig.la "-dlopen" self -collectd_DEPENDENCIES = $(LIBLTDL) libconfig/libconfig.la - -pkglib_LTLIBRARIES = - -if BUILD_MODULE_APACHE -pkglib_LTLIBRARIES += apache.la -apache_la_SOURCES = apache.c -apache_la_LDFLAGS = -module -avoid-version -if BUILD_WITH_LIBCURL -apache_la_LDFLAGS += $(BUILD_WITH_LIBCURL_LIBS) -endif -collectd_LDADD += "-dlopen" apache.la -collectd_DEPENDENCIES += apache.la -endif - -if BUILD_MODULE_APPLE_SENSORS -pkglib_LTLIBRARIES += apple_sensors.la -apple_sensors_la_SOURCES = apple_sensors.c -apple_sensors_la_LDFLAGS = -module -avoid-version -if BUILD_WITH_LIBIOKIT -apple_sensors_la_LDFLAGS += -lIOKit -endif -collectd_LDADD += "-dlopen" apple_sensors.la -collectd_DEPENDENCIES += apple_sensors.la -endif - -if BUILD_MODULE_BATTERY -pkglib_LTLIBRARIES += battery.la -battery_la_SOURCES = battery.c -battery_la_LDFLAGS = -module -avoid-version -if BUILD_WITH_LIBIOKIT -battery_la_LDFLAGS += -lIOKit -endif -collectd_LDADD += "-dlopen" battery.la -collectd_DEPENDENCIES += battery.la +liboping_la_LDFLAGS += -lsocket endif - -if BUILD_MODULE_CPU -pkglib_LTLIBRARIES += cpu.la -cpu_la_SOURCES = cpu.c -cpu_la_LDFLAGS = -module -avoid-version -if BUILD_WITH_LIBKSTAT -cpu_la_LDFLAGS += -lkstat -endif -if BUILD_WITH_LIBDEVINFO -cpu_la_LDFLAGS += -ldevinfo -endif -collectd_LDADD += "-dlopen" cpu.la -collectd_DEPENDENCIES += cpu.la -endif - -if BUILD_MODULE_CPUFREQ -pkglib_LTLIBRARIES += cpufreq.la -cpufreq_la_SOURCES = cpufreq.c -cpufreq_la_LDFLAGS = -module -avoid-version -collectd_LDADD += "-dlopen" cpufreq.la -collectd_DEPENDENCIES += cpufreq.la -endif - -if BUILD_MODULE_DF -pkglib_LTLIBRARIES += df.la -df_la_SOURCES = df.c -df_la_LDFLAGS = -module -avoid-version -collectd_LDADD += "-dlopen" df.la -collectd_DEPENDENCIES += df.la -endif - -if BUILD_MODULE_DISK -pkglib_LTLIBRARIES += disk.la -disk_la_SOURCES = disk.c -disk_la_LDFLAGS = -module -avoid-version -if BUILD_WITH_LIBKSTAT -disk_la_LDFLAGS += -lkstat -endif -if BUILD_WITH_LIBDEVINFO -disk_la_LDFLAGS += -ldevinfo -endif -if BUILD_WITH_LIBIOKIT -disk_la_LDFLAGS += -lIOKit -endif -collectd_LDADD += "-dlopen" disk.la -collectd_DEPENDENCIES += disk.la -endif - -#if BUILD_MODULE_QUOTA -#pkglib_LTLIBRARIES += quota.la -#quota_la_SOURCES = quota_plugin.c quota_plugin.h -#quota_la_SOURCES += quota_fs.c quota_fs.h -#quota_la_SOURCES += quota_mnt.c quota_mnt.h -#quota_la_LDFLAGS = -module -avoid-version -#quota_la_CFLAGS = -Werror -#collectd_LDADD += "-dlopen" quota.la -#collectd_DEPENDENCIES += quota.la -#endif - -if BUILD_MODULE_HDDTEMP -pkglib_LTLIBRARIES += hddtemp.la -hddtemp_la_SOURCES = hddtemp.c -hddtemp_la_LDFLAGS = -module -avoid-version -if BUILD_WITH_LIBSOCKET -hddtemp_la_LDFLAGS += -lsocket -endif -collectd_LDADD += "-dlopen" hddtemp.la -collectd_DEPENDENCIES += hddtemp.la -endif - -if BUILD_MODULE_LOAD -pkglib_LTLIBRARIES += load.la -load_la_SOURCES = load.c -load_la_LDFLAGS = -module -avoid-version -collectd_LDADD += "-dlopen" load.la -collectd_DEPENDENCIES += load.la -if BUILD_WITH_LIBSTATGRAB -if BUILD_WITH_LIBKVM -load_la_LDFLAGS += -lkvm -endif -if BUILD_WITH_LIBDEVSTAT -load_la_LDFLAGS += -ldevstat -endif -load_la_LDFLAGS += -lstatgrab -endif -endif - -if BUILD_MODULE_MEMORY -pkglib_LTLIBRARIES += memory.la -memory_la_SOURCES = memory.c -memory_la_LDFLAGS = -module -avoid-version -collectd_LDADD += "-dlopen" memory.la -collectd_DEPENDENCIES += memory.la -if BUILD_WITH_LIBKSTAT -memory_la_LDFLAGS += -lkstat -endif -if BUILD_WITH_LIBDEVINFO -memory_la_LDFLAGS += -ldevinfo -endif -if BUILD_WITH_LIBSTATGRAB -if BUILD_WITH_LIBKVM -memory_la_LDFLAGS += -lkvm -endif -if BUILD_WITH_LIBDEVSTAT -memory_la_LDFLAGS += -ldevstat -endif -memory_la_LDFLAGS += -lstatgrab -endif -endif - -if BUILD_MODULE_MYSQL -pkglib_LTLIBRARIES += mysql.la -mysql_la_SOURCES = mysql.c -mysql_la_LDFLAGS = -module -avoid-version -if BUILD_WITH_LIBMYSQL -mysql_la_LDFLAGS += -lmysqlclient -endif -collectd_LDADD += "-dlopen" mysql.la -collectd_DEPENDENCIES += mysql.la -endif - -if BUILD_MODULE_NFS -pkglib_LTLIBRARIES += nfs.la -nfs_la_SOURCES = nfs.c -nfs_la_LDFLAGS = -module -avoid-version -collectd_LDADD += "-dlopen" nfs.la -collectd_DEPENDENCIES += nfs.la -endif - -if BUILD_MODULE_PING -pkglib_LTLIBRARIES += ping.la -ping_la_SOURCES = ping.c -ping_la_LDFLAGS = -module -avoid-version -ping_la_LIBADD = liboping/liboping.la -ping_la_DEPENDENCIES = liboping/liboping.la -collectd_LDADD += "-dlopen" ping.la -collectd_DEPENDENCIES += ping.la -endif - -if BUILD_MODULE_PROCESSES -pkglib_LTLIBRARIES += processes.la -processes_la_SOURCES = processes.c -processes_la_LDFLAGS = -module -avoid-version -collectd_LDADD += "-dlopen" processes.la -collectd_DEPENDENCIES += processes.la -endif - -if BUILD_MODULE_SENSORS -pkglib_LTLIBRARIES += sensors.la -sensors_la_SOURCES = sensors.c -sensors_la_LDFLAGS = -module -avoid-version -if BUILD_WITH_LM_SENSORS -sensors_la_LDFLAGS += -lsensors -endif -collectd_LDADD += "-dlopen" sensors.la -collectd_DEPENDENCIES += sensors.la -endif - -if BUILD_MODULE_SERIAL -pkglib_LTLIBRARIES += serial.la -serial_la_SOURCES = serial.c -serial_la_LDFLAGS = -module -avoid-version -collectd_LDADD += "-dlopen" serial.la -collectd_DEPENDENCIES += serial.la -endif - -if BUILD_MODULE_SWAP -pkglib_LTLIBRARIES += swap.la -swap_la_SOURCES = swap.c -swap_la_LDFLAGS = -module -avoid-version -collectd_LDADD += "-dlopen" swap.la -collectd_DEPENDENCIES += swap.la -if BUILD_WITH_LIBKSTAT -swap_la_LDFLAGS += -lkstat -endif -if BUILD_WITH_LIBDEVINFO -swap_la_LDFLAGS += -ldevinfo -endif -if BUILD_WITH_LIBSTATGRAB -if BUILD_WITH_LIBKVM -swap_la_LDFLAGS += -lkvm -endif -if BUILD_WITH_LIBDEVSTAT -swap_la_LDFLAGS += -ldevstat -endif -swap_la_LDFLAGS += -lstatgrab -endif -endif - -if BUILD_MODULE_TAPE -pkglib_LTLIBRARIES += tape.la -tape_la_SOURCES = tape.c -tape_la_LDFLAGS = -module -avoid-version -if BUILD_WITH_LIBKSTAT -tape_la_LDFLAGS += -lkstat -endif -if BUILD_WITH_LIBDEVINFO -tape_la_LDFLAGS += -ldevinfo -endif -collectd_LDADD += "-dlopen" tape.la -collectd_DEPENDENCIES += tape.la -endif - -if BUILD_MODULE_TRAFFIC -pkglib_LTLIBRARIES += traffic.la -traffic_la_SOURCES = traffic.c -traffic_la_LDFLAGS = -module -avoid-version -collectd_LDADD += "-dlopen" traffic.la -collectd_DEPENDENCIES += traffic.la -if BUILD_WITH_LIBKSTAT -traffic_la_LDFLAGS += -lkstat -endif -if BUILD_WITH_LIBDEVINFO -traffic_la_LDFLAGS += -ldevinfo -endif -if BUILD_WITH_LIBSTATGRAB -if BUILD_WITH_LIBKVM -traffic_la_LDFLAGS += -lkvm -endif -if BUILD_WITH_LIBDEVSTAT -traffic_la_LDFLAGS += -ldevstat -endif -traffic_la_LDFLAGS += -lstatgrab -endif -endif - -if BUILD_MODULE_USERS -pkglib_LTLIBRARIES += users.la -users_la_SOURCES = users.c -users_la_LDFLAGS = -module -avoid-version -collectd_LDADD += "-dlopen" users.la -collectd_DEPENDENCIES += users.la -endif - -if BUILD_MODULE_VSERVER -pkglib_LTLIBRARIES += vserver.la -vserver_la_SOURCES = vserver.c -vserver_la_LDFLAGS = -module -avoid-version -collectd_LDADD += "-dlopen" vserver.la -collectd_DEPENDENCIES += vserver.la -endif - -if BUILD_MODULE_WIRELESS -pkglib_LTLIBRARIES += wireless.la -wireless_la_SOURCES = wireless.c -wireless_la_LDFLAGS = -module -avoid-version -collectd_LDADD += "-dlopen" wireless.la -collectd_DEPENDENCIES += wireless.la -endif - -man_MANS = collectd.1 collectd.conf.5 -#collectd_1_SOURCES = collectd.pod - -EXTRA_DIST = $(man_MANS) - -.pod.1: - pod2man --release=$(VERSION) --center=$(PACKAGE) $< >$@ - -.pod.5: - pod2man --section=5 --release=$(VERSION) --center=$(PACKAGE) $< >$@ +liboping_la_SOURCES = liboping.h liboping.c diff --git a/src/liboping.c b/src/liboping.c new file mode 100644 index 00000000..de66cd53 --- /dev/null +++ b/src/liboping.c @@ -0,0 +1,1061 @@ +/** + * 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 + +#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 +#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 "liboping.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; + + 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 (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); + + return (ret); +} + +static int ping_send_one_ipv4 (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 (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 (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 (ph, buf, buflen); + if (status < 0) + { + perror ("ping_sendto"); + return (-1); + } + + dprintf ("sendto: status = %i\n", status); + + return (0); +} + +static int ping_send_all (pinghost_t *ph) +{ + pinghost_t *ptr; + + 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); + 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 (ptr) != 0) + { + timerclear (ptr->timer); + continue; + } + } + else if (ptr->addrfamily == AF_INET) + { + dprintf ("Sending ICMPv4 echo request to `%s'\n", ptr->hostname); + if (ping_send_one_ipv4 (ptr) != 0) + { + timerclear (ptr->timer); + continue; + } + } + else /* this should not happen */ + { + dprintf ("Unknown address family: %i\n", ptr->addrfamily); + timerclear (ptr->timer); + continue; + } + + ptr->sequence++; + } + + /* FIXME */ + return (0); +} + +/* + * 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->head) < 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; + } + + 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); +} + +const char *ping_iterator_get_host (pingobj_iter_t *iter) +{ + return (iter->hostname); +} + +double ping_iterator_get_latency (pingobj_iter_t *iter) +{ + return (iter->latency); +} diff --git a/src/liboping.h b/src/liboping.h new file mode 100644 index 00000000..9fd7597f --- /dev/null +++ b/src/liboping.h @@ -0,0 +1,77 @@ +/** + * 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); + +const char *ping_iterator_get_host (pingobj_iter_t *iter); +double ping_iterator_get_latency (pingobj_iter_t *iter); + +const char *ping_get_error (pingobj_t *obj); + +#endif /* OCTO_PING_H */ -- 2.30.2