xref: /freebsd/sbin/ping/main.c (revision e5b786625f7f82a1fa91e41823332459ea5550f9)
1 /*-
2  * SPDX-License-Identifier: BSD-2-Clause
3  *
4  * Copyright (C) 2019 Jan Sucan <jansucan@FreeBSD.org>
5  * All rights reserved.
6  *
7  * Redistribution and use in source and binary forms, with or without
8  * modification, are permitted provided that the following conditions
9  * are met:
10  * 1. Redistributions of source code must retain the above copyright
11  *    notice, this list of conditions and the following disclaimer.
12  * 2. Redistributions in binary form must reproduce the above copyright
13  *    notice, this list of conditions and the following disclaimer in the
14  *    documentation and/or other materials provided with the distribution.
15  *
16  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
17  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
18  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
19  * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
20  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
21  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
22  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
23  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
24  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
25  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
26  * SUCH DAMAGE.
27  */
28 
29 #include <sys/cdefs.h>
30 #include <sys/types.h>
31 #include <sys/socket.h>
32 
33 #include <arpa/inet.h>
34 #include <netdb.h>
35 #include <netinet/in.h>
36 
37 #include <err.h>
38 #include <math.h>
39 #include <signal.h>
40 #include <stdbool.h>
41 #include <stdio.h>
42 #include <stdlib.h>
43 #include <string.h>
44 #include <sysexits.h>
45 #include <unistd.h>
46 
47 #include "main.h"
48 #ifdef INET
49 #include "ping.h"
50 #endif
51 #ifdef INET6
52 #include "ping6.h"
53 #endif
54 
55 #if defined(INET) && defined(INET6)
56 #define	OPTSTR PING6OPTS PING4OPTS
57 #elif defined(INET)
58 #define	OPTSTR PING4OPTS
59 #elif defined(INET6)
60 #define	OPTSTR PING6OPTS
61 #else
62 #error At least one of INET and INET6 is required
63 #endif
64 
65 /* various options */
66 u_int options;
67 
68 char *hostname;
69 
70 /* counters */
71 long nreceived;		/* # of packets we got back */
72 long nrepeats;		/* number of duplicates */
73 long ntransmitted;	/* sequence # for outbound packets = #sent */
74 long nrcvtimeout = 0;	/* # of packets we got back after waittime */
75 
76 /* nonzero if we've been told to finish up */
77 volatile sig_atomic_t seenint;
78 volatile sig_atomic_t seeninfo;
79 
80 /* timing */
81 int timing;		/* flag to do timing */
82 double tmin = 999999999.0;	/* minimum round trip time */
83 double tmax = 0.0;	/* maximum round trip time */
84 double tsum = 0.0;	/* sum of all times, for doing average */
85 double tsumsq = 0.0;	/* sum of all times squared, for std. dev. */
86 
87 int
88 main(int argc, char *argv[])
89 {
90 #if defined(INET)
91 	struct in_addr a;
92 #endif
93 #if defined(INET6)
94 	struct in6_addr a6;
95 #endif
96 #if defined(INET) && defined(INET6)
97 	struct addrinfo hints, *res, *ai;
98 	const char *target;
99 	int error;
100 #endif
101 	int opt;
102 
103 #ifdef INET6
104 	if (strcmp(getprogname(), "ping6") == 0)
105 		return ping6(argc, argv);
106 #endif
107 
108 	while ((opt = getopt(argc, argv, ":" OPTSTR)) != -1) {
109 		switch (opt) {
110 #ifdef INET
111 		case '4':
112 			goto ping4;
113 #endif
114 #ifdef INET6
115 		case '6':
116 			goto ping6;
117 #endif
118 		case 'S':
119 			/*
120 			 * If -S is given with a numeric parameter,
121 			 * force use of the corresponding version.
122 			 */
123 #ifdef INET
124 			if (inet_pton(AF_INET, optarg, &a) == 1)
125 				goto ping4;
126 #endif
127 #ifdef INET6
128 			if (inet_pton(AF_INET6, optarg, &a6) == 1)
129 				goto ping6;
130 #endif
131 			break;
132 		default:
133 			break;
134 		}
135 	}
136 
137 	/*
138 	 * For IPv4, only one positional argument, the target, is allowed.
139 	 * For IPv6, multiple positional argument are allowed; the last
140 	 * one is the target, and preceding ones are intermediate hops.
141 	 * This nuance is lost here, but the only case where it matters is
142 	 * an error.
143 	 */
144 	if (optind >= argc)
145 		usage();
146 
147 #if defined(INET) && defined(INET6)
148 	target = argv[argc - 1];
149 	memset(&hints, 0, sizeof(hints));
150 	hints.ai_socktype = SOCK_RAW;
151 	if (feature_present("inet") && !feature_present("inet6"))
152 		hints.ai_family = AF_INET;
153 	if (feature_present("inet6") && !feature_present("inet"))
154 		hints.ai_family = AF_INET6;
155 	else
156 		hints.ai_family = AF_UNSPEC;
157 	error = getaddrinfo(target, NULL, &hints, &res);
158 	if (res == NULL)
159 		errx(EX_NOHOST, "cannot resolve %s: %s",
160 		    target, gai_strerror(error));
161 	for (ai = res; ai != NULL; ai = ai->ai_next) {
162 		if (ai->ai_family == AF_INET) {
163 			freeaddrinfo(res);
164 			goto ping4;
165 		}
166 		if (ai->ai_family == AF_INET6) {
167 			freeaddrinfo(res);
168 			goto ping6;
169 		}
170 	}
171 	freeaddrinfo(res);
172 	errx(EX_NOHOST, "cannot resolve %s", target);
173 #endif
174 #ifdef INET
175 ping4:
176 	optreset = 1;
177 	optind = 1;
178 	return ping(argc, argv);
179 #endif
180 #ifdef INET6
181 ping6:
182 	optreset = 1;
183 	optind = 1;
184 	return ping6(argc, argv);
185 #endif
186 }
187 
188 /*
189  * onsignal --
190  *	Set the global bit that causes the main loop to quit.
191  */
192 void
193 onsignal(int sig)
194 {
195 	switch (sig) {
196 	case SIGALRM:
197 	case SIGINT:
198 		/*
199 		 * When doing reverse DNS lookups, the seenint flag might not
200 		 * be noticed for a while.  Just exit if we get a second SIGINT.
201 		 */
202 		if (!(options & F_HOSTNAME) && seenint != 0)
203 			_exit(nreceived ? 0 : 2);
204 		seenint++;
205 		break;
206 	case SIGINFO:
207 		seeninfo++;
208 		break;
209 	}
210 }
211 
212 /*
213  * pr_summary --
214  *	Print out summary statistics to the given output stream.
215  */
216 void
217 pr_summary(FILE * restrict stream)
218 {
219 	fprintf(stream, "\n--- %s ping statistics ---\n", hostname);
220 	fprintf(stream, "%ld packets transmitted, ", ntransmitted);
221 	fprintf(stream, "%ld packets received, ", nreceived);
222 	if (nrepeats)
223 		fprintf(stream, "+%ld duplicates, ", nrepeats);
224 	if (ntransmitted) {
225 		if (nreceived > ntransmitted)
226 			fprintf(stream, "-- somebody's duplicating packets!");
227 		else
228 			fprintf(stream, "%.1f%% packet loss",
229 			    ((((double)ntransmitted - nreceived) * 100.0) /
230 			    ntransmitted));
231 	}
232 	if (nrcvtimeout)
233 		fprintf(stream, ", %ld packets out of wait time", nrcvtimeout);
234 	fputc('\n', stream);
235 	if (nreceived && timing) {
236 		/* Only display average to microseconds */
237 		double num = nreceived + nrepeats;
238 		double avg = tsum / num;
239 		double stddev = sqrt(fmax(0, tsumsq / num - avg * avg));
240 		fprintf(stream,
241 		    "round-trip min/avg/max/stddev = %.3f/%.3f/%.3f/%.3f ms\n",
242 		    tmin, avg, tmax, stddev);
243 	}
244 	fflush(stream);
245 }
246 
247 void
248 usage(void)
249 {
250 	(void)fprintf(stderr,
251 	    "usage:\n"
252 #ifdef INET
253 	    "\tping [-4AaDdfHnoQqRrv] [-C pcp] [-c count] "
254 	    "[-G sweepmaxsize]\n"
255 	    "\t    [-g sweepminsize] [-h sweepincrsize] [-i wait] "
256 	    "[-l preload]\n"
257 	    "\t    [-M mask | time] [-m ttl] "
258 #ifdef IPSEC
259 	    "[-P policy] "
260 #endif
261 	    "[-p pattern] [-S src_addr] \n"
262 	    "\t    [-s packetsize] [-t timeout] [-W waittime] [-z tos] "
263 	    "IPv4-host\n"
264 	    "\tping [-4AaDdfHLnoQqRrv] [-C pcp] [-c count] [-I iface] "
265 	    "[-i wait]\n"
266 	    "\t    [-l preload] [-M mask | time] [-m ttl] "
267 #ifdef IPSEC
268 	    "[-P policy] "
269 #endif
270 	    "[-p pattern]\n"
271 	    "\t    [-S src_addr] [-s packetsize] [-T ttl] [-t timeout] [-W waittime]\n"
272 	    "\t    [-z tos] IPv4-mcast-group\n"
273 #endif /* INET */
274 #ifdef INET6
275 	    "\tping [-6AaDd"
276 #if defined(IPSEC) && !defined(IPSEC_POLICY_IPSEC)
277 	    "E"
278 #endif
279 	    "fHnNoOq"
280 #ifdef IPV6_USE_MIN_MTU
281 	    "u"
282 #endif
283 	    "vyY"
284 #if defined(IPSEC) && !defined(IPSEC_POLICY_IPSEC)
285 	    "Z"
286 #endif
287 	    "] "
288 	    "[-b bufsiz] [-C pcp] [-c count] [-e gateway]\n"
289 	    "\t    [-I interface] [-i wait] [-k addrtype] [-l preload] "
290 	    "[-m hoplimit]\n"
291 	    "\t    [-p pattern]"
292 #if defined(IPSEC) && defined(IPSEC_POLICY_IPSEC)
293 	    " [-P policy]"
294 #endif
295 	    " [-S sourceaddr] [-s packetsize] [-t timeout]\n"
296 	    "\t    [-W waittime] [-z tclass] [IPv6-hops ...] IPv6-host\n"
297 #endif	/* INET6 */
298 	    );
299 
300 	exit(1);
301 }
302