X-Git-Url: https://git.tokkee.org/?a=blobdiff_plain;f=src%2Fliboping.c;h=eca5c162d578655dc3cd7e7ea1b1f473d4c80e14;hb=fe85c13e095749191a4dc7bc5e066b1c922702f6;hp=776f24f09e2af9f6b25ee248f759a76514ce3867;hpb=d717d41928fab9354e88d91ef1996151565012d8;p=liboping.git diff --git a/src/liboping.c b/src/liboping.c index 776f24f..eca5c16 100644 --- a/src/liboping.c +++ b/src/liboping.c @@ -1,6 +1,6 @@ /** * Object oriented C module to send ICMP and ICMPv6 `echo's. - * Copyright (C) 2006-2011 Florian octo Forster + * Copyright (C) 2006-2017 Florian octo Forster * * 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 @@ -17,6 +17,10 @@ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ +#ifdef __APPLE__ +#define __APPLE_USE_RFC_3542 +#endif + #if HAVE_CONFIG_H # include #endif @@ -142,10 +146,13 @@ struct pingobj 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 +303,20 @@ static pinghost_t *ping_receive_ipv4 (pingobj_t *obj, char *buffer, 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) { @@ -389,12 +393,12 @@ static pinghost_t *ping_receive_ipv6 (pingobj_t *obj, char *buffer, 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) { @@ -454,7 +458,7 @@ static int ping_receive_one (pingobj_t *obj, struct timeval *now, int addrfam) pinghost_t *host = NULL; int recv_ttl; uint8_t recv_qos; - + /* * Set up the receive buffer.. */ @@ -545,6 +549,7 @@ static int ping_receive_one (pingobj_t *obj, struct timeval *now, int addrfam) 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), @@ -552,6 +557,25 @@ static int ping_receive_one (pingobj_t *obj, struct timeval *now, int addrfam) 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); @@ -655,29 +679,28 @@ 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 = (struct icmp) { + .icmp_type = ICMP_ECHO, + .icmp_id = htons (ph->ident), + .icmp_seq = htons (ph->sequence), + }; - 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); + datalen = strlen (ph->data); + buflen = ICMP_MINLEN + datalen; + if (sizeof (buf) < buflen) + return (EINVAL); - buflen = 4096 - sizeof (struct icmp); - strncpy (data, ph->data, buflen); - datalen = strlen (data); - - buflen = datalen + sizeof (struct icmp); + data = buf + ICMP_MINLEN; + memcpy (data, ph->data, datalen); icmp4->icmp_cksum = ping_icmp4_checksum (buf, buflen); @@ -700,7 +723,7 @@ 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; @@ -708,23 +731,22 @@ static int ping_send_one_ipv6 (pingobj_t *obj, pinghost_t *ph, int fd) 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); @@ -903,7 +925,7 @@ static int ping_get_ident (void) retval = (int) random (); dprintf ("Random number: %#x\n", retval); - + return (retval); } @@ -935,18 +957,19 @@ static pinghost_t *ping_alloc (void) static void ping_free (pinghost_t *ph) { - if (ph->username != NULL) - free (ph->username); - - if (ph->hostname != NULL) - free (ph->hostname); + if (ph == NULL) + return; - if (ph->data != NULL) - free (ph->data); + free (ph->username); + free (ph->hostname); + free (ph->data); free (ph); } +/* ping_open_socket opens, initializes and returns a new raw socket to use for + * ICMPv4 or ICMPv6 packets. addrfam must be either AF_INET or AF_INET6. On + * error, -1 is returned and obj->errmsg is set appropriately. */ static int ping_open_socket(pingobj_t *obj, int addrfam) { int fd; @@ -960,18 +983,28 @@ static int ping_open_socket(pingobj_t *obj, int addrfam) } else /* this should not happen */ { + ping_set_error (obj, "ping_open_socket", "Unknown address family"); dprintf ("Unknown address family: %i\n", addrfam); - return (-1); + return -1; } if (fd == -1) { + ping_set_errno (obj, errno); #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) + { + ping_set_errno (obj, EMFILE); + 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); return -1; } @@ -982,12 +1015,12 @@ static int ping_open_socket(pingobj_t *obj, int addrfam) if (bind (fd, obj->srcaddr, obj->srcaddrlen) == -1) { + ping_set_errno (obj, errno); #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; } @@ -999,34 +1032,47 @@ static int ping_open_socket(pingobj_t *obj, int addrfam) if (setsockopt (fd, SOL_SOCKET, SO_BINDTODEVICE, obj->device, strlen (obj->device) + 1) != 0) { + ping_set_errno (obj, errno); #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)); + int status = setsockopt (fd, SOL_SOCKET, SO_TIMESTAMP, + &(int){1}, sizeof(int)); if (status != 0) { + ping_set_errno (obj, errno); #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; } @@ -1035,35 +1081,27 @@ static int ping_open_socket(pingobj_t *obj, int addrfam) 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)); + setsockopt (fd, IPPROTO_IP, IP_RECVTOS, &(int){1}, sizeof(int)); +#endif /* IP_RECVTOS */ /* Enable receiving the TTL field */ - opt = 1; - setsockopt (fd, IPPROTO_IP, IP_RECVTTL, - &opt, sizeof (opt)); + setsockopt (fd, IPPROTO_IP, IP_RECVTTL, &(int){1}, sizeof(int)); } #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)); + &(int){1}, sizeof(int)); # 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)); + &(int){1}, sizeof(int)); # endif /* IPV6_RECVTCLASS */ } #endif /* IPV6_RECVHOPLIMIT || IPV6_RECVTCLASS */ @@ -1085,9 +1123,9 @@ pingobj_t *ping_construct (void) { pingobj_t *obj; - if ((obj = (pingobj_t *) malloc (sizeof (pingobj_t))) == NULL) + if ((obj = malloc (sizeof (*obj))) == NULL) return (NULL); - memset (obj, 0, sizeof (pingobj_t)); + memset (obj, 0, sizeof (*obj)); obj->timeout = PING_DEF_TIMEOUT; obj->ttl = PING_DEF_TTL; @@ -1103,29 +1141,22 @@ pingobj_t *ping_construct (void) void ping_destroy (pingobj_t *obj) { pinghost_t *current; - pinghost_t *next; if (obj == NULL) return; current = obj->head; - next = NULL; while (current != NULL) { - next = current->next; + pinghost_t *next = current->next; ping_free (current); current = next; } - if (obj->data != NULL) - free (obj->data); - - if (obj->srcaddr != NULL) - free (obj->srcaddr); - - if (obj->device != NULL) - free (obj->device); + free (obj->data); + free (obj->srcaddr); + free (obj->device); if (obj->fd4 != -1) close(obj->fd4); @@ -1285,6 +1316,19 @@ int ping_setopt (pingobj_t *obj, int option, void *value) } /* 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) */ @@ -1294,66 +1338,49 @@ int ping_setopt (pingobj_t *obj, int option, void *value) int ping_send (pingobj_t *obj) { - 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; + _Bool need_ipv4_socket = 0; + _Bool need_ipv6_socket = 0; - int fd4 = obj->fd4; - int fd6 = obj->fd6; - - for (ptr = ph; ptr != NULL; ptr = ptr->next) + for (ptr = obj->head; 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 (ptr->addrfamily == AF_INET) + need_ipv4_socket = 1; + else if (ptr->addrfamily == AF_INET6) + need_ipv6_socket = 1; } - if (fd4 == -1 && fd6 == -1) + if (!need_ipv4_socket && !need_ipv6_socket) { - dprintf("No sockets to use\n"); + ping_set_error (obj, "ping_send", "No hosts to ping"); return (-1); } + if (need_ipv4_socket && obj->fd4 == -1) + { + obj->fd4 = ping_open_socket(obj, AF_INET); + if (obj->fd4 == -1) + return (-1); + ping_set_ttl (obj, obj->ttl); + ping_set_qos (obj, obj->qos); + } + if (need_ipv6_socket && obj->fd6 == -1) + { + obj->fd6 = ping_open_socket(obj, AF_INET6); + if (obj->fd6 == -1) + return (-1); + ping_set_ttl (obj, obj->ttl); + ping_set_qos (obj, obj->qos); + } + if (gettimeofday (&nowtime, NULL) == -1) { ping_set_errno (obj, errno); @@ -1370,27 +1397,58 @@ int ping_send (pingobj_t *obj) 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; + /* host_to_ping points to the host to which to send the next ping. The + * pointer is advanced to the next host in the linked list after the + * ping has been sent. If host_to_ping is NULL, no more pings need to be + * send out. */ + pinghost_t *host_to_ping = obj->head; + + /* pings_in_flight is the number of hosts we sent a "ping" to but didn't + * receive a "pong" yet. */ + int pings_in_flight = 0; - while (pings > 0 || ptr != NULL) + /* pongs_received is the number of echo replies received. Unless there + * is an error, this is used as the return value of ping_send(). */ + int pongs_received = 0; + + int error_count = 0; + + while (pings_in_flight > 0 || host_to_ping != NULL) { + fd_set read_fds; + fd_set write_fds; + + int write_fd = -1; + int max_fd = -1; + 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 (obj->fd4 != -1) + { + FD_SET(obj->fd4, &read_fds); + if (host_to_ping != NULL && host_to_ping->addrfamily == AF_INET) + write_fd = obj->fd4; + + if (max_fd < obj->fd4) + max_fd = obj->fd4; + } - 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 (obj->fd6 != -1) + { + FD_SET(obj->fd6, &read_fds); + if (host_to_ping != NULL && host_to_ping->addrfamily == AF_INET6) + write_fd = obj->fd6; + + if (max_fd < obj->fd6) + max_fd = obj->fd6; + } + + if (write_fd != -1) + FD_SET(write_fd, &write_fds); + + assert (max_fd != -1); + assert (max_fd < FD_SETSIZE); if (gettimeofday (&nowtime, NULL) == -1) { @@ -1401,11 +1459,12 @@ int ping_send (pingobj_t *obj) 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); + dprintf ("Waiting on %i sockets for %u.%06u seconds\n", + ((obj->fd4 != -1) ? 1 : 0) + ((obj->fd6 != -1) ? 1 : 0), + (unsigned) timeout.tv_sec, + (unsigned) timeout.tv_usec); - status = select (max_fd + 1, &read_fds, &write_fds, &err_fds, &timeout); + int status = select (max_fd + 1, &read_fds, &write_fds, NULL, &timeout); if (gettimeofday (&nowtime, NULL) == -1) { @@ -1413,78 +1472,63 @@ int ping_send (pingobj_t *obj) return (-1); } - if ((status == -1) && (errno == EINTR)) - { - dprintf ("select was interrupted by signal..\n"); - continue; - } - else if (status < 0) + if (status == -1) { -#if WITH_DEBUG - char errbuf[PING_ERRMSG_LEN]; - dprintf ("select: %s\n", - sstrerror (errno, errbuf, sizeof (errbuf))); -#endif - break; + ping_set_errno (obj, errno); + dprintf ("select: %s\n", obj->errmsg); + return (-1); } else if (status == 0) { dprintf ("select timed out\n"); - for (ptr = ph; ptr != NULL; ptr = ptr->next) - if (ptr->latency < 0.0) - ptr->dropped++; - + + pinghost_t *ph; + for (ph = obj->head; ph != NULL; ph = ph->next) + if (ph->latency < 0.0) + ph->dropped++; break; } - if (fd4 != -1) + /* first, check if we can receive a reply ... */ + if (obj->fd6 != -1 && FD_ISSET (obj->fd6, &read_fds)) { - if (FD_ISSET (fd4, &read_fds)) + if (ping_receive_one (obj, &nowtime, AF_INET6) == 0) { - if (!ping_receive_one(obj, &nowtime, AF_INET)) - --pings; + pings_in_flight--; + pongs_received++; } - 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; - } - } - + continue; } - - if (fd6 != -1) + if (obj->fd4 != -1 && FD_ISSET (obj->fd4, &read_fds)) { - if (FD_ISSET (fd6, &read_fds)) + if (ping_receive_one (obj, &nowtime, AF_INET) == 0) { - 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; - } + pings_in_flight--; + pongs_received++; } + continue; } + /* ... and if no reply is available to read, continue sending + * out pings. */ + + /* this condition should always be true. We keep it for + * consistency with the read blocks above and just to be on the + * safe side. */ + if (write_fd != -1 && FD_ISSET (write_fd, &write_fds)) + { + if (ping_send_one (obj, host_to_ping, write_fd) == 0) + pings_in_flight++; + else + error_count++; + host_to_ping = host_to_ping->next; + continue; + } } /* while (1) */ - return (ret); + if (error_count) + return (-1 * error_count); + return (pongs_received); } /* int ping_send */ static pinghost_t *ping_host_search (pinghost_t *ph, const char *host) @@ -1628,7 +1672,6 @@ int ping_host_add (pingobj_t *obj, const char *host) } } #endif /* AI_CANONNAME */ - } /* for (ai_ptr = ai_list; ai_ptr != NULL; ai_ptr = ai_ptr->ai_next) */ freeaddrinfo (ai_list); @@ -1689,10 +1732,10 @@ int ping_host_remove (pingobj_t *obj, const char *host) obj->head = cur->next; else pre->next = cur->next; - + target = cur; pre = NULL; - + cur = obj->table[target->ident % PING_TABLE_LEN]; while (cur != NULL) @@ -1735,6 +1778,20 @@ pingobj_iter_t *ping_iterator_next (pingobj_iter_t *iter) return ((pingobj_iter_t *) iter->next); } +int ping_iterator_count (pingobj_t *obj) +{ + if (obj == NULL) + return 0; + + int count = 0; + pingobj_iter_t *iter = obj->head; + while (iter) { + count++; + iter = iter->next; + } + return count; +} + int ping_iterator_get_info (pingobj_iter_t *iter, int info, void *buffer, size_t *buffer_len) {