src/liboping.c: When setting the TTL, update all existing pinghost_t, too.
[liboping.git] / src / liboping.c
index c64060c..4f7d955 100644 (file)
@@ -112,6 +112,7 @@ struct pinghost
        struct timeval          *timer;
        double                   latency;
        uint32_t                 dropped;
+       int                      recv_ttl;
        char                    *data;
 
        void                    *context;
@@ -126,7 +127,7 @@ struct pingobj
        int                      addrfamily;
        char                    *data;
 
-       struct sockaddr_storage *srcaddr;
+       struct sockaddr         *srcaddr;
        socklen_t                srcaddrlen;
 
        char                     errmsg[PING_ERRMSG_LEN];
@@ -187,8 +188,7 @@ static void ping_set_error (pingobj_t *obj, const char *function,
        obj->errmsg[sizeof (obj->errmsg) - 1] = 0;
 }
 
-static void ping_set_errno (pingobj_t *obj, const char *function,
-               int error_number)
+static void ping_set_errno (pingobj_t *obj, int error_number)
 {
        sstrerror (error_number, obj->errmsg, sizeof (obj->errmsg));
 }
@@ -423,21 +423,40 @@ 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;
-
-       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)
+       int family;
+       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;
+
+       payload_buffer_len = recvmsg (fd, &msghdr, /* flags = */ 0);
+       if (payload_buffer_len < 0)
        {
 #if WITH_DEBUG
                char errbuf[PING_ERRMSG_LEN];
@@ -446,19 +465,71 @@ 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 */
+       family = -1;
+       recv_ttl = -1;
+       for (cmsg = CMSG_FIRSTHDR (&msghdr); /* {{{ */
+                       cmsg != NULL;
+                       cmsg = CMSG_NXTHDR (&msghdr, cmsg))
+       {
+               if (cmsg->cmsg_level == IPPROTO_IP) /* {{{ */
+               {
+                       family = AF_INET;
+                       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 (cmsg->cmsg_level == IPPROTO_IPV6) /* {{{ */
+               {
+                       family = AF_INET6;
+                       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) */
 
-       dprintf ("Read %zi bytes from fd = %i\n", buffer_len, fd);
-
-       if (sa.ss_family == AF_INET)
+       if (family == 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 (family == 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",
+                               family);
+               return (-1);
+       }
 
        dprintf ("rcvd: %12i.%06i\n",
                        (int) now->tv_sec,
@@ -480,6 +551,8 @@ static int ping_receive_one (int fd, pinghost_t *ph, struct timeval *now)
        host->latency  = ((double) diff.tv_usec) / 1000.0;
        host->latency += ((double) diff.tv_sec)  * 1000.0;
 
+       host->recv_ttl = recv_ttl;
+
        timerclear (host->timer);
 
        return (0);
@@ -509,7 +582,7 @@ static int ping_receive_all (pingobj_t *obj)
 
        if (gettimeofday (&nowtime, NULL) == -1)
        {
-               ping_set_errno (obj, "gettimeofday", errno);
+               ping_set_errno (obj, errno);
                return (-1);
        }
 
@@ -546,7 +619,7 @@ static int ping_receive_all (pingobj_t *obj)
 
                if (gettimeofday (&nowtime, NULL) == -1)
                {
-                       ping_set_errno (obj, "gettimeofday", errno);
+                       ping_set_errno (obj, errno);
                        return (-1);
                }
 
@@ -561,7 +634,7 @@ static int ping_receive_all (pingobj_t *obj)
 
                if (gettimeofday (&nowtime, NULL) == -1)
                {
-                       ping_set_errno (obj, "gettimeofday", errno);
+                       ping_set_errno (obj, errno);
                        return (-1);
                }
                
@@ -630,7 +703,7 @@ static ssize_t ping_sendto (pingobj_t *obj, pinghost_t *ph,
                if (errno == ENETUNREACH)
                        return (0);
 #endif
-               ping_set_errno (obj, "sendto", errno);
+               ping_set_errno (obj, errno);
        }
 
        return (ret);
@@ -799,11 +872,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);
@@ -964,6 +1041,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:
@@ -1032,16 +1116,16 @@ int ping_setopt (pingobj_t *obj, int option, void *value)
                        if (obj->srcaddr == NULL)
                        {
                                obj->srcaddrlen = 0;
-                               obj->srcaddr = (struct sockaddr_storage *) malloc (sizeof (struct sockaddr_storage));
+                               obj->srcaddr = malloc (sizeof (struct sockaddr_storage));
                                if (obj->srcaddr == NULL)
                                {
-                                       ping_set_errno (obj, "malloc", errno);
+                                       ping_set_errno (obj, errno);
                                        ret = -1;
                                        freeaddrinfo (ai_list);
                                        break;
                                }
                        }
-                       memset ((void *) obj->srcaddr, '\0', sizeof (struct sockaddr_storage));
+                       memset ((void *) obj->srcaddr, 0, sizeof (struct sockaddr_storage));
                        assert (ai_list->ai_addrlen <= sizeof (struct sockaddr_storage));
                        memcpy ((void *) obj->srcaddr, (const void *) ai_list->ai_addr,
                                        ai_list->ai_addrlen);
@@ -1090,9 +1174,6 @@ 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;
@@ -1122,7 +1203,7 @@ int ping_host_add (pingobj_t *obj, const char *host)
        if ((ph->username = strdup (host)) == NULL)
        {
                dprintf ("Out of memory!\n");
-               ping_set_errno (obj, "strdup", errno);
+               ping_set_errno (obj, errno);
                ping_free (ph);
                return (-1);
        }
@@ -1130,7 +1211,7 @@ int ping_host_add (pingobj_t *obj, const char *host)
        if ((ph->hostname = strdup (host)) == NULL)
        {
                dprintf ("Out of memory!\n");
-               ping_set_errno (obj, "strdup", errno);
+               ping_set_errno (obj, errno);
                ping_free (ph);
                return (-1);
        }
@@ -1139,7 +1220,7 @@ int ping_host_add (pingobj_t *obj, const char *host)
        if ((ph->data = strdup (obj->data == NULL ? PING_DEF_DATA : obj->data)) == NULL)
        {
                dprintf ("Out of memory!\n");
-               ping_set_errno (obj, "strdup", errno);
+               ping_set_errno (obj, errno);
                ping_free (ph);
                return (-1);
        }
@@ -1167,30 +1248,13 @@ int ping_host_add (pingobj_t *obj, const char *host)
        {
                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;
                }
@@ -1216,7 +1280,7 @@ int ping_host_add (pingobj_t *obj, const char *host)
                        dprintf ("socket: %s\n",
                                        sstrerror (errno, errbuf, sizeof (errbuf)));
 #endif
-                       ping_set_errno (obj, "socket", errno);
+                       ping_set_errno (obj, errno);
                        continue;
                }
 
@@ -1225,14 +1289,14 @@ int ping_host_add (pingobj_t *obj, const char *host)
                        assert (obj->srcaddrlen > 0);
                        assert (obj->srcaddrlen <= sizeof (struct sockaddr_storage));
 
-                       if (bind (ph->fd, (struct sockaddr *) obj->srcaddr, obj->srcaddrlen) == -1)
+                       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, "bind", errno);
+                               ping_set_errno (obj, errno);
                                close (ph->fd);
                                ph->fd = -1;
                                continue;
@@ -1267,6 +1331,21 @@ 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));
+               }
+               else if (ph->addrfamily == AF_INET6)
+               {
+                       int opt = 1;
+
+                       setsockopt (ph->fd, IPPROTO_IPV6, IPV6_RECVHOPLIMIT,
+                                       &opt, sizeof (opt));
+               }
+
                break;
        }
 
@@ -1302,7 +1381,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)
 {
@@ -1456,6 +1535,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);