diff --git a/src/liboping.c b/src/liboping.c
index bea0be840d06efd0dddf67c842ada89ffc8b4b50..e80ea294d660f75fcb98b5c0d02f95b184d7207d 100644 (file)
--- a/src/liboping.c
+++ b/src/liboping.c
# include <stdlib.h>
# include <stdio.h>
# include <string.h>
-# include <stdint.h>
# include <inttypes.h>
# include <errno.h>
# include <assert.h>
# error "You don't have the standard C99 header files installed"
#endif /* STDC_HEADERS */
+#ifdef HAVE_STDINT_H
+# include <stdint.h>
+#endif
+
#if HAVE_UNISTD_H
# include <unistd.h>
#endif
#if HAVE_SYS_SOCKET_H
# include <sys/socket.h>
#endif
+
#if HAVE_NETDB_H
# include <netdb.h>
#endif
struct timeval *timer;
double latency;
uint32_t dropped;
+ int recv_ttl;
char *data;
void *context;
struct sockaddr *srcaddr;
socklen_t srcaddrlen;
+ char *device;
+
char errmsg[PING_ERRMSG_LEN];
pinghost_t *head;
/* Even though Posix requires "strerror_r" to return an "int",
* some systems (e.g. the GNU libc) return a "char *" _and_
* ignore the second argument ... -tokkee */
-char *sstrerror (int errnum, char *buf, size_t buflen)
+static char *sstrerror (int errnum, char *buf, size_t buflen)
{
buf[0] = 0;
return (ret);
}
-static pinghost_t *ping_receive_ipv4 (pinghost_t *ph, char *buffer, size_t buffer_len)
+static pinghost_t *ping_receive_ipv4 (pingobj_t *obj, char *buffer,
+ size_t buffer_len)
{
struct ip *ip_hdr;
struct icmp *icmp_hdr;
@@ -301,7 +309,8 @@ static pinghost_t *ping_receive_ipv4 (pinghost_t *ph, char *buffer, size_t buffe
if (recv_checksum != calc_checksum)
{
- dprintf ("Checksum missmatch: Got 0x%04x, calculated 0x%04x\n",
+ dprintf ("Checksum missmatch: Got 0x%04"PRIx16", "
+ "calculated 0x%04"PRIx16"\n",
recv_checksum, calc_checksum);
return (NULL);
}
@@ -309,7 +318,9 @@ static pinghost_t *ping_receive_ipv4 (pinghost_t *ph, char *buffer, size_t buffe
ident = ntohs (icmp_hdr->icmp_id);
seq = ntohs (icmp_hdr->icmp_seq);
- for (ptr = ph; ptr != NULL; ptr = ptr->next)
+ /* 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)
{
dprintf ("hostname = %s, ident = 0x%04x, seq = %i\n",
ptr->hostname, ptr->ident, ((ptr->sequence - 1) & 0xFFFF));
@@ -326,7 +337,8 @@ static pinghost_t *ping_receive_ipv4 (pinghost_t *ph, char *buffer, size_t buffe
if (((ptr->sequence - 1) & 0xFFFF) != seq)
continue;
- dprintf ("Match found: hostname = %s, ident = 0x%04x, seq = %i\n",
+ dprintf ("Match found: hostname = %s, ident = 0x%04"PRIx16", "
+ "seq = %"PRIu16"\n",
ptr->hostname, ident, seq);
break;
@@ -334,10 +346,13 @@ static pinghost_t *ping_receive_ipv4 (pinghost_t *ph, char *buffer, size_t buffe
if (ptr == NULL)
{
- dprintf ("No match found for ident = 0x%04x, seq = %i\n",
+ dprintf ("No match found for ident = 0x%04"PRIx16", seq = %"PRIu16"\n",
ident, seq);
}
+ if (ptr != NULL)
+ ptr->recv_ttl = ip_hdr->ip_ttl;
+
return (ptr);
}
@@ -357,7 +372,8 @@ static pinghost_t *ping_receive_ipv4 (pinghost_t *ph, char *buffer, size_t buffe
# endif
#endif
-static pinghost_t *ping_receive_ipv6 (pinghost_t *ph, char *buffer, size_t buffer_len)
+static pinghost_t *ping_receive_ipv6 (pingobj_t *obj, char *buffer,
+ size_t buffer_len)
{
struct icmp6_hdr *icmp_hdr;
@@ -388,7 +404,9 @@ static pinghost_t *ping_receive_ipv6 (pinghost_t *ph, char *buffer, size_t buffe
ident = ntohs (icmp_hdr->icmp6_id);
seq = ntohs (icmp_hdr->icmp6_seq);
- for (ptr = ph; ptr != NULL; ptr = ptr->next)
+ /* We have to iterate over all hosts, since ICMPv6 packets may
+ * be received on any raw v6 socket. */
+ for (ptr = obj->head; ptr != NULL; ptr = ptr->next)
{
dprintf ("hostname = %s, ident = 0x%04x, seq = %i\n",
ptr->hostname, ptr->ident, ((ptr->sequence - 1) & 0xFFFF));
@@ -405,7 +423,8 @@ static pinghost_t *ping_receive_ipv6 (pinghost_t *ph, char *buffer, size_t buffe
if (((ptr->sequence - 1) & 0xFFFF) != seq)
continue;
- dprintf ("Match found: hostname = %s, ident = 0x%04x, seq = %i\n",
+ dprintf ("Match found: hostname = %s, ident = 0x%04"PRIx16", "
+ "seq = %"PRIu16"\n",
ptr->hostname, ident, seq);
break;
@@ -413,30 +432,57 @@ static pinghost_t *ping_receive_ipv6 (pinghost_t *ph, char *buffer, size_t buffe
if (ptr == NULL)
{
- dprintf ("No match found for ident = 0x%04x, seq = %i\n",
+ dprintf ("No match found for ident = 0x%04"PRIx16", "
+ "seq = %"PRIu16"\n",
ident, seq);
}
return (ptr);
}
-static int ping_receive_one (int fd, pinghost_t *ph, struct timeval *now)
+static int ping_receive_one (pingobj_t *obj, const pinghost_t *ph,
+ struct timeval *now)
{
- char buffer[4096];
- ssize_t buffer_len;
+ /* 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. */
struct timeval diff;
-
pinghost_t *host = NULL;
+ int recv_ttl;
+
+ /*
+ * Set up the receive buffer..
+ */
+ struct msghdr msghdr;
+ struct cmsghdr *cmsg;
+ char payload_buffer[4096];
+ ssize_t payload_buffer_len;
+ char control_buffer[4096];
+ struct iovec payload_iovec;
+
+ memset (&payload_iovec, 0, sizeof (payload_iovec));
+ payload_iovec.iov_base = payload_buffer;
+ payload_iovec.iov_len = sizeof (payload_buffer);
+
+ memset (&msghdr, 0, sizeof (msghdr));
+ /* unspecified source address */
+ msghdr.msg_name = NULL;
+ msghdr.msg_namelen = 0;
+ /* output buffer vector, see readv(2) */
+ msghdr.msg_iov = &payload_iovec;
+ msghdr.msg_iovlen = 1;
+ /* output buffer for control messages */
+ msghdr.msg_control = control_buffer;
+ msghdr.msg_controllen = sizeof (control_buffer);
+ /* flags; this is an output only field.. */
+ msghdr.msg_flags = 0;
+#ifdef MSG_XPG4_2
+ msghdr.msg_flags |= MSG_XPG4_2;
+#endif
- 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 < 0)
+ payload_buffer_len = recvmsg (ph->fd, &msghdr, /* flags = */ 0);
+ if (payload_buffer_len < 0)
{
#if WITH_DEBUG
char errbuf[PING_ERRMSG_LEN];
#endif
return (-1);
}
+ dprintf ("Read %zi bytes from fd = %i\n", payload_buffer_len, ph->fd);
+
+ /* Iterate over all auxiliary data in msghdr */
+ recv_ttl = -1;
+ for (cmsg = CMSG_FIRSTHDR (&msghdr); /* {{{ */
+ cmsg != NULL;
+ cmsg = CMSG_NXTHDR (&msghdr, cmsg))
+ {
+ if (ph->addrfamily == AF_INET) /* {{{ */
+ {
+ if (cmsg->cmsg_level != IPPROTO_IP)
+ continue;
+
+ if (cmsg->cmsg_type == IP_TTL)
+ {
+ memcpy (&recv_ttl, CMSG_DATA (cmsg),
+ sizeof (recv_ttl));
+ dprintf ("TTLv4 = %i;\n", recv_ttl);
+ }
+ else
+ {
+ dprintf ("Not handling option %i.\n",
+ cmsg->cmsg_type);
+ }
+ } /* }}} */
+ else if (ph->addrfamily == AF_INET6) /* {{{ */
+ {
+ if (cmsg->cmsg_level != IPPROTO_IPV6)
+ continue;
- dprintf ("Read %zi bytes from fd = %i\n", buffer_len, fd);
+ if (cmsg->cmsg_type == IPV6_HOPLIMIT)
+ {
+ memcpy (&recv_ttl, CMSG_DATA (cmsg),
+ sizeof (recv_ttl));
+ dprintf ("TTLv6 = %i;\n", recv_ttl);
+ }
+ else
+ {
+ dprintf ("Not handling option %i.\n",
+ cmsg->cmsg_type);
+ }
+ } /* }}} */
+ else
+ {
+ dprintf ("Don't know how to handle "
+ "unknown protocol %i.\n",
+ cmsg->cmsg_level);
+ }
+ } /* }}} for (cmsg) */
- if (sa.ss_family == AF_INET)
+ if (ph->addrfamily == AF_INET)
{
- if ((host = ping_receive_ipv4 (ph, buffer, buffer_len)) == NULL)
+ host = ping_receive_ipv4 (obj, payload_buffer, payload_buffer_len);
+ if (host == NULL)
return (-1);
}
- else if (sa.ss_family == AF_INET6)
+ else if (ph->addrfamily == AF_INET6)
{
- if ((host = ping_receive_ipv6 (ph, buffer, buffer_len)) == NULL)
+ host = ping_receive_ipv6 (obj, payload_buffer, payload_buffer_len);
+ if (host == NULL)
return (-1);
}
+ else
+ {
+ dprintf ("ping_receive_one: Unknown address family %i.\n",
+ ph->addrfamily);
+ return (-1);
+ }
dprintf ("rcvd: %12i.%06i\n",
(int) now->tv_sec,
(int) diff.tv_sec,
(int) diff.tv_usec);
+ if (recv_ttl >= 0)
+ host->recv_ttl = recv_ttl;
+
host->latency = ((double) diff.tv_usec) / 1000.0;
host->latency += ((double) diff.tv_sec) * 1000.0;
static int ping_receive_all (pingobj_t *obj)
{
- fd_set readfds;
- int num_readfds;
- int max_readfds;
+ fd_set read_fds;
+ fd_set err_fds;
+ int num_fds;
+ int max_fd;
pinghost_t *ph;
pinghost_t *ptr;
ret = 0;
for (ptr = ph; ptr != NULL; ptr = ptr->next)
- ptr->latency = -1.0;
+ {
+ ptr->latency = -1.0;
+ ptr->recv_ttl = -1;
+ }
if (gettimeofday (&nowtime, NULL) == -1)
{
while (1)
{
- FD_ZERO (&readfds);
- num_readfds = 0;
- max_readfds = -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, &readfds);
- num_readfds++;
+ FD_SET (ptr->fd, &read_fds);
+ FD_SET (ptr->fd, &err_fds);
+ num_fds++;
- if (max_readfds < ptr->fd)
- max_readfds = ptr->fd;
+ if (max_fd < ptr->fd)
+ max_fd = ptr->fd;
}
- if (num_readfds == 0)
+ if (num_fds == 0)
break;
if (gettimeofday (&nowtime, NULL) == -1)
if (ping_timeval_sub (&endtime, &nowtime, &timeout) == -1)
break;
- dprintf ("Waiting on %i sockets for %i.%06i seconds\n", num_readfds,
+ dprintf ("Waiting on %i sockets for %i.%06i seconds\n", num_fds,
(int) timeout.tv_sec,
(int) timeout.tv_usec);
- status = select (max_readfds + 1, &readfds, NULL, NULL, &timeout);
+ status = select (max_fd + 1, &read_fds, NULL, &err_fds, &timeout);
if (gettimeofday (&nowtime, NULL) == -1)
{
for (ptr = ph; ptr != NULL; ptr = ptr->next)
{
- if (FD_ISSET (ptr->fd, &readfds))
- if (ping_receive_one (ptr->fd, ph, &nowtime) == 0)
+ 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) */
if (ph->addrfamily == AF_INET)
{
- ret = setsockopt (ph->fd, IPPROTO_IP, IP_TTL, &ttl, sizeof (ttl));
+ dprintf ("Setting TTLv4 to %i\n", ttl);
+ 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));
+ dprintf ("Setting TTLv6 to %i\n", ttl);
+ ret = setsockopt (ph->fd, IPPROTO_IPV6, IPV6_UNICAST_HOPS,
+ &ttl, sizeof (ttl));
}
return (ret);
*/
const char *ping_get_error (pingobj_t *obj)
{
+ if (obj == NULL)
+ return (NULL);
return (obj->errmsg);
}
pinghost_t *current;
pinghost_t *next;
+ if (obj == NULL)
+ return;
+
current = obj->head;
next = NULL;
if (obj->srcaddr != NULL)
free (obj->srcaddr);
+ if (obj->device != NULL)
+ free (obj->device);
+
free (obj);
return;
{
int ret = 0;
+ if ((obj == NULL) || (value == NULL))
+ return (-1);
+
switch (option)
{
case PING_OPT_TIMEOUT:
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);
+ }
break;
case PING_OPT_AF:
} /* case PING_OPT_SOURCE */
break;
+ case PING_OPT_DEVICE:
+ {
+#ifdef SO_BINDTODEVICE
+ char *device = strdup ((char *) value);
+
+ if (device == NULL)
+ {
+ ping_set_errno (obj, errno);
+ ret = -1;
+ break;
+ }
+
+ if (obj->device != NULL)
+ free (obj->device);
+ obj->device = device;
+#else /* ! SO_BINDTODEVICE */
+ ping_set_errno (obj, ENOTSUP);
+ ret = -1;
+#endif /* ! SO_BINDTODEVICE */
+ } /* case PING_OPT_DEVICE */
+ break;
+
default:
ret = -2;
} /* switch (option) */
{
int ret;
+ if (obj == NULL)
+ return (-1);
+
if (ping_send_all (obj) < 0)
return (-1);
struct addrinfo *ai_list, *ai_ptr;
int ai_return;
+ if ((obj == NULL) || (host == NULL))
+ return (-1);
+
dprintf ("host = %s\n", host);
if (ping_host_search (obj->head, host) != NULL)
}
}
+#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: %s\n",
+ sstrerror (errno, errbuf, sizeof (errbuf)));
+#endif
+ ping_set_errno (obj, errno);
+ close (ph->fd);
+ ph->fd = -1;
+ continue;
+ }
+ }
+#endif /* SO_BINDTODEVICE */
+
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 = 1;
+
+ setsockopt (ph->fd, IPPROTO_IP, IP_RECVTTL,
+ &opt, sizeof (opt));
+ }
+#if defined(IPPROTO_IPV6) && defined(IPV6_RECVHOPLIMIT)
+ else if (ph->addrfamily == AF_INET6)
+ {
+ int opt = 1;
+
+ setsockopt (ph->fd, IPPROTO_IPV6, IPV6_RECVHOPLIMIT,
+ &opt, sizeof (opt));
+ }
+#endif
+
break;
}
ping_set_ttl (ph, obj->ttl);
return (0);
-}
+} /* int ping_host_add */
int ping_host_remove (pingobj_t *obj, const char *host)
{
pinghost_t *pre, *cur;
+ if ((obj == NULL) || (host == NULL))
+ return (-1);
+
pre = NULL;
cur = obj->head;
pingobj_iter_t *ping_iterator_get (pingobj_t *obj)
{
+ if (obj == NULL)
+ return (NULL);
return ((pingobj_iter_t *) obj->head);
}
pingobj_iter_t *ping_iterator_next (pingobj_iter_t *iter)
{
+ if (iter == NULL)
+ return (NULL);
return ((pingobj_iter_t *) iter->next);
}
size_t orig_buffer_len = *buffer_len;
+ if ((iter == NULL) || (buffer_len == NULL))
+ return (-1);
+
+ if ((buffer == NULL) && (*buffer_len != 0 ))
+ return (-1);
+
switch (info)
{
case PING_INFO_USERNAME:
strncpy ((char *) buffer, iter->data, orig_buffer_len);
ret = 0;
break;
+
+ case PING_INFO_RECV_TTL:
+ ret = ENOMEM;
+ *buffer_len = sizeof (int);
+ if (orig_buffer_len < sizeof (int))
+ break;
+ *((int *) buffer) = iter->recv_ttl;
+ ret = 0;
+ break;
}
return (ret);
void *ping_iterator_get_context (pingobj_iter_t *iter)
{
+ if (iter == NULL)
+ return (NULL);
return (iter->context);
}
void ping_iterator_set_context (pingobj_iter_t *iter, void *context)
{
+ if (iter == NULL)
+ return;
iter->context = context;
}