/** * return 0 on succes, <0 on fail (nothing sent), >0 on sent, but something * failed (do increment sent counter) */ static int sendEcho(int fd, int seq) { int err = 0; void *packet = 0; ssize_t packetlen; if (options.verbose > 2) { fprintf(stderr, "%s: sendEcho(%d, %d)\n", argv0, fd, seq); } if (0 > (packetlen = mkping(seq, &packet))) { err = packetlen; goto errout; } if (options.verbose > 1) { fprintf(stderr, "%s: Sending GTP ping with seq=%d size %d\n", argv0, curSeq, (int)packetlen); } sendTimes[seq % TRACKPINGS_SIZE] = clock_get_dbl(); gotIt[seq % TRACKPINGS_SIZE] = 0; if (packetlen != send(fd, packet, packetlen, 0)) { err = errno; if (err == ECONNREFUSED) { printf("Connection refused\n"); connectionRefused++; goto errout; } fprintf(stderr, "%s: send(%d, ...): %s\n", argv0, fd, strerror(errno)); err = -err; goto errout; } errout: free(packet); return 0; }
/** * 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; }
/** * FIXME: this function needs a cleanup, and probably some merging * with pingMainloop() */ static int tracerouteMainloop(int fd) { int ttl = 0; int ttlTry = 0; double curPingTime; double lastRecvTime = 0; double lastPingTime = 0; int n; int endOfTraceroute = 0; int printStar = 0; double timewait; printf("GTPING traceroute to %s (%s) packet version %d.\n", options.target, options.targetip, (int)options.version); while (!sigintReceived) { struct pollfd fds; fds.fd = fd; fds.events = POLLIN; fds.revents = 0; /* time to send yet? */ curPingTime = clock_get_dbl(); if ((lastRecvTime >= lastPingTime) || (curPingTime > lastPingTime + options.interval)) { if (printStar) { printf("*\n"); } ttlTry++; if (ttlTry < options.traceroutehops && ttl != 0) { printf(" "); } else { if (endOfTraceroute) { break; } ttl++; ttlTry = 0; printf("%4d ", ttl); fflush(stdout); } if (setsockopt(fd, SOL_IP, IP_TTL, &ttl, sizeof(ttl))) { fprintf(stderr, "%s: setsockopt(%d, SOL_IP, IP_TTL, " "%d): %s\n", argv0, fd, ttl, strerror(errno)); } if (0 <= sendEcho(fd, curSeq++)) { lastPingTime = curPingTime; printStar = 1; } } /* max waittime: until it's time to send the next one */ timewait = lastPingTime+options.interval - clock_get_dbl(); if (timewait < 0) { timewait = 0; } timewait *= 0.5; /* leave room for overhead */ switch ((n = poll(&fds, 1, (int)(timewait * 1000)))) { case 1: /* read ready */ printStar = 0; if (fds.revents & POLLERR) { int e; e = handleRecvErr(fd, NULL, lastPingTime); if (e) { lastRecvTime = clock_get_dbl(); } if (e > 1) { endOfTraceroute = 1; } } if (fds.revents & POLLIN) { n = recvEchoReply(fd); endOfTraceroute = 1; if (!n) { lastRecvTime = clock_get_dbl(); } else if (n > 0) { /* still ok, but no reply */ printStar = 1; } else { return 1; } } break; case 0: /* timeout */ break; case -1: /* error */ switch (errno) { case EINTR: case EAGAIN: break; default: fprintf(stderr, "%s: poll([%d], 1, %d): %s\n", argv0, fd, (int)(timewait*1000), strerror(errno)); exit(2); } break; default: /* can't happen */ fprintf(stderr, "%s: poll() returned %d!\n", argv0, n); exit(2); break; } } return 0; }
/** * return value is sent directly to return value of main() */ static int pingMainloop(int fd) { unsigned sent = 0; unsigned recvd = 0; double lastpingTime = 0; /* last time we sent out a ping */ double curPingTime; /* if we ping now, this is the timestamp of it */ double lastRecvTime = 0; /* last time we got a reply */ int recvErrors = 0; if (options.verbose > 2) { fprintf(stderr, "%s: mainloop(%d)\n", argv0, fd); } startTime = clock_get_dbl(); printf("GTPING %s (%s) packet version %d\n", options.target, options.targetip, options.version); lastRecvTime = startTime; while (!sigintReceived) { /* max time to wait for replies before checking if it's time * to send another ping */ double timewait; int n; struct pollfd fds; /* sent all we are going to send, and got all replies * (either errors or good replies) */ if (options.count && (sent == options.count) && (sent == (recvd + recvErrors))) { break; } /* time to send yet? */ curPingTime = clock_get_dbl(); /* if clock is not monotonic and time set backwards * since last ping, start a new ping cycle */ if (curPingTime < lastpingTime) { lastpingTime = curPingTime - options.interval - 0.001; } if (curPingTime > lastpingTime + options.interval) { if (options.count && (curSeq == options.count)) { if (lastRecvTime+options.wait < curPingTime) { break; } } else if (0 <= sendEcho(fd, curSeq++)) { sent++; lastpingTime = curPingTime; if (options.flood) { printf("."); fflush(stdout); } } } fds.fd = fd; fds.events = POLLIN; fds.revents = 0; /* max waittime: until it's time to send the next one */ timewait = lastpingTime+options.interval - clock_get_dbl(); /* never wait more than an interval. this can happen if * clock is not monotonic */ if (timewait > options.interval) { timewait = options.interval; } /* this should never happen, should have been taken care of * above. */ if (timewait < 0) { timewait = 0; } /* leave room for overhead */ timewait *= 0.5; switch ((n = poll(&fds, 1, (int)(timewait * 1000)))) { case 1: /* read ready */ if (fds.revents & POLLERR) { if (handleRecvErr(fd, NULL, 0)) { recvErrors++; } } if (fds.revents & POLLIN) { n = recvEchoReply(fd); if (!n) { recvd++; lastRecvTime = clock_get_dbl(); } else if (n > 0) { /* still ok, but no reply */ } else { /* n < 0 */ return 1; } } break; case 0: /* timeout */ break; case -1: /* error */ switch (errno) { case EINTR: case EAGAIN: break; default: fprintf(stderr, "%s: poll([%d], 1, %d): %s\n", argv0, fd, (int)(timewait*1000), strerror(errno)); exit(2); } break; default: /* can't happen */ fprintf(stderr, "%s: poll() returned %d!\n", argv0, n); exit(2); break; } } printf("\n--- %s GTP ping statistics ---\n" "%u packets transmitted, %u received, " "%d%% packet loss, " "time %dms\n" "%u out of order, %u dups, " "%u connection refused", options.target, sent, recvd, (int)((100.0*(sent-recvd))/sent), (int)(1000*(clock_get_dbl()-startTime)), reorder, dups, connectionRefused); errInspectionPrintSummary(); printf("\n"); if (totalTimeCount) { printf("rtt min/avg/max/mdev = %.3f/%.3f/%.3f/%.3f ms", 1000*totalMin, 1000*(totalTime / totalTimeCount), 1000*totalMax, 1000*sqrt((totalTimeSquared - (totalTime * totalTime) /totalTimeCount)/totalTimeCount)); } printf("\n"); return recvd == 0; }
/** * return: * 0 if no error * 1 if TTL exceeded * >1 if other icmp-like error */ static int handleRecvErrSEE(struct sock_extended_err *see, int returnttl, const char *tos, double lastPingTime) { int isicmp = 0; int ret = 0; if (!see) { fprintf(stderr, "%s: Error, but no error info\n", argv0); return ret; } /* print "From ...: */ if (see->ee_origin == SO_EE_ORIGIN_LOCAL) { printf("From local system: "); } else { struct sockaddr *offender = SO_EE_OFFENDER(see); char abuf[NI_MAXHOST]; int err; if (offender->sa_family == AF_UNSPEC) { if (!options.traceroute) { printf("From "); } printf("<unknown>: "); } else if ((err = getnameinfo(offender, sizeof(struct sockaddr_storage), abuf, NI_MAXHOST, NULL, 0, NI_NUMERICHOST))) { fprintf(stderr, "%s: getnameinfo(): %s\n", argv0, gai_strerror(err)); if (!options.traceroute) { printf("From "); } printf("<unknown>"); if (tos) { printf(" %s", tos); } if (returnttl > 0) { printf(" ttl=%d", returnttl); } printf(": "); } else { if (!options.traceroute) { printf("From "); } printf("%s", abuf); if (tos) { printf(" %s", tos); } if (returnttl > 0) { printf(" ttl=%d", returnttl); } if (lastPingTime) { printf(" time=%.2f ms", 1000*(clock_get_dbl()-lastPingTime)); } printf(": "); } } if (see->ee_origin == SO_EE_ORIGIN_ICMP6 || see->ee_origin == SO_EE_ORIGIN_ICMP) { isicmp = 1; } /* Print error message */ switch (see->ee_errno) { case ECONNREFUSED: printf("Port closed"); ret = 2; break; case EMSGSIZE: printf("PMTU %d", see->ee_info); ret = 2; break; case EPROTO: printf("Protocol error"); ret = 2; break; case ENETUNREACH: printf("Network unreachable"); ret = 2; break; case EACCES: printf("Access denied"); ret = 2; break; case EHOSTUNREACH: if (isicmp && see->ee_type == 11 && see->ee_code == 0) { printf("TTL exceeded"); ret = 1; } else { printf("Host unreachable"); ret = 2; } break; default: printf("%s", strerror(see->ee_errno)); ret = 2; break; } icmpError++; if (options.verbose && (0 < returnttl)) { printf(". return TTL: %d.", returnttl); } printf("\n"); return ret; }