Code

Merge remote-tracking branch 'github/master' into lh/performance
authorFlorian Forster <ff@octo.it>
Fri, 5 May 2017 06:43:52 +0000 (08:43 +0200)
committerFlorian Forster <ff@octo.it>
Fri, 5 May 2017 08:46:44 +0000 (10:46 +0200)
1  2 
src/liboping.c

diff --combined src/liboping.c
index 776f24f09e2af9f6b25ee248f759a76514ce3867,fc9ed65c63198f081a9e5ecb2b7dc2ab474177a9..31b88135cdc090827627325ddb52545572f66905
@@@ -1,6 -1,6 +1,6 @@@
  /**
   * Object oriented C module to send ICMP and ICMPv6 `echo's.
-  * Copyright (C) 2006-2011  Florian octo Forster <ff at octo.it>
+  * Copyright (C) 2006-2016  Florian octo Forster <ff at octo.it>
   *
   * This library is free software; you can redistribute it and/or modify it
   * under the terms of the GNU Lesser General Public License as published by the
   * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
   */
  
+ #ifdef __APPLE__
+ #define __APPLE_USE_RFC_3542
+ #endif
  #if HAVE_CONFIG_H
  # include <config.h>
  #endif
  #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                    *device;
  
+       char                    set_mark;
+       int                     mark;
        char                     errmsg[PING_ERRMSG_LEN];
  
        pinghost_t              *head;
-       pinghost_t              *table[PING_TABLE_LEN];
++      pinghost_t              *table[PING_TABLE_LEN];
  };
  
  /*
@@@ -296,23 -298,20 +303,20 @@@ static pinghost_t *ping_receive_ipv4 (p
        buffer     += ip_hdr_len;
        buffer_len -= ip_hdr_len;
  
-       if (buffer_len < sizeof (struct icmp))
+       if (buffer_len < ICMP_MINLEN)
                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);
+               dprintf ("Unexpected ICMP type: %"PRIu8"\n", icmp_hdr->icmp_type);
                return (NULL);
        }
  
        recv_checksum = icmp_hdr->icmp_cksum;
+       /* This writes to buffer. */
        icmp_hdr->icmp_cksum = 0;
-       calc_checksum = ping_icmp4_checksum ((char *) icmp_hdr,
-                       sizeof (struct icmp) + buffer_len);
+       calc_checksum = ping_icmp4_checksum (buffer, buffer_len);
  
        if (recv_checksum != calc_checksum)
        {
        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));
@@@ -389,12 -389,12 +393,12 @@@ static pinghost_t *ping_receive_ipv6 (p
  
        pinghost_t *ptr;
  
-       if (buffer_len < sizeof (struct icmp6_hdr))
+       if (buffer_len < ICMP_MINLEN)
                return (NULL);
  
        icmp_hdr = (struct icmp6_hdr *) buffer;
-       buffer     += sizeof (struct icmp);
-       buffer_len -= sizeof (struct icmp);
+       buffer     += ICMP_MINLEN;
+       buffer_len -= ICMP_MINLEN;
  
        if (icmp_hdr->icmp6_type != ICMP6_ECHO_REPLY)
        {
        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;
                                                sizeof (recv_qos));
                                dprintf ("TOSv6 = 0x%02"PRIx8";\n", recv_qos);
                        } else
