src/liboping.c: Do not reset recv_ttl in ping_receive_one().
[liboping.git] / src / liboping.c
index bea0be8..2ccbf4d 100644 (file)
@@ -25,7 +25,6 @@
 # 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
@@ -61,6 +64,7 @@
 #if HAVE_SYS_SOCKET_H
 # include <sys/socket.h>
 #endif
+
 #if HAVE_NETDB_H
 # include <netdb.h>
 #endif
@@ -112,6 +116,7 @@ struct pinghost
        struct timeval          *timer;
        double                   latency;
        uint32_t                 dropped;
+       int                      recv_ttl;
        char                    *data;
 
        void                    *context;
@@ -140,7 +145,7 @@ struct pingobj
 /* 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;
 
@@ -338,6 +343,9 @@ static pinghost_t *ping_receive_ipv4 (pinghost_t *ph, char *buffer, size_t buffe
                                ident, seq);
        }
 
+       if (ptr != NULL)
+               ptr->recv_ttl = ip_hdr->ip_ttl;
+
        return (ptr);
 }
 
@@ -422,21 +430,42 @@ static pinghost_t *ping_receive_ipv6 (pinghost_t *ph, char *buffer, size_t buffe
 
 static int ping_receive_one (int fd, pinghost_t *ph, struct timeval *now)
 {
-       char   buffer[4096];
-       ssize_t buffer_len;
-
        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 (fd, &msghdr, /* flags = */ 0);
+       if (payload_buffer_len < 0)
        {
 #if WITH_DEBUG
                char errbuf[PING_ERRMSG_LEN];
@@ -445,19 +474,74 @@ static int ping_receive_one (int fd, pinghost_t *ph, struct timeval *now)
 #endif
                return (-1);
        }
+       dprintf ("Read %zi bytes from fd = %i\n", payload_buffer_len, 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;
 
-       dprintf ("Read %zi bytes from fd = %i\n", buffer_len, fd);
+                       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;
 
-       if (sa.ss_family == AF_INET)
+                       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 (ph->addrfamily == AF_INET)
        {
-               if ((host = ping_receive_ipv4 (ph, buffer, buffer_len)) == NULL)
+               host = ping_receive_ipv4 (ph, 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 (ph, 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,
@@ -476,6 +560,9 @@ static int ping_receive_one (int fd, pinghost_t *ph, struct timeval *now)
                        (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;
 
@@ -504,7 +591,10 @@ static int ping_receive_all (pingobj_t *obj)
        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)
        {
@@ -798,11 +888,15 @@ static int ping_set_ttl (pinghost_t *ph, int ttl)
 
        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);
@@ -963,6 +1057,13 @@ int ping_setopt (pingobj_t *obj, int option, void *value)
                                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:
@@ -1246,6 +1347,23 @@ int ping_host_add (pingobj_t *obj, const char *host)
                }
 #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;
        }
 
@@ -1281,7 +1399,7 @@ int ping_host_add (pingobj_t *obj, const char *host)
        ping_set_ttl (ph, obj->ttl);
 
        return (0);
-}
+} /* int ping_host_add */
 
 int ping_host_remove (pingobj_t *obj, const char *host)
 {
@@ -1435,6 +1553,15 @@ int ping_iterator_get_info (pingobj_iter_t *iter, int info,
                        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);