char errmsg[PING_ERRMSG_LEN];
pinghost_t *head;
- pinghost_t *table[PING_TABLE_LEN];
++ pinghost_t *table[PING_TABLE_LEN];
};
/*
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
*/
} /* 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)
{