13cde9171SAlan Somers /*- 24d846d26SWarner Losh * SPDX-License-Identifier: BSD-2-Clause 33cde9171SAlan Somers * 43cde9171SAlan Somers * Copyright (C) 2019 Jan Sucan <jansucan@FreeBSD.org> 53cde9171SAlan Somers * All rights reserved. 63cde9171SAlan Somers * 73cde9171SAlan Somers * Redistribution and use in source and binary forms, with or without 83cde9171SAlan Somers * modification, are permitted provided that the following conditions 93cde9171SAlan Somers * are met: 103cde9171SAlan Somers * 1. Redistributions of source code must retain the above copyright 113cde9171SAlan Somers * notice, this list of conditions and the following disclaimer. 123cde9171SAlan Somers * 2. Redistributions in binary form must reproduce the above copyright 133cde9171SAlan Somers * notice, this list of conditions and the following disclaimer in the 143cde9171SAlan Somers * documentation and/or other materials provided with the distribution. 153cde9171SAlan Somers * 163cde9171SAlan Somers * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 173cde9171SAlan Somers * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 183cde9171SAlan Somers * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 193cde9171SAlan Somers * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 203cde9171SAlan Somers * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 213cde9171SAlan Somers * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 223cde9171SAlan Somers * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 233cde9171SAlan Somers * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 243cde9171SAlan Somers * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 253cde9171SAlan Somers * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 263cde9171SAlan Somers * SUCH DAMAGE. 273cde9171SAlan Somers */ 283cde9171SAlan Somers 293cde9171SAlan Somers #include <sys/cdefs.h> 303cde9171SAlan Somers #include <sys/types.h> 313cde9171SAlan Somers #include <sys/socket.h> 323cde9171SAlan Somers 333cde9171SAlan Somers #include <arpa/inet.h> 343cde9171SAlan Somers #include <netdb.h> 353cde9171SAlan Somers #include <netinet/in.h> 363cde9171SAlan Somers 373cde9171SAlan Somers #include <err.h> 3803d4d1c7SJose Luis Duran #include <math.h> 3903d4d1c7SJose Luis Duran #include <signal.h> 403cde9171SAlan Somers #include <stdbool.h> 413cde9171SAlan Somers #include <stdio.h> 423cde9171SAlan Somers #include <stdlib.h> 433cde9171SAlan Somers #include <string.h> 44*c4ffb80eSDag-Erling Smørgrav #include <sysexits.h> 453cde9171SAlan Somers #include <unistd.h> 463cde9171SAlan Somers 473cde9171SAlan Somers #include "main.h" 48fd26389bSAlan Somers #ifdef INET 493cde9171SAlan Somers #include "ping.h" 50fd26389bSAlan Somers #endif 513cde9171SAlan Somers #ifdef INET6 523cde9171SAlan Somers #include "ping6.h" 533cde9171SAlan Somers #endif 543cde9171SAlan Somers 55fd26389bSAlan Somers #if defined(INET) && defined(INET6) 569ce201f2SAlan Somers #define OPTSTR PING6OPTS PING4OPTS 57fd26389bSAlan Somers #elif defined(INET) 589ce201f2SAlan Somers #define OPTSTR PING4OPTS 59fd26389bSAlan Somers #elif defined(INET6) 609ce201f2SAlan Somers #define OPTSTR PING6OPTS 61a4ef9e58SEd Maste #else 62a4ef9e58SEd Maste #error At least one of INET and INET6 is required 633cde9171SAlan Somers #endif 643cde9171SAlan Somers 6503d4d1c7SJose Luis Duran /* various options */ 6603d4d1c7SJose Luis Duran u_int options; 6703d4d1c7SJose Luis Duran 6803d4d1c7SJose Luis Duran char *hostname; 6903d4d1c7SJose Luis Duran 7003d4d1c7SJose Luis Duran /* counters */ 7103d4d1c7SJose Luis Duran long nreceived; /* # of packets we got back */ 7203d4d1c7SJose Luis Duran long nrepeats; /* number of duplicates */ 7303d4d1c7SJose Luis Duran long ntransmitted; /* sequence # for outbound packets = #sent */ 7403d4d1c7SJose Luis Duran long nrcvtimeout = 0; /* # of packets we got back after waittime */ 7503d4d1c7SJose Luis Duran 7603d4d1c7SJose Luis Duran /* nonzero if we've been told to finish up */ 7703d4d1c7SJose Luis Duran volatile sig_atomic_t seenint; 7803d4d1c7SJose Luis Duran volatile sig_atomic_t seeninfo; 7903d4d1c7SJose Luis Duran 8003d4d1c7SJose Luis Duran /* timing */ 8103d4d1c7SJose Luis Duran int timing; /* flag to do timing */ 8203d4d1c7SJose Luis Duran double tmin = 999999999.0; /* minimum round trip time */ 8303d4d1c7SJose Luis Duran double tmax = 0.0; /* maximum round trip time */ 8403d4d1c7SJose Luis Duran double tsum = 0.0; /* sum of all times, for doing average */ 8503d4d1c7SJose Luis Duran double tsumsq = 0.0; /* sum of all times squared, for std. dev. */ 8603d4d1c7SJose Luis Duran 873cde9171SAlan Somers int 883cde9171SAlan Somers main(int argc, char *argv[]) 893cde9171SAlan Somers { 907fd2c91aSDag-Erling Smørgrav #if defined(INET) 913cde9171SAlan Somers struct in_addr a; 927fd2c91aSDag-Erling Smørgrav #endif 937fd2c91aSDag-Erling Smørgrav #if defined(INET6) 943cde9171SAlan Somers struct in6_addr a6; 95fd26389bSAlan Somers #endif 967fd2c91aSDag-Erling Smørgrav #if defined(INET) && defined(INET6) 977fd2c91aSDag-Erling Smørgrav struct addrinfo hints, *res, *ai; 98*c4ffb80eSDag-Erling Smørgrav const char *target; 997fd2c91aSDag-Erling Smørgrav int error; 100fd26389bSAlan Somers #endif 1017fd2c91aSDag-Erling Smørgrav int opt; 1027fd2c91aSDag-Erling Smørgrav 103fd26389bSAlan Somers #ifdef INET6 104d2624517SAlan Somers if (strcmp(getprogname(), "ping6") == 0) 1057fd2c91aSDag-Erling Smørgrav return ping6(argc, argv); 1063cde9171SAlan Somers #endif 1073cde9171SAlan Somers 1087fd2c91aSDag-Erling Smørgrav while ((opt = getopt(argc, argv, ":" OPTSTR)) != -1) { 1097fd2c91aSDag-Erling Smørgrav switch (opt) { 110fd26389bSAlan Somers #ifdef INET 1113cde9171SAlan Somers case '4': 1127fd2c91aSDag-Erling Smørgrav goto ping4; 113fd26389bSAlan Somers #endif 1143cde9171SAlan Somers #ifdef INET6 1153cde9171SAlan Somers case '6': 1167fd2c91aSDag-Erling Smørgrav goto ping6; 1173cde9171SAlan Somers #endif 1185af718a5SMike Karels case 'S': 1195af718a5SMike Karels /* 1205af718a5SMike Karels * If -S is given with a numeric parameter, 1215af718a5SMike Karels * force use of the corresponding version. 1225af718a5SMike Karels */ 1237fd2c91aSDag-Erling Smørgrav #ifdef INET 1245af718a5SMike Karels if (inet_pton(AF_INET, optarg, &a) == 1) 1257fd2c91aSDag-Erling Smørgrav goto ping4; 1265af718a5SMike Karels #endif 1277fd2c91aSDag-Erling Smørgrav #ifdef INET6 1287fd2c91aSDag-Erling Smørgrav if (inet_pton(AF_INET6, optarg, &a6) == 1) 1297fd2c91aSDag-Erling Smørgrav goto ping6; 1307fd2c91aSDag-Erling Smørgrav #endif 1317fd2c91aSDag-Erling Smørgrav break; 1323cde9171SAlan Somers default: 1333cde9171SAlan Somers break; 1343cde9171SAlan Somers } 1353cde9171SAlan Somers } 1363cde9171SAlan Somers 1377fd2c91aSDag-Erling Smørgrav /* 1387fd2c91aSDag-Erling Smørgrav * For IPv4, only one positional argument, the target, is allowed. 1397fd2c91aSDag-Erling Smørgrav * For IPv6, multiple positional argument are allowed; the last 1407fd2c91aSDag-Erling Smørgrav * one is the target, and preceding ones are intermediate hops. 1417fd2c91aSDag-Erling Smørgrav * This nuance is lost here, but the only case where it matters is 1427fd2c91aSDag-Erling Smørgrav * an error. 1437fd2c91aSDag-Erling Smørgrav */ 1443cde9171SAlan Somers if (optind >= argc) 1453cde9171SAlan Somers usage(); 1463cde9171SAlan Somers 147fd26389bSAlan Somers #if defined(INET) && defined(INET6) 148*c4ffb80eSDag-Erling Smørgrav target = argv[argc - 1]; 1493cde9171SAlan Somers memset(&hints, 0, sizeof(hints)); 1503cde9171SAlan Somers hints.ai_socktype = SOCK_RAW; 1517fd2c91aSDag-Erling Smørgrav if (feature_present("inet") && !feature_present("inet6")) 152fd26389bSAlan Somers hints.ai_family = AF_INET; 1537fd2c91aSDag-Erling Smørgrav if (feature_present("inet6") && !feature_present("inet")) 154fd26389bSAlan Somers hints.ai_family = AF_INET6; 1557fd2c91aSDag-Erling Smørgrav else 1567fd2c91aSDag-Erling Smørgrav hints.ai_family = AF_UNSPEC; 157*c4ffb80eSDag-Erling Smørgrav error = getaddrinfo(target, NULL, &hints, &res); 1587fd2c91aSDag-Erling Smørgrav if (res == NULL) 159*c4ffb80eSDag-Erling Smørgrav errx(EX_NOHOST, "cannot resolve %s: %s", 160*c4ffb80eSDag-Erling Smørgrav target, gai_strerror(error)); 1617fd2c91aSDag-Erling Smørgrav for (ai = res; ai != NULL; ai = ai->ai_next) { 1627fd2c91aSDag-Erling Smørgrav if (ai->ai_family == AF_INET) { 1637fd2c91aSDag-Erling Smørgrav freeaddrinfo(res); 1647fd2c91aSDag-Erling Smørgrav goto ping4; 1657fd2c91aSDag-Erling Smørgrav } 1667fd2c91aSDag-Erling Smørgrav if (ai->ai_family == AF_INET6) { 1677fd2c91aSDag-Erling Smørgrav freeaddrinfo(res); 1687fd2c91aSDag-Erling Smørgrav goto ping6; 1697fd2c91aSDag-Erling Smørgrav } 1707fd2c91aSDag-Erling Smørgrav } 1717fd2c91aSDag-Erling Smørgrav freeaddrinfo(res); 172*c4ffb80eSDag-Erling Smørgrav errx(EX_NOHOST, "cannot resolve %s", target); 1737fd2c91aSDag-Erling Smørgrav #endif 1747fd2c91aSDag-Erling Smørgrav #ifdef INET 1757fd2c91aSDag-Erling Smørgrav ping4: 1767fd2c91aSDag-Erling Smørgrav optreset = 1; 1777fd2c91aSDag-Erling Smørgrav optind = 1; 1787fd2c91aSDag-Erling Smørgrav return ping(argc, argv); 1797fd2c91aSDag-Erling Smørgrav #endif 1807fd2c91aSDag-Erling Smørgrav #ifdef INET6 1817fd2c91aSDag-Erling Smørgrav ping6: 1827fd2c91aSDag-Erling Smørgrav optreset = 1; 1837fd2c91aSDag-Erling Smørgrav optind = 1; 1847fd2c91aSDag-Erling Smørgrav return ping6(argc, argv); 1857fd2c91aSDag-Erling Smørgrav #endif 1863cde9171SAlan Somers } 1873cde9171SAlan Somers 18803d4d1c7SJose Luis Duran /* 18903d4d1c7SJose Luis Duran * onsignal -- 19003d4d1c7SJose Luis Duran * Set the global bit that causes the main loop to quit. 19103d4d1c7SJose Luis Duran */ 19203d4d1c7SJose Luis Duran void 19303d4d1c7SJose Luis Duran onsignal(int sig) 19403d4d1c7SJose Luis Duran { 19503d4d1c7SJose Luis Duran switch (sig) { 19603d4d1c7SJose Luis Duran case SIGALRM: 19703d4d1c7SJose Luis Duran case SIGINT: 19803d4d1c7SJose Luis Duran /* 19903d4d1c7SJose Luis Duran * When doing reverse DNS lookups, the seenint flag might not 20003d4d1c7SJose Luis Duran * be noticed for a while. Just exit if we get a second SIGINT. 20103d4d1c7SJose Luis Duran */ 20203d4d1c7SJose Luis Duran if (!(options & F_HOSTNAME) && seenint != 0) 20303d4d1c7SJose Luis Duran _exit(nreceived ? 0 : 2); 20403d4d1c7SJose Luis Duran seenint++; 20503d4d1c7SJose Luis Duran break; 20603d4d1c7SJose Luis Duran case SIGINFO: 20703d4d1c7SJose Luis Duran seeninfo++; 20803d4d1c7SJose Luis Duran break; 20903d4d1c7SJose Luis Duran } 21003d4d1c7SJose Luis Duran } 21103d4d1c7SJose Luis Duran 21203d4d1c7SJose Luis Duran /* 21303d4d1c7SJose Luis Duran * pr_summary -- 21403d4d1c7SJose Luis Duran * Print out summary statistics to the given output stream. 21503d4d1c7SJose Luis Duran */ 21603d4d1c7SJose Luis Duran void 21703d4d1c7SJose Luis Duran pr_summary(FILE * restrict stream) 21803d4d1c7SJose Luis Duran { 21903d4d1c7SJose Luis Duran fprintf(stream, "\n--- %s ping statistics ---\n", hostname); 22003d4d1c7SJose Luis Duran fprintf(stream, "%ld packets transmitted, ", ntransmitted); 22103d4d1c7SJose Luis Duran fprintf(stream, "%ld packets received, ", nreceived); 22203d4d1c7SJose Luis Duran if (nrepeats) 22303d4d1c7SJose Luis Duran fprintf(stream, "+%ld duplicates, ", nrepeats); 22403d4d1c7SJose Luis Duran if (ntransmitted) { 22503d4d1c7SJose Luis Duran if (nreceived > ntransmitted) 22603d4d1c7SJose Luis Duran fprintf(stream, "-- somebody's duplicating packets!"); 22703d4d1c7SJose Luis Duran else 22803d4d1c7SJose Luis Duran fprintf(stream, "%.1f%% packet loss", 22903d4d1c7SJose Luis Duran ((((double)ntransmitted - nreceived) * 100.0) / 23003d4d1c7SJose Luis Duran ntransmitted)); 23103d4d1c7SJose Luis Duran } 23203d4d1c7SJose Luis Duran if (nrcvtimeout) 23303d4d1c7SJose Luis Duran fprintf(stream, ", %ld packets out of wait time", nrcvtimeout); 23403d4d1c7SJose Luis Duran fputc('\n', stream); 23503d4d1c7SJose Luis Duran if (nreceived && timing) { 23603d4d1c7SJose Luis Duran /* Only display average to microseconds */ 23703d4d1c7SJose Luis Duran double num = nreceived + nrepeats; 23803d4d1c7SJose Luis Duran double avg = tsum / num; 23903d4d1c7SJose Luis Duran double stddev = sqrt(fmax(0, tsumsq / num - avg * avg)); 24003d4d1c7SJose Luis Duran fprintf(stream, 24103d4d1c7SJose Luis Duran "round-trip min/avg/max/stddev = %.3f/%.3f/%.3f/%.3f ms\n", 24203d4d1c7SJose Luis Duran tmin, avg, tmax, stddev); 24303d4d1c7SJose Luis Duran } 24403d4d1c7SJose Luis Duran fflush(stream); 24503d4d1c7SJose Luis Duran } 24603d4d1c7SJose Luis Duran 2473cde9171SAlan Somers void 2483cde9171SAlan Somers usage(void) 2493cde9171SAlan Somers { 2503cde9171SAlan Somers (void)fprintf(stderr, 251fd26389bSAlan Somers "usage:\n" 252fd26389bSAlan Somers #ifdef INET 253fd26389bSAlan Somers "\tping [-4AaDdfHnoQqRrv] [-C pcp] [-c count] " 2543cde9171SAlan Somers "[-G sweepmaxsize]\n" 255526e8a7dSJose Luis Duran "\t [-g sweepminsize] [-h sweepincrsize] [-i wait] " 2563cde9171SAlan Somers "[-l preload]\n" 257526e8a7dSJose Luis Duran "\t [-M mask | time] [-m ttl] " 2583cde9171SAlan Somers #ifdef IPSEC 2593cde9171SAlan Somers "[-P policy] " 2603cde9171SAlan Somers #endif 2613cde9171SAlan Somers "[-p pattern] [-S src_addr] \n" 262526e8a7dSJose Luis Duran "\t [-s packetsize] [-t timeout] [-W waittime] [-z tos] " 2633cde9171SAlan Somers "IPv4-host\n" 264fd26389bSAlan Somers "\tping [-4AaDdfHLnoQqRrv] [-C pcp] [-c count] [-I iface] " 2653cde9171SAlan Somers "[-i wait]\n" 266526e8a7dSJose Luis Duran "\t [-l preload] [-M mask | time] [-m ttl] " 2673cde9171SAlan Somers #ifdef IPSEC 2683cde9171SAlan Somers "[-P policy] " 2693cde9171SAlan Somers #endif 2703cde9171SAlan Somers "[-p pattern]\n" 271526e8a7dSJose Luis Duran "\t [-S src_addr] [-s packetsize] [-T ttl] [-t timeout] [-W waittime]\n" 272526e8a7dSJose Luis Duran "\t [-z tos] IPv4-mcast-group\n" 273fd26389bSAlan Somers #endif /* INET */ 2743cde9171SAlan Somers #ifdef INET6 2759ce201f2SAlan Somers "\tping [-6AaDd" 2763cde9171SAlan Somers #if defined(IPSEC) && !defined(IPSEC_POLICY_IPSEC) 2773cde9171SAlan Somers "E" 2783cde9171SAlan Somers #endif 2793cde9171SAlan Somers "fHnNoOq" 2803cde9171SAlan Somers #ifdef IPV6_USE_MIN_MTU 2813cde9171SAlan Somers "u" 2823cde9171SAlan Somers #endif 2833cde9171SAlan Somers "vyY" 2843cde9171SAlan Somers #if defined(IPSEC) && !defined(IPSEC_POLICY_IPSEC) 2853cde9171SAlan Somers "Z" 2863cde9171SAlan Somers #endif 2873cde9171SAlan Somers "] " 288a2d13601SDag-Erling Smørgrav "[-b bufsiz] [-C pcp] [-c count] [-e gateway]\n" 289526e8a7dSJose Luis Duran "\t [-I interface] [-i wait] [-k addrtype] [-l preload] " 2903cde9171SAlan Somers "[-m hoplimit]\n" 291526e8a7dSJose Luis Duran "\t [-p pattern]" 2923cde9171SAlan Somers #if defined(IPSEC) && defined(IPSEC_POLICY_IPSEC) 2933cde9171SAlan Somers " [-P policy]" 2943cde9171SAlan Somers #endif 2953cde9171SAlan Somers " [-S sourceaddr] [-s packetsize] [-t timeout]\n" 296526e8a7dSJose Luis Duran "\t [-W waittime] [-z tclass] [IPv6-hops ...] IPv6-host\n" 2973cde9171SAlan Somers #endif /* INET6 */ 2983cde9171SAlan Somers ); 2993cde9171SAlan Somers 3003cde9171SAlan Somers exit(1); 3013cde9171SAlan Somers } 302