summary | shortlog | log | commit | commitdiff | tree
raw | patch | inline | side by side (parent: 207d51d)
raw | patch | inline | side by side (parent: 207d51d)
author | Luke Heberling <collectd@c-ware.com> | |
Sat, 6 Aug 2016 12:46:30 +0000 (14:46 +0200) | ||
committer | Florian Forster <ff@octo.it> | |
Sat, 6 Aug 2016 12:46:30 +0000 (14:46 +0200) |
I'm finding collectd's ping plugin inadequate for host lists larger than
a few hundred. It consumes an entire 2.8Ghz CPU and reports random false
packet loss (Latest collectd and liboping from git.verplant.org on
debian squeeze and lenny). This seems to be because of some
inefficiencies in liboping. For instance, a FD is created for every
host. Since each raw socket receives all raw packets, only one FD would
be needed for all hosts (of the same addrfamily). Besides consuming all
those additional FD's, each reply packet is received and processed
separately on each FD causing O(n^2) complexity. Also for each packet
processed on each FD, a scan of all hosts is performed causing O(n^3).
So, I implemented some new features for liboping:
* use icmp ident as "hash" for table lookup on RX
* allocate one socket FD per addressfamily (not per host)
* multiplex socket writes in the same select loop as reads
* This was necessary because sometimes the first reply
is received before last request is sent.
It's working well for me in testing so far. 1-3% CPU with 1000 hosts,
compared to 100% and useless at 500 hosts before these changes. If
there's interest I'll update this thread with any significant changes.
As always, changes to address style/portability/other issues that
inhibit integration upstream will be my pleasure.
a few hundred. It consumes an entire 2.8Ghz CPU and reports random false
packet loss (Latest collectd and liboping from git.verplant.org on
debian squeeze and lenny). This seems to be because of some
inefficiencies in liboping. For instance, a FD is created for every
host. Since each raw socket receives all raw packets, only one FD would
be needed for all hosts (of the same addrfamily). Besides consuming all
those additional FD's, each reply packet is received and processed
separately on each FD causing O(n^2) complexity. Also for each packet
processed on each FD, a scan of all hosts is performed causing O(n^3).
So, I implemented some new features for liboping:
* use icmp ident as "hash" for table lookup on RX
* allocate one socket FD per addressfamily (not per host)
* multiplex socket writes in the same select loop as reads
* This was necessary because sometimes the first reply
is received before last request is sent.
It's working well for me in testing so far. 1-3% CPU with 1000 hosts,
compared to 100% and useless at 500 hosts before these changes. If
there's interest I'll update this thread with any significant changes.
As always, changes to address style/portability/other issues that
inhibit integration upstream will be my pleasure.
src/liboping.c | patch | blob | history |
diff --git a/src/liboping.c b/src/liboping.c
index 83ca9c242c072cb61622ede4c0058d51c9036f69..776f24f09e2af9f6b25ee248f759a76514ce3867 100644 (file)
--- a/src/liboping.c
+++ b/src/liboping.c
#endif
#define PING_ERRMSG_LEN 256
+#define PING_TABLE_LEN 5381
struct pinghost
{
struct sockaddr_storage *addr;
socklen_t addrlen;
int addrfamily;
- int fd;
int ident;
int sequence;
struct timeval *timer;
void *context;
struct pinghost *next;
+ struct pinghost *table_next;
};
struct pingobj
uint8_t qos;
char *data;
+ int fd4;
+ int fd6;
+
struct sockaddr *srcaddr;
socklen_t srcaddrlen;
char errmsg[PING_ERRMSG_LEN];
pinghost_t *head;
+ pinghost_t *table[PING_TABLE_LEN];
};
/*
ident = ntohs (icmp_hdr->icmp_id);
seq = ntohs (icmp_hdr->icmp_seq);
- /* We have to iterate over all hosts, since ICMPv4 packets may
- * be received on any raw v4 socket. */
- for (ptr = obj->head; ptr != NULL; ptr = ptr->next)
+ for (ptr = obj->table[ident % PING_TABLE_LEN];
+ ptr != NULL; ptr = ptr->table_next)
{
dprintf ("hostname = %s, ident = 0x%04x, seq = %i\n",
ptr->hostname, ptr->ident, ((ptr->sequence - 1) & 0xFFFF));
return (ptr);
}
-static int ping_receive_one (pingobj_t *obj, const pinghost_t *ph,
- struct timeval *now)
+static int ping_receive_one (pingobj_t *obj, struct timeval *now, int addrfam)
{
- /* Note: 'ph' is not necessarily the host object for which we receive a
- * reply. The right object will be returned by ping_receive_ipv*(). For
- * now, we can only rely on ph->fd and ph->addrfamily. */
-
+ int fd = addrfam == AF_INET6 ? obj->fd6 : obj->fd4;
struct timeval diff, pkt_now = *now;
pinghost_t *host = NULL;
int recv_ttl;
msghdr.msg_flags |= MSG_XPG4_2;
#endif
- payload_buffer_len = recvmsg (ph->fd, &msghdr, /* flags = */ 0);
+ payload_buffer_len = recvmsg (fd, &msghdr, /* flags = */ 0);
if (payload_buffer_len < 0)
{
#if WITH_DEBUG
#endif
return (-1);
}
- dprintf ("Read %zi bytes from fd = %i\n", payload_buffer_len, ph->fd);
+ dprintf ("Read %zi bytes from fd = %i\n", payload_buffer_len, fd);
/* Iterate over all auxiliary data in msghdr */
recv_ttl = -1;
memcpy (&pkt_now, CMSG_DATA (cmsg), sizeof (pkt_now));
#endif /* SO_TIMESTAMP */
}
- else if (ph->addrfamily == AF_INET) /* {{{ */
+ else if (addrfam == AF_INET) /* {{{ */
{
if (cmsg->cmsg_level != IPPROTO_IP)
continue;
cmsg->cmsg_type);
}
} /* }}} */
- else if (ph->addrfamily == AF_INET6) /* {{{ */
+ else if (addrfam == AF_INET6) /* {{{ */
{
if (cmsg->cmsg_level != IPPROTO_IPV6)
continue;
}
} /* }}} for (cmsg) */
- if (ph->addrfamily == AF_INET)
+ if (addrfam == AF_INET)
{
host = ping_receive_ipv4 (obj, payload_buffer, payload_buffer_len);
if (host == NULL)
return (-1);
}
- else if (ph->addrfamily == AF_INET6)
+ else if (addrfam == AF_INET6)
{
host = ping_receive_ipv6 (obj, payload_buffer, payload_buffer_len);
if (host == NULL)
else
{
dprintf ("ping_receive_one: Unknown address family %i.\n",
- ph->addrfamily);
+ addrfam);
return (-1);
}
return (0);
}
-static int ping_receive_all (pingobj_t *obj)
-{
- fd_set read_fds;
- fd_set err_fds;
- int num_fds;
- int max_fd;
-
- 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;
- ptr->recv_ttl = -1;
- }
-
- if (gettimeofday (&nowtime, NULL) == -1)
- {
- ping_set_errno (obj, 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 (&read_fds);
- FD_ZERO (&err_fds);
- num_fds = 0;
- max_fd = -1;
-
- for (ptr = ph; ptr != NULL; ptr = ptr->next)
- {
- if (!timerisset (ptr->timer))
- continue;
-
- FD_SET (ptr->fd, &read_fds);
- FD_SET (ptr->fd, &err_fds);
- num_fds++;
-
- if (max_fd < ptr->fd)
- max_fd = ptr->fd;
- }
-
- if (num_fds == 0)
- break;
-
- if (gettimeofday (&nowtime, NULL) == -1)
- {
- ping_set_errno (obj, errno);
- return (-1);
- }
-
- if (ping_timeval_sub (&endtime, &nowtime, &timeout) == -1)
- break;
-
- dprintf ("Waiting on %i sockets for %i.%06i seconds\n", num_fds,
- (int) timeout.tv_sec,
- (int) timeout.tv_usec);
-
- status = select (max_fd + 1, &read_fds, NULL, &err_fds, &timeout);
-
- if (gettimeofday (&nowtime, NULL) == -1)
- {
- ping_set_errno (obj, errno);
- return (-1);
- }
-
- if ((status == -1) && (errno == EINTR))
- {
- dprintf ("select was interrupted by signal..\n");
- continue;
- }
- else if (status < 0)
- {
-#if WITH_DEBUG
- char errbuf[PING_ERRMSG_LEN];
- dprintf ("select: %s\n",
- sstrerror (errno, errbuf, sizeof (errbuf)));
-#endif
- break;
- }
- else if (status == 0)
- {
- dprintf ("select timed out\n");
- for (ptr = ph; ptr != NULL; ptr = ptr->next)
- if (ptr->latency < 0.0)
- ptr->dropped++;
- break;
- }
-
- for (ptr = ph; ptr != NULL; ptr = ptr->next)
- {
- if (FD_ISSET (ptr->fd, &read_fds))
- {
- if (ping_receive_one (obj, ptr, &nowtime) == 0)
- ret++;
- }
- else if (FD_ISSET (ptr->fd, &err_fds))
- {
- /* clear the timer in this case so that we
- * don't run into an endless loop. */
- /* TODO: Set an error flag in this case. */
- timerclear (ptr->timer);
- }
- }
- } /* while (1) */
-
- return (ret);
-}
-
/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
* Sending functions: *
* *
* `-> ping_send_one_ipv6 *
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
static ssize_t ping_sendto (pingobj_t *obj, pinghost_t *ph,
- const void *buf, size_t buflen)
+ const void *buf, size_t buflen, int fd)
{
ssize_t ret;
return (-1);
}
- ret = sendto (ph->fd, buf, buflen, 0,
+ ret = sendto (fd, buf, buflen, 0,
(struct sockaddr *) ph->addr, ph->addrlen);
if (ret < 0)
return (ret);
}
-static int ping_send_one_ipv4 (pingobj_t *obj, pinghost_t *ph)
+static int ping_send_one_ipv4 (pingobj_t *obj, pinghost_t *ph, int fd)
{
struct icmp *icmp4;
int status;
dprintf ("Sending ICMPv4 package with ID 0x%04x\n", ph->ident);
- status = ping_sendto (obj, ph, buf, buflen);
+ status = ping_sendto (obj, ph, buf, buflen, fd);
if (status < 0)
{
perror ("ping_sendto");
return (0);
}
-static int ping_send_one_ipv6 (pingobj_t *obj, pinghost_t *ph)
+static int ping_send_one_ipv6 (pingobj_t *obj, pinghost_t *ph, int fd)
{
struct icmp6_hdr *icmp6;
int status;
dprintf ("Sending ICMPv6 package with ID 0x%04x\n", ph->ident);
- status = ping_sendto (obj, ph, buf, buflen);
+ status = ping_sendto (obj, ph, buf, buflen, fd);
if (status < 0)
{
perror ("ping_sendto");
return (0);
}
-static int ping_send_all (pingobj_t *obj)
+static int ping_send_one (pingobj_t *obj, pinghost_t *ptr, int fd)
{
- pinghost_t *ph;
- pinghost_t *ptr;
-
- int ret;
-
- ret = 0;
- ph = obj->head;
-
- for (ptr = ph; ptr != NULL; ptr = ptr->next)
+ if (gettimeofday (ptr->timer, NULL) == -1)
{
/* start timer.. The GNU `ping6' starts the timer before
* sending the packet, so I will do that too */
- if (gettimeofday (ptr->timer, NULL) == -1)
- {
#if WITH_DEBUG
- char errbuf[PING_ERRMSG_LEN];
- dprintf ("gettimeofday: %s\n",
- sstrerror (errno, errbuf, sizeof (errbuf)));
+ char errbuf[PING_ERRMSG_LEN];
+ dprintf ("gettimeofday: %s\n",
+ sstrerror (errno, errbuf, sizeof (errbuf)));
#endif
- timerclear (ptr->timer);
- ret--;
- continue;
- }
- else
- {
- dprintf ("timer set for hostname = %s\n", ptr->hostname);
- }
+ timerclear (ptr->timer);
+ return (-1);
+ }
+ 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)
+ if (ptr->addrfamily == AF_INET6)
+ {
+ dprintf ("Sending ICMPv6 echo request to `%s'\n", ptr->hostname);
+ if (ping_send_one_ipv6 (obj, ptr, fd) != 0)
{
- dprintf ("Sending ICMPv4 echo request to `%s'\n", ptr->hostname);
- if (ping_send_one_ipv4 (obj, ptr) != 0)
- {
- timerclear (ptr->timer);
- ret--;
- continue;
- }
+ timerclear (ptr->timer);
+ return (-1);
}
- else /* this should not happen */
+ }
+ else if (ptr->addrfamily == AF_INET)
+ {
+ dprintf ("Sending ICMPv4 echo request to `%s'\n", ptr->hostname);
+ if (ping_send_one_ipv4 (obj, ptr, fd) != 0)
{
- dprintf ("Unknown address family: %i\n", ptr->addrfamily);
timerclear (ptr->timer);
- ret--;
- continue;
+ return (-1);
}
-
- ptr->sequence++;
+ }
+ else /* this should not happen */
+ {
+ dprintf ("Unknown address family: %i\n", ptr->addrfamily);
+ timerclear (ptr->timer);
+ return (-1);
}
- return (ret);
+ ptr->sequence++;
+
+ return (0);
}
/*
* Set the TTL of a socket protocol independently.
*/
-static int ping_set_ttl (pinghost_t *ph, int ttl)
+static int ping_set_ttl (pingobj_t *obj, int ttl)
{
- int ret = -2;
+ int ret = 0;
+ char errbuf[PING_ERRMSG_LEN];
- if (ph->addrfamily == AF_INET)
+ if (obj->fd4 != -1)
{
- dprintf ("Setting TTLv4 to %i\n", ttl);
- ret = setsockopt (ph->fd, IPPROTO_IP, IP_TTL,
- &ttl, sizeof (ttl));
+ if (setsockopt (obj->fd4, IPPROTO_IP, IP_TTL,
+ &ttl, sizeof (ttl)))
+ {
+ ret = errno;
+ ping_set_error (obj, "ping_set_ttl",
+ sstrerror (ret, errbuf, sizeof (errbuf)));
+ dprintf ("Setting TTLv4 failed: %s\n", errbuf);
+ }
}
- else if (ph->addrfamily == AF_INET6)
+
+ if (obj->fd6 != -1)
{
dprintf ("Setting TTLv6 to %i\n", ttl);
- ret = setsockopt (ph->fd, IPPROTO_IPV6, IPV6_UNICAST_HOPS,
- &ttl, sizeof (ttl));
+ if (setsockopt (obj->fd6, IPPROTO_IPV6, IPV6_UNICAST_HOPS,
+ &ttl, sizeof (ttl)))
+ {
+ ret = errno;
+ ping_set_error (obj, "ping_set_ttl",
+ sstrerror (ret, errbuf, sizeof (errbuf)));
+ dprintf ("Setting TTLv6 failed: %s\n", errbuf);
+ }
}
return (ret);
* Using SOL_SOCKET / SO_PRIORITY might be a protocol independent way to
* set this. See socket(7) for details.
*/
-static int ping_set_qos (pingobj_t *obj, pinghost_t *ph, uint8_t qos)
+static int ping_set_qos (pingobj_t *obj, uint8_t qos)
{
- int ret = EINVAL;
+ int ret = 0;
char errbuf[PING_ERRMSG_LEN];
- if (ph->addrfamily == AF_INET)
+ if (obj->fd4 != -1)
{
dprintf ("Setting TP_TOS to %#04"PRIx8"\n", qos);
- ret = setsockopt (ph->fd, IPPROTO_IP, IP_TOS,
- &qos, sizeof (qos));
- if (ret != 0)
+ if (setsockopt (obj->fd4, IPPROTO_IP, IP_TOS,
+ &qos, sizeof (qos)))
{
ret = errno;
ping_set_error (obj, "ping_set_qos",
dprintf ("Setting TP_TOS failed: %s\n", errbuf);
}
}
- else if (ph->addrfamily == AF_INET6)
+
+ if (obj->fd6 != -1)
{
/* IPV6_TCLASS requires an "int". */
int tmp = (int) qos;
dprintf ("Setting IPV6_TCLASS to %#04"PRIx8" (%i)\n", qos, tmp);
- ret = setsockopt (ph->fd, IPPROTO_IPV6, IPV6_TCLASS,
- &tmp, sizeof (tmp));
- if (ret != 0)
+ if (setsockopt (obj->fd6, IPPROTO_IPV6, IPV6_TCLASS,
+ &tmp, sizeof (tmp)))
{
ret = errno;
ping_set_error (obj, "ping_set_qos",
ph->addr = (struct sockaddr_storage *) (ph->timer + 1);
ph->addrlen = sizeof (struct sockaddr_storage);
- ph->fd = -1;
ph->latency = -1.0;
ph->dropped = 0;
ph->ident = ping_get_ident () & 0xFFFF;
static void ping_free (pinghost_t *ph)
{
- if (ph->fd >= 0)
- close (ph->fd);
-
if (ph->username != NULL)
free (ph->username);
free (ph);
}
+static int ping_open_socket(pingobj_t *obj, int addrfam)
+{
+ int fd;
+ if (addrfam == AF_INET6)
+ {
+ fd = socket(addrfam, SOCK_RAW, IPPROTO_ICMPV6);
+ }
+ else if (addrfam == AF_INET)
+ {
+ fd = socket(addrfam, SOCK_RAW, IPPROTO_ICMP);
+ }
+ else /* this should not happen */
+ {
+ dprintf ("Unknown address family: %i\n", addrfam);
+ return (-1);
+ }
+
+ if (fd == -1)
+ {
+#if WITH_DEBUG
+ char errbuf[PING_ERRMSG_LEN];
+ dprintf ("socket: %s\n",
+ sstrerror (errno, errbuf, sizeof (errbuf)));
+#endif
+ ping_set_errno (obj, errno);
+ return -1;
+ }
+
+ if (obj->srcaddr != NULL)
+ {
+ assert (obj->srcaddrlen > 0);
+ assert (obj->srcaddrlen <= sizeof (struct sockaddr_storage));
+
+ if (bind (fd, obj->srcaddr, obj->srcaddrlen) == -1)
+ {
+#if WITH_DEBUG
+ char errbuf[PING_ERRMSG_LEN];
+ dprintf ("bind: %s\n",
+ sstrerror (errno, errbuf, sizeof (errbuf)));
+#endif
+ ping_set_errno (obj, errno);
+ close (fd);
+ return -1;
+ }
+ }
+
+#ifdef SO_BINDTODEVICE
+ if (obj->device != NULL)
+ {
+ if (setsockopt (fd, SOL_SOCKET, SO_BINDTODEVICE,
+ obj->device, strlen (obj->device) + 1) != 0)
+ {
+#if WITH_DEBUG
+ char errbuf[PING_ERRMSG_LEN];
+ dprintf ("setsockopt (SO_BINDTODEVICE): %s\n",
+ sstrerror (errno, errbuf, sizeof (errbuf)));
+#endif
+ ping_set_errno (obj, errno);
+ close (fd);
+ return -1;
+ }
+ }
+#endif /* SO_BINDTODEVICE */
+#ifdef SO_TIMESTAMP
+ if (1) /* {{{ */
+ {
+ int status;
+ int opt = 1;
+
+ status = setsockopt (fd,
+ SOL_SOCKET, SO_TIMESTAMP,
+ &opt, sizeof (opt));
+ if (status != 0)
+ {
+#if WITH_DEBUG
+ char errbuf[PING_ERRMSG_LEN];
+ dprintf ("setsockopt (SO_TIMESTAMP): %s\n",
+ sstrerror (errno, errbuf, sizeof (errbuf)));
+#endif
+ ping_set_errno (obj, errno);
+ close (fd);
+ return -1;
+ }
+ } /* }}} if (1) */
+#endif /* SO_TIMESTAMP */
+
+ if (addrfam == AF_INET)
+ {
+ int opt;
+
+ /* Enable receiving the TOS field */
+ opt = 1;
+ setsockopt (fd, IPPROTO_IP, IP_RECVTOS,
+ &opt, sizeof (opt));
+
+ /* Enable receiving the TTL field */
+ opt = 1;
+ setsockopt (fd, IPPROTO_IP, IP_RECVTTL,
+ &opt, sizeof (opt));
+ }
+#if defined(IPV6_RECVHOPLIMIT) || defined(IPV6_RECVTCLASS)
+ else if (addrfam == AF_INET6)
+ {
+ int opt;
+
+# if defined(IPV6_RECVHOPLIMIT)
+ /* For details see RFC 3542, section 6.3. */
+ opt = 1;
+ setsockopt (fd, IPPROTO_IPV6, IPV6_RECVHOPLIMIT,
+ &opt, sizeof (opt));
+# endif /* IPV6_RECVHOPLIMIT */
+
+# if defined(IPV6_RECVTCLASS)
+ /* For details see RFC 3542, section 6.5. */
+ opt = 1;
+ setsockopt (fd, IPPROTO_IPV6, IPV6_RECVTCLASS,
+ &opt, sizeof (opt));
+# endif /* IPV6_RECVTCLASS */
+ }
+#endif /* IPV6_RECVHOPLIMIT || IPV6_RECVTCLASS */
+
+ return fd;
+}
+
/*
* public methods
*/
obj->addrfamily = PING_DEF_AF;
obj->data = strdup (PING_DEF_DATA);
obj->qos = 0;
+ obj->fd4 = -1;
+ obj->fd6 = -1;
return (obj);
}
if (obj->device != NULL)
free (obj->device);
+ if (obj->fd4 != -1)
+ close(obj->fd4);
+
+ if (obj->fd6 != -1)
+ close(obj->fd6);
+
free (obj);
return;
{
case PING_OPT_QOS:
{
- pinghost_t *ph;
-
obj->qos = *((uint8_t *) value);
- for (ph = obj->head; ph != NULL; ph = ph->next)
- ping_set_qos (obj, ph, obj->qos);
+ ret = ping_set_qos (obj, obj->qos);
break;
}
break;
case PING_OPT_TTL:
- obj->ttl = *((int *) value);
- if ((obj->ttl < 1) || (obj->ttl > 255))
+ ret = *((int *) value);
+ if ((ret < 1) || (ret > 255))
{
obj->ttl = PING_DEF_TTL;
ret = -1;
}
else
{
- pinghost_t *ph;
-
- for (ph = obj->head; ph != NULL; ph = ph->next)
- ping_set_ttl (ph, obj->ttl);
+ obj->ttl = ret;
+ ret = ping_set_ttl (obj, obj->ttl);
}
break;
return (ret);
} /* int ping_setopt */
-
int ping_send (pingobj_t *obj)
{
- int ret;
+ fd_set read_fds;
+ fd_set write_fds;
+ fd_set err_fds;
- if (obj == NULL)
+ int num_fds;
+ int max_fd;
+
+ pinghost_t *ph;
+ pinghost_t *ptr;
+
+ struct timeval endtime;
+ struct timeval nowtime;
+ struct timeval timeout;
+ int status;
+
+ int pings = 0;
+ int ret = 0;
+
+ ph = obj->head;
+
+ int fd4 = obj->fd4;
+ int fd6 = obj->fd6;
+
+ for (ptr = ph; ptr != NULL; ptr = ptr->next)
+ {
+ if (fd6 == -1 && ptr->addrfamily == AF_INET6)
+ {
+ obj->fd6 = fd6 = ping_open_socket(obj, AF_INET6);
+ ping_set_ttl (obj, obj->ttl);
+ ping_set_qos (obj, obj->qos);
+ }
+ else if (fd4 == -1 && ptr->addrfamily == AF_INET)
+ {
+ obj->fd4 = fd4 = ping_open_socket(obj, AF_INET);
+ ping_set_ttl (obj, obj->ttl);
+ ping_set_qos (obj, obj->qos);
+ }
+
+ if ((fd6 == -1 && ptr->addrfamily == AF_INET6)
+ || (fd4 == -1 && ptr->addrfamily == AF_INET))
+ {
+#if WITH_DEBUG
+ char errbuf[PING_ERRMSG_LEN];
+ dprintf ("socket: %s\n",
+ sstrerror (errno, errbuf, sizeof (errbuf)));
+#endif
+ ping_set_errno (obj, errno);
+ return (-1);
+ }
+
+ ptr->latency = -1.0;
+ ptr->recv_ttl = -1;
+ }
+
+ if (fd4 == -1 && fd6 == -1)
+ {
+ dprintf("No sockets to use\n");
return (-1);
+ }
- if (ping_send_all (obj) < 0)
+ if (gettimeofday (&nowtime, NULL) == -1)
+ {
+ ping_set_errno (obj, 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);
+
+ ptr = ph;
+ num_fds = 0;
+ if (fd4 != -1) num_fds++;
+ if (fd6 != -1) num_fds++;
+ max_fd = fd4 > fd6 ? fd4 : fd6;
+
+ while (pings > 0 || ptr != NULL)
+ {
+ FD_ZERO (&read_fds);
+ FD_ZERO (&write_fds);
+ FD_ZERO (&err_fds);
+
+ if (fd4 != -1) FD_SET(fd4, &read_fds);
+ if (fd6 != -1) FD_SET(fd6, &read_fds);
+
+ if (fd4 != -1 && ptr != NULL && ptr->addrfamily == AF_INET)
+ FD_SET(fd4, &write_fds);
+ if (fd6 != -1 && ptr != NULL && ptr->addrfamily == AF_INET6)
+ FD_SET(fd6, &write_fds);
+ if (fd4 != -1) FD_SET(fd4, &err_fds);
+ if (fd6 != -1) FD_SET(fd6, &err_fds);
+
+ if (gettimeofday (&nowtime, NULL) == -1)
+ {
+ ping_set_errno (obj, errno);
+ return (-1);
+ }
+
+ if (ping_timeval_sub (&endtime, &nowtime, &timeout) == -1)
+ break;
+
+ dprintf ("Waiting on %i sockets for %i.%06i seconds\n", num_fds,
+ (int) timeout.tv_sec,
+ (int) timeout.tv_usec);
- if ((ret = ping_receive_all (obj)) < 0)
- return (-2);
+ status = select (max_fd + 1, &read_fds, &write_fds, &err_fds, &timeout);
+
+ if (gettimeofday (&nowtime, NULL) == -1)
+ {
+ ping_set_errno (obj, errno);
+ return (-1);
+ }
+
+ if ((status == -1) && (errno == EINTR))
+ {
+ dprintf ("select was interrupted by signal..\n");
+ continue;
+ }
+ else if (status < 0)
+ {
+#if WITH_DEBUG
+ char errbuf[PING_ERRMSG_LEN];
+ dprintf ("select: %s\n",
+ sstrerror (errno, errbuf, sizeof (errbuf)));
+#endif
+ break;
+ }
+ else if (status == 0)
+ {
+ dprintf ("select timed out\n");
+ for (ptr = ph; ptr != NULL; ptr = ptr->next)
+ if (ptr->latency < 0.0)
+ ptr->dropped++;
+
+ break;
+ }
+
+ if (fd4 != -1)
+ {
+ if (FD_ISSET (fd4, &read_fds))
+ {
+ if (!ping_receive_one(obj, &nowtime, AF_INET))
+ --pings;
+ }
+ else if (ptr != NULL && ptr->addrfamily == AF_INET &&
+ FD_ISSET (fd4, &write_fds))
+ {
+ if (!ping_send_one(obj, ptr, fd4))
+ {
+ ptr = ptr->next;
+ ++pings;
+ }
+ else
+ {
+ --ret;
+ }
+ }
+
+ }
+
+ if (fd6 != -1)
+ {
+ if (FD_ISSET (fd6, &read_fds))
+ {
+ if (!ping_receive_one(obj, &nowtime, AF_INET6))
+ --pings;
+ }
+ else if (ptr != NULL && ptr->addrfamily == AF_INET6 &&
+ FD_ISSET (fd6, &write_fds))
+ {
+ if (!ping_send_one(obj, ptr, fd6))
+ {
+ ++pings;
+ ptr = ptr->next;
+ }
+ else
+ {
+ --ret;
+ }
+ }
+ }
+
+ } /* while (1) */
return (ret);
-}
+} /* int ping_send */
static pinghost_t *ping_host_search (pinghost_t *ph, const char *host)
{
for (ai_ptr = ai_list; ai_ptr != NULL; ai_ptr = ai_ptr->ai_next)
{
- ph->fd = -1;
-
if (ai_ptr->ai_family == AF_INET)
{
ai_ptr->ai_socktype = SOCK_RAW;
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)
- {
-#if WITH_DEBUG
- char errbuf[PING_ERRMSG_LEN];
- dprintf ("socket: %s\n",
- sstrerror (errno, errbuf, sizeof (errbuf)));
-#endif
- ping_set_errno (obj, errno);
- continue;
- }
-
- if (obj->srcaddr != NULL)
- {
- assert (obj->srcaddrlen > 0);
- assert (obj->srcaddrlen <= sizeof (struct sockaddr_storage));
-
- if (bind (ph->fd, obj->srcaddr, obj->srcaddrlen) == -1)
- {
-#if WITH_DEBUG
- char errbuf[PING_ERRMSG_LEN];
- dprintf ("bind: %s\n",
- sstrerror (errno, errbuf, sizeof (errbuf)));
-#endif
- ping_set_errno (obj, errno);
- close (ph->fd);
- ph->fd = -1;
- continue;
- }
- }
-
-#ifdef SO_BINDTODEVICE
- if (obj->device != NULL)
- {
- if (setsockopt (ph->fd, SOL_SOCKET, SO_BINDTODEVICE,
- obj->device, strlen (obj->device) + 1) != 0)
- {
-#if WITH_DEBUG
- char errbuf[PING_ERRMSG_LEN];
- dprintf ("setsockopt (SO_BINDTODEVICE): %s\n",
- sstrerror (errno, errbuf, sizeof (errbuf)));
-#endif
- ping_set_errno (obj, errno);
- close (ph->fd);
- ph->fd = -1;
- continue;
- }
- }
-#endif /* SO_BINDTODEVICE */
-#ifdef SO_TIMESTAMP
- if (1) /* {{{ */
- {
- int status;
- int opt = 1;
-
- status = setsockopt (ph->fd,
- SOL_SOCKET, SO_TIMESTAMP,
- &opt, sizeof (opt));
- if (status != 0)
- {
-#if WITH_DEBUG
- char errbuf[PING_ERRMSG_LEN];
- dprintf ("setsockopt (SO_TIMESTAMP): %s\n",
- sstrerror (errno, errbuf, sizeof (errbuf)));
-#endif
- ping_set_errno (obj, errno);
- close (ph->fd);
- ph->fd = -1;
- continue;
- }
- } /* }}} if (1) */
-#endif /* SO_TIMESTAMP */
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);
}
#endif /* AI_CANONNAME */
- if (ph->addrfamily == AF_INET)
- {
- int opt;
-
- /* Enable receiving the TOS field */
- opt = 1;
- setsockopt (ph->fd, IPPROTO_IP, IP_RECVTOS,
- &opt, sizeof (opt));
-
- /* Enable receiving the TTL field */
- opt = 1;
- setsockopt (ph->fd, IPPROTO_IP, IP_RECVTTL,
- &opt, sizeof (opt));
- }
-#if defined(IPV6_RECVHOPLIMIT) || defined(IPV6_RECVTCLASS)
- else if (ph->addrfamily == AF_INET6)
- {
- int opt;
-
-# if defined(IPV6_RECVHOPLIMIT)
- /* For details see RFC 3542, section 6.3. */
- opt = 1;
- setsockopt (ph->fd, IPPROTO_IPV6, IPV6_RECVHOPLIMIT,
- &opt, sizeof (opt));
-# endif /* IPV6_RECVHOPLIMIT */
-
-# if defined(IPV6_RECVTCLASS)
- /* For details see RFC 3542, section 6.5. */
- opt = 1;
- setsockopt (ph->fd, IPPROTO_IPV6, IPV6_RECVTCLASS,
- &opt, sizeof (opt));
-# endif /* IPV6_RECVTCLASS */
- }
-#endif /* IPV6_RECVHOPLIMIT || IPV6_RECVTCLASS */
-
- break;
} /* for (ai_ptr = ai_list; ai_ptr != NULL; ai_ptr = ai_ptr->ai_next) */
freeaddrinfo (ai_list);
- if (ph->fd < 0)
- {
- ping_free (ph);
- return (-1);
- }
-
/*
* Adding in the front is much easier, but then the iterator will
* return the host that was added last as first host. That's just not
hptr->next = ph;
}
- ping_set_ttl (ph, obj->ttl);
- ping_set_qos (obj, ph, obj->qos);
+ ph->table_next = obj->table[ph->ident % PING_TABLE_LEN];
+ obj->table[ph->ident % PING_TABLE_LEN] = ph;
return (0);
} /* int ping_host_add */
int ping_host_remove (pingobj_t *obj, const char *host)
{
- pinghost_t *pre, *cur;
+ pinghost_t *pre, *cur, *target;
if ((obj == NULL) || (host == NULL))
return (-1);
else
pre->next = cur->next;
+ target = cur;
+ pre = NULL;
+
+ cur = obj->table[target->ident % PING_TABLE_LEN];
+
+ while (cur != NULL)
+ {
+ if (cur == target)
+ break;
+
+ pre = cur;
+ cur = cur->table_next;
+ }
+
+ if (cur == NULL)
+ {
+ ping_set_error(obj, "ping_host_remove", "Host not found (T)");
+ ping_free(target);
+ return (-1);
+ }
+
+ if (pre == NULL)
+ obj->table[target->ident % PING_TABLE_LEN] = cur->table_next;
+ else
+ pre->table_next = cur->table_next;
+
ping_free (cur);
return (0);