+ #ifdef IPV6_HOPLIMIT
                        if (cmsg->cmsg_type == IPV6_HOPLIMIT)
                        {
                                memcpy (&recv_ttl, CMSG_DATA (cmsg),
                                dprintf ("TTLv6 = %i;\n", recv_ttl);
                        }
                        else
+ #endif
+ #ifdef IPV6_UNICAST_HOPS
+                       if (cmsg->cmsg_type == IPV6_UNICAST_HOPS)
+                       {
+                               memcpy (&recv_ttl, CMSG_DATA (cmsg),
+                                               sizeof (recv_ttl));
+                               dprintf ("TTLv6 = %i;\n", recv_ttl);
+                       }
+                       else
+ #endif
+ #ifdef IPV6_MULTICAST_HOPS
+                       if (cmsg->cmsg_type == IPV6_MULTICAST_HOPS)
+                       {
+                               memcpy (&recv_ttl, CMSG_DATA (cmsg),
+                                               sizeof (recv_ttl));
+                               dprintf ("TTLv6 = %i;\n", recv_ttl);
+                       }
+                       else
+ #endif
                        {
                                dprintf ("Not handling option %i.\n",
                                                cmsg->cmsg_type);
                }
        } /* }}} 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);
  }
  
 -/* Blocks until a packet was received from all hosts or the timeout is reached.
 - * When interrupted, (-EINTR) is returned. On error, -1 is returned. On
 - * success, returns zero. */
 -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;
 -
 -                      assert (ptr->fd < FD_SETSIZE);
 -                      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");
 -                      ping_set_errno (obj, EINTR);
 -                      return (-EINTR);
 -              }
 -              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);
 -} /* int ping_receive_all */
 -
  /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
   * 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;
  
-       char buf[4096];
-       int  buflen;
+       char   buf[4096] = {0};
+       size_t buflen;
  
        char *data;
-       int   datalen;
+       size_t 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);
+       *icmp4 = (struct icmp) {
+               .icmp_type = ICMP_ECHO,
+               .icmp_id   = htons (ph->ident),
+               .icmp_seq  = htons (ph->sequence),
+       };
  
-       buflen = 4096 - sizeof (struct icmp);
-       strncpy (data, ph->data, buflen);
-       datalen = strlen (data);
+       datalen = strlen (ph->data);
+       buflen = ICMP_MINLEN + datalen;
+       if (sizeof (buf) < buflen)
+               return (EINVAL);
  
-       buflen = datalen + sizeof (struct icmp);
+       data  = buf + ICMP_MINLEN;
+       memcpy (data, ph->data, datalen);
  
        icmp4->icmp_cksum = ping_icmp4_checksum (buf, buflen);
  
        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;
  
-       char buf[4096];
+       char buf[4096] = {0};
        int  buflen;
  
        char *data;
  
        dprintf ("ph->hostname = %s\n", ph->hostname);
  
-       memset (buf, '\0', sizeof (buf));
        icmp6 = (struct icmp6_hdr *) buf;
-       data  = (char *) (icmp6 + 1);
+       *icmp6 = (struct icmp6_hdr) {
+               .icmp6_type  = ICMP6_ECHO_REQUEST,
+               .icmp6_id    = htons (ph->ident),
+               .icmp6_seq   = htons (ph->sequence),
+       };
  
-       icmp6->icmp6_type  = ICMP6_ECHO_REQUEST;
-       icmp6->icmp6_code  = 0;
-       /* The checksum will be calculated by the TCP/IP stack.  */
-       /* FIXME */
-       icmp6->icmp6_cksum = 0;
-       icmp6->icmp6_id    = htons (ph->ident);
-       icmp6->icmp6_seq   = htons (ph->sequence);
+       datalen = strlen (ph->data);
+       buflen = sizeof (*icmp6) + datalen;
+       if (sizeof (buf) < buflen)
+               return (EINVAL);
  
-       buflen = 4096 - sizeof (struct icmp6_hdr);
-       strncpy (data, ph->data, buflen);
-       datalen = strlen (data);
+       data  = buf + ICMP_MINLEN;
+       memcpy (data, ph->data, datalen);
  
-       buflen = datalen + sizeof (struct icmp6_hdr);
+       /* The checksum will be calculated by the TCP/IP stack. */
  
        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",
@@@ -926,6 -1085,7 +948,6 @@@ static pinghost_t *ping_alloc (void
        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;
 +      }
