/** * return 0 on success/got reply, * <0 on fail. Errno returned. * >0 on success, but no packet (EINTR or dup packet) */ static int recvEchoReply(int fd) { int err; char packet[1024]; ssize_t packetlen; double now; char lag[128]; int isDup = 0; int isReorder = 0; int ttl; int tos; char tosString[128] = {0}; char ttlString[128] = {0}; struct GtpReply gtp; if (options.verbose > 2) { fprintf(stderr, "%s: recvEchoReply()\n", argv0); } now = clock_get_dbl(); memset(packet, 0, sizeof(packet)); if (0 > (packetlen = doRecv(fd, (void*)packet, sizeof(packet), &ttl, &tos))) { switch(errno) { case ECONNREFUSED: connectionRefused++; handleRecvErr(fd, "Port closed", 0); return 1; case EINTR: return 1; case EHOSTUNREACH: handleRecvErr(fd, "Host unreachable or TTL exceeded", 0); return 1; default: err = errno; fprintf(stderr, "%s: recv(%d, ...): %s\n", argv0, fd, strerror(errno)); return err; } } /* create ttl string */ if (0 <= ttl) { snprintf(ttlString, sizeof(ttlString), "ttl=%d ", ttl); } /* create tos string */ if (0 <= tos) { char scratch[128]; snprintf(tosString, sizeof(tosString), "%s ", tos2String(tos, scratch, sizeof(scratch))); } gtp = parseReply(packet, packetlen); if (!gtp.ok) { return 1; } if (gtp.msg != GTPMSG_ECHOREPLY) { fprintf(stderr, "%s: Got non-EchoReply type of msg (type: %d)\n", argv0, gtp.msg); return 1; } if (curSeq - gtp.seq >= TRACKPINGS_SIZE) { strcpy(lag, "Inf"); } else { int pos = gtp.seq % TRACKPINGS_SIZE; double lagf = now - sendTimes[pos]; if (gotIt[pos]) { isDup = 1; } gotIt[pos]++; snprintf(lag, sizeof(lag), "%.2f ms", 1000 * lagf); if (!isDup) { totalTime += lagf; totalTimeSquared += lagf * lagf; totalTimeCount++; if ((0 > totalMin) || (lagf < totalMin)) { totalMin = lagf; } if ((0 > totalMax) || (lagf > totalMax)) { totalMax = lagf; } } if (options.autowait) { options.wait = 2 * (totalTime / totalTimeCount); if (options.verbose > 1) { fprintf(stderr, "%s: Adjusting waittime to %.6f\n", argv0, options.wait); } } } /* detect packet reordering */ if (!isDup) { if (highestSeq > gtp.seq) { reorder++; isReorder = 1; } else { highestSeq = gtp.seq; } } if (options.flood) { if (!isDup) { printf("\b \b"); } } else { printf("%u bytes from %s: ver=%d seq=%u %s%stime=%s%s%s\n", (int)packetlen, options.targetip, gtp.version, gtp.seq, tosString[0] ? tosString : "", ttlString[0] ? ttlString : "", lag, isDup ? " (DUP)" : "", isReorder ? " (out of order)" : ""); } if (isDup) { dups++; } return isDup; }
/** * return: * 0 if no error * 1 if TTL exceeded * >1 if other icmp-like error */ int handleRecvErr(int fd, const char *reason, double lastPingTime) { struct msghdr msg; struct cmsghdr *cmsg; char cbuf[512]; char buf[5120]; struct sockaddr_storage sa; struct iovec iov; int n; int returnttl = -1; char *tos = 0; int ret = 0; /* ignore reason, we know better */ reason = reason; /* get error data */ iov.iov_base = buf; iov.iov_len = sizeof(buf); memset(&msg, 0, sizeof(msg)); msg.msg_name = (char*)&sa; msg.msg_namelen = sizeof(sa); msg.msg_iov = &iov; msg.msg_iovlen = 1; msg.msg_control = cbuf; msg.msg_controllen = sizeof(cbuf); if (0 > (n = recvmsg(fd, &msg, MSG_ERRQUEUE))) { if (errno == EAGAIN) { goto errout; } fprintf(stderr, "%s: recvmsg(%d, ..., MSG_ERRQUEUE): %s\n", argv0, fd, strerror(errno)); goto errout; } /* First find ttl */ for (cmsg = CMSG_FIRSTHDR(&msg); cmsg; cmsg = CMSG_NXTHDR(&msg, cmsg)) { if ((cmsg->cmsg_level == SOL_IP || cmsg->cmsg_level == SOL_IPV6) && (cmsg->cmsg_type == IP_TTL || cmsg->cmsg_type == IPV6_HOPLIMIT || cmsg->cmsg_type == REAL_IPV6_HOPLIMIT )) { returnttl = *(int*)CMSG_DATA(cmsg); } } for (cmsg = CMSG_FIRSTHDR(&msg); cmsg; cmsg = CMSG_NXTHDR(&msg, cmsg)) { if (cmsg->cmsg_level == SOL_IP || cmsg->cmsg_level == SOL_IPV6) { switch(cmsg->cmsg_type) { case IP_TOS: #ifdef IPV6_TCLASS case IPV6_TCLASS: #endif { char scratch[128]; free(tos); if (!(tos = malloc(128))) { fprintf(stderr, "%s: " "malloc(128): %s\n", argv0, strerror(errno)); goto errout; } snprintf(tos, 128, "%s", tos2String(*(unsigned char*) CMSG_DATA(cmsg), scratch, sizeof(scratch))); break; } case IP_RECVERR: case IPV6_RECVERR: ret = handleRecvErrSEE((struct sock_extended_err*) CMSG_DATA(cmsg), returnttl, tos, lastPingTime); break; case IP_TTL: #if IPV6_HOPLIMIT != REAL_IPV6_HOPLIMIT case REAL_IPV6_HOPLIMIT: #endif case IPV6_HOPLIMIT: /* ignore */ break; default: fprintf(stderr, "%s: Got cmsg type: %d", argv0, cmsg->cmsg_type); if (0 < returnttl) { fprintf(stderr, ". Return TTL: %d", returnttl); } printf("\n"); } } } errout:; free(tos); return ret; }