From cc9cb1487db0e8b93472854e1b0e9de4d4620636 Mon Sep 17 00:00:00 2001 From: Florian Forster Date: Sun, 15 Mar 2009 11:21:21 +0100 Subject: [PATCH] src/liboping.c: Provide the TTL on received packets. Thanks to Vladimir V. Melnikov for the suggestion and initial patch. --- src/liboping.c | 138 ++++++++++++++++++++++++++++++------ src/mans/ping_iterator_get_info.pod | 11 ++- src/oping.h | 19 ++--- 3 files changed, 137 insertions(+), 31 deletions(-) diff --git a/src/liboping.c b/src/liboping.c index bea0be8..0f4c595 100644 --- a/src/liboping.c +++ b/src/liboping.c @@ -112,6 +112,7 @@ struct pinghost struct timeval *timer; double latency; uint32_t dropped; + int recv_ttl; char *data; void *context; @@ -422,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]; @@ -445,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, @@ -479,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); @@ -1246,6 +1320,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; } @@ -1281,7 +1370,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 +1524,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); diff --git a/src/mans/ping_iterator_get_info.pod b/src/mans/ping_iterator_get_info.pod index e3b2006..1d4e8cf 100644 --- a/src/mans/ping_iterator_get_info.pod +++ b/src/mans/ping_iterator_get_info.pod @@ -73,8 +73,15 @@ echo responses being received or not. The buffer should hold an integer. =item B Return the ident that is put into every ICMP packet sent to this host. Per -convention this usually is the PID of the sending process, but since liboping -can handle several hosts in parallel it uses a (pseudo-)random number here. The +convention this usually is the PID of the sending process, but since +I can handle several hosts in parallel it uses a (pseudo-)random +number here. The buffer should be big enough to hold an integer value. + +=item B + +Returns the I