++      else if (fd >= FD_SETSIZE)
++      {
++              dprintf ("socket(2) returned file descriptor %d, which is above the file "
++                       "descriptor limit for select(2) (FD_SETSIZE = %d)\n",
++                       fd, FD_SETSIZE);
++              close (fd);
++              ping_set_errno (obj, EMFILE);
++              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_MARK
++      if (obj->set_mark)
++      {
++              if (setsockopt(fd, SOL_SOCKET, SO_MARK,
++                              &obj->mark, sizeof(obj->mark)) != 0)
++              {
++                      ping_set_errno (obj, errno);
++#if WITH_DEBUG
++                      char errbuf[PING_ERRMSG_LEN];
++                      dprintf ("setsockopt (SO_MARK): %s\n",
++                               sstrerror (errno, errbuf, sizeof (errbuf)));
++#endif
++                      close (fd);
++                      return -1;
++              }
++      }
++#endif
 +#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;
 +
++#ifdef IP_RECVTOS
 +              /* Enable receiving the TOS field */
 +              opt = 1;
 +              setsockopt (fd, IPPROTO_IP, IP_RECVTOS,
 +                              &opt, sizeof (opt));
++#endif /* IP_RECVTOS */
 +
 +              /* 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
   */
@@@ -1094,8 -1133,6 +1144,8 @@@ pingobj_t *ping_construct (void
        obj->addrfamily = PING_DEF_AF;
        obj->data       = strdup (PING_DEF_DATA);
        obj->qos        = 0;
 +      obj->fd4        = -1;
 +      obj->fd6        = -1;
  
        return (obj);
  }
@@@ -1127,12 -1164,6 +1177,12 @@@ void ping_destroy (pingobj_t *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;
@@@ -1149,8 -1180,11 +1199,8 @@@ int ping_setopt (pingobj_t *obj, int op
        {
                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;
  
                } /* case PING_OPT_DEVICE */
                break;
  
 -                      
+               case PING_OPT_MARK:
+               {
+ #ifdef SO_MARK
+                       obj->mark     = *(int*)(value);
+                       obj->set_mark = 1;
+ #else /* SO_MARK */
+                       ping_set_errno (obj, ENOTSUP);
+                       ret = -1;
+ #endif /* !SO_MARK */
++
+               } /* case PING_OPT_MARK */
+               break;
                default:
                        ret = -2;
        } /* switch (option) */
        return (ret);
  } /* int ping_setopt */
  
 -
  int ping_send (pingobj_t *obj)
  {
 -      if (obj == NULL)
 +      fd_set read_fds;
 +      fd_set write_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 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);
 +      }
  
 -      return (ping_receive_all (obj));
 -}
 +      /* 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;
++      assert (max_fd < FD_SETSIZE);
 +
 +      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);
 +
 +              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;
++                      ping_set_errno (obj, EINTR);
++                      return (-1);
 +              }
 +              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 && 
++                      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)
  {
@@@ -1579,6 -1445,8 +1644,6 @@@ int ping_host_add (pingobj_t *obj, cons
  
        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;
 -              }
 -              else if (ph->fd >= FD_SETSIZE)
 -              {
 -                      dprintf("socket(2) returned file descriptor %d, which is above the file "
 -                              "descriptor limit for select(2) (FD_SETSIZE = %d)\n",
 -                              ph->fd, FD_SETSIZE);
 -                      close(ph->fd);
 -                      ph->fd = -1;
 -                      ping_set_errno(obj, EMFILE);
 -                      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_MARK
 -              if(obj->set_mark)
 -              {
 -                      if(setsockopt(ph->fd, SOL_SOCKET, SO_MARK, &(obj->mark), sizeof(obj->mark)) != 0)
 -                      {
 -#if WITH_DEBUG
 -                              char errbuf[PING_ERRMSG_LEN];
 -                              dprintf ("setsockopt (SO_MARK): %s\n",
 -                                              sstrerror (errno, errbuf, sizeof (errbuf)));
 -#endif
 -                              ping_set_errno (obj, errno);
 -                              close (ph->fd);
 -                              ph->fd = -1;
 -                              continue;
 -                      }
 -              }
 -#endif
 -#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;
 -
 -#ifdef IP_RECVTOS
 -                      /* Enable receiving the TOS field */
 -                      opt = 1;
 -                      setsockopt (ph->fd, IPPROTO_IP, IP_RECVTOS,
 -                                      &opt, sizeof (opt));
 -#endif        /* IP_RECVTOS */
 -
 -                      /* 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);