[git commit] ping: implement -A "adaptive ping"

Denys Vlasenko vda.linux at googlemail.com
Tue Feb 13 22:21:33 UTC 2018


commit: https://git.busybox.net/busybox/commit/?id=7189af847fd8a54a85f08e9bf51cd103f0d87393
branch: https://git.busybox.net/busybox/commit/?id=refs/heads/master

function                                             old     new   delta
common_ping_main                                    1757    1862    +105
packed_usage                                       32367   32427     +60
sendping_tail                                        236     209     -27
------------------------------------------------------------------------------
(add/remove: 0/0 grow/shrink: 2/1 up/down: 165/-27)           Total: 138 bytes

Signed-off-by: Denys Vlasenko <vda.linux at googlemail.com>
---
 networking/ping.c | 100 ++++++++++++++++++++++++++++++++----------------------
 1 file changed, 60 insertions(+), 40 deletions(-)

diff --git a/networking/ping.c b/networking/ping.c
index d1d59d545..bf750d032 100644
--- a/networking/ping.c
+++ b/networking/ping.c
@@ -74,6 +74,7 @@
 //usage:	)
 //usage:     "\n	-c CNT		Send only CNT pings"
 //usage:     "\n	-s SIZE		Send SIZE data bytes in packets (default 56)"
+//usage:     "\n	-A		Ping as soon as reply is recevied"
 //usage:     "\n	-t TTL		Set TTL"
 //usage:     "\n	-I IFACE/IP	Source interface or IP address"
 //usage:     "\n	-W SEC		Seconds to wait for the first response (default 10)"
@@ -90,6 +91,7 @@
 //usage:       "Send ICMP ECHO_REQUEST packets to network hosts\n"
 //usage:     "\n	-c CNT		Send only CNT pings"
 //usage:     "\n	-s SIZE		Send SIZE data bytes in packets (default 56)"
+//usage:     "\n	-A		Ping as soon as reply is recevied"
 //usage:     "\n	-I IFACE/IP	Source interface or IP address"
 //usage:     "\n	-q		Quiet, only display output at start"
 //usage:     "\n			and when finished"
@@ -348,20 +350,21 @@ static int common_ping_main(sa_family_t af, char **argv)
 /* Full(er) version */
 
 /* -c NUM, -t NUM, -w NUM, -W NUM */
-#define OPT_STRING "qvc:+s:t:+w:+W:+I:np:4"IF_PING6("6")
+#define OPT_STRING "qvAc:+s:t:+w:+W:+I:np:4"IF_PING6("6")
 enum {
 	OPT_QUIET = 1 << 0,
 	OPT_VERBOSE = 1 << 1,
-	OPT_c = 1 << 2,
-	OPT_s = 1 << 3,
-	OPT_t = 1 << 4,
-	OPT_w = 1 << 5,
-	OPT_W = 1 << 6,
-	OPT_I = 1 << 7,
-	/*OPT_n = 1 << 8, - ignored */
-	OPT_p = 1 << 9,
-	OPT_IPV4 = 1 << 10,
-	OPT_IPV6 = (1 << 11) * ENABLE_PING6,
+	OPT_A = 1 << 2,
+	OPT_c = 1 << 3,
+	OPT_s = 1 << 4,
+	OPT_t = 1 << 5,
+	OPT_w = 1 << 6,
+	OPT_W = 1 << 7,
+	OPT_I = 1 << 8,
+	/*OPT_n = 1 << 9, - ignored */
+	OPT_p = 1 << 10,
+	OPT_IPV4 = 1 << 11,
+	OPT_IPV6 = (1 << 12) * ENABLE_PING6,
 };
 
 
@@ -377,9 +380,8 @@ struct globals {
 	uint8_t pattern;
 	unsigned tmin, tmax; /* in us */
 	unsigned long long tsum; /* in us, sum of all times */
-	unsigned deadline;
+	unsigned deadline_ms;
 	unsigned timeout;
-	unsigned total_secs;
 	unsigned sizeof_rcv_packet;
 	char *rcv_packet; /* [datalen + MAXIPLEN + MAXICMPLEN] */
 	void *snd_packet; /* [datalen + ipv4/ipv6_const] */
@@ -405,9 +407,7 @@ struct globals {
 #define tmin         (G.tmin        )
 #define tmax         (G.tmax        )
 #define tsum         (G.tsum        )
-#define deadline     (G.deadline    )
 #define timeout      (G.timeout     )
-#define total_secs   (G.total_secs  )
 #define hostname     (G.hostname    )
 #define dotted       (G.dotted      )
 #define pingaddr     (G.pingaddr    )
@@ -455,7 +455,7 @@ static void print_stats_and_exit(int junk UNUSED_PARAM)
 			tmax / 1000, tmax % 1000);
 	}
 	/* if condition is true, exit with 1 -- 'failure' */
-	exit(nrecv == 0 || (deadline && nrecv < pingcount));
+	exit(nrecv == 0 || (G.deadline_ms && nrecv < pingcount));
 }
 
 static void sendping_tail(void (*sp)(int), int size_pkt)
@@ -467,22 +467,23 @@ static void sendping_tail(void (*sp)(int), int size_pkt)
 
 	size_pkt += datalen;
 
+	if (G.deadline_ms) {
+		unsigned n = ((unsigned)monotonic_ms()) - G.deadline_ms;
+		if ((int)n >= 0)
+			print_stats_and_exit(0);
+	}
+
 	/* sizeof(pingaddr) can be larger than real sa size, but I think
 	 * it doesn't matter */
 	sz = xsendto(pingsock, G.snd_packet, size_pkt, &pingaddr.sa, sizeof(pingaddr));
 	if (sz != size_pkt)
 		bb_error_msg_and_die(bb_msg_write_error);
 
-	if (pingcount == 0 || deadline || G.ntransmitted < pingcount) {
+	if (pingcount == 0 || G.ntransmitted < pingcount) {
 		/* Didn't send all pings yet - schedule next in 1s */
 		signal(SIGALRM, sp);
-		if (deadline) {
-			total_secs += PINGINTERVAL;
-			if (total_secs >= deadline)
-				signal(SIGALRM, print_stats_and_exit);
-		}
 		alarm(PINGINTERVAL);
-	} else { /* -c NN, and all NN are sent (and no deadline) */
+	} else { /* -c NN, and all NN are sent */
 		/* Wait for the last ping to come back.
 		 * -W timeout: wait for a response in seconds.
 		 * Affects only timeout in absence of any responses,
@@ -632,7 +633,7 @@ static void unpack_tail(int sz, uint32_t *tp,
 	puts(dupmsg);
 	fflush_all();
 }
-static void unpack4(char *buf, int sz, struct sockaddr_in *from)
+static int unpack4(char *buf, int sz, struct sockaddr_in *from)
 {
 	struct icmp *icmppkt;
 	struct iphdr *iphdr;
@@ -640,7 +641,7 @@ static void unpack4(char *buf, int sz, struct sockaddr_in *from)
 
 	/* discard if too short */
 	if (sz < (datalen + ICMP_MINLEN))
-		return;
+		return 0;
 
 	/* check IP header */
 	iphdr = (struct iphdr *) buf;
@@ -648,7 +649,7 @@ static void unpack4(char *buf, int sz, struct sockaddr_in *from)
 	sz -= hlen;
 	icmppkt = (struct icmp *) (buf + hlen);
 	if (icmppkt->icmp_id != myid)
-		return;				/* not our ping */
+		return 0;				/* not our ping */
 
 	if (icmppkt->icmp_type == ICMP_ECHOREPLY) {
 		uint16_t recv_seq = ntohs(icmppkt->icmp_seq);
@@ -659,25 +660,28 @@ static void unpack4(char *buf, int sz, struct sockaddr_in *from)
 		unpack_tail(sz, tp,
 			inet_ntoa(*(struct in_addr *) &from->sin_addr.s_addr),
 			recv_seq, iphdr->ttl);
-	} else if (icmppkt->icmp_type != ICMP_ECHO) {
+		return 1;
+	}
+	if (icmppkt->icmp_type != ICMP_ECHO) {
 		bb_error_msg("warning: got ICMP %d (%s)",
 				icmppkt->icmp_type,
 				icmp_type_name(icmppkt->icmp_type));
 	}
+	return 0;
 }
 #if ENABLE_PING6
-static void unpack6(char *packet, int sz, struct sockaddr_in6 *from, int hoplimit)
+static int unpack6(char *packet, int sz, struct sockaddr_in6 *from, int hoplimit)
 {
 	struct icmp6_hdr *icmppkt;
 	char buf[INET6_ADDRSTRLEN];
 
 	/* discard if too short */
 	if (sz < (datalen + sizeof(struct icmp6_hdr)))
-		return;
+		return 0;
 
 	icmppkt = (struct icmp6_hdr *) packet;
 	if (icmppkt->icmp6_id != myid)
-		return;				/* not our ping */
+		return 0;				/* not our ping */
 
 	if (icmppkt->icmp6_type == ICMP6_ECHO_REPLY) {
 		uint16_t recv_seq = ntohs(icmppkt->icmp6_seq);
@@ -689,11 +693,14 @@ static void unpack6(char *packet, int sz, struct sockaddr_in6 *from, int hoplimi
 			inet_ntop(AF_INET6, &from->sin6_addr,
 					buf, sizeof(buf)),
 			recv_seq, hoplimit);
-	} else if (icmppkt->icmp6_type != ICMP6_ECHO_REQUEST) {
+		return 1;
+	}
+	if (icmppkt->icmp6_type != ICMP6_ECHO_REQUEST) {
 		bb_error_msg("warning: got ICMP %d (%s)",
 				icmppkt->icmp6_type,
 				icmp6_type_name(icmppkt->icmp6_type));
 	}
+	return 0;
 }
 #endif
 
@@ -726,6 +733,7 @@ static void ping4(len_and_sockaddr *lsa)
 	signal(SIGINT, print_stats_and_exit);
 
 	/* start the ping's going ... */
+ send_ping:
 	sendping4(0);
 
 	/* listen for replies */
@@ -741,9 +749,12 @@ static void ping4(len_and_sockaddr *lsa)
 				bb_perror_msg("recvfrom");
 			continue;
 		}
-		unpack4(G.rcv_packet, c, &from);
+		c = unpack4(G.rcv_packet, c, &from);
 		if (pingcount && G.nreceived >= pingcount)
 			break;
+		if (c && (option_mask32 & OPT_A)) {
+			goto send_ping;
+		}
 	}
 }
 #if ENABLE_PING6
@@ -794,10 +805,6 @@ static void ping6(len_and_sockaddr *lsa)
 
 	signal(SIGINT, print_stats_and_exit);
 
-	/* start the ping's going ... */
-	sendping6(0);
-
-	/* listen for replies */
 	msg.msg_name = &from;
 	msg.msg_namelen = sizeof(from);
 	msg.msg_iov = &iov;
@@ -805,12 +812,18 @@ static void ping6(len_and_sockaddr *lsa)
 	msg.msg_control = control_buf;
 	iov.iov_base = G.rcv_packet;
 	iov.iov_len = G.sizeof_rcv_packet;
+
+	/* start the ping's going ... */
+ send_ping:
+	sendping6(0);
+
+	/* listen for replies */
 	while (1) {
 		int c;
 		struct cmsghdr *mp;
 		int hoplimit = -1;
-		msg.msg_controllen = sizeof(control_buf);
 
+		msg.msg_controllen = sizeof(control_buf);
 		c = recvmsg(pingsock, &msg, 0);
 		if (c < 0) {
 			if (errno != EINTR)
@@ -827,9 +840,12 @@ static void ping6(len_and_sockaddr *lsa)
 				move_from_unaligned_int(hoplimit, CMSG_DATA(mp));
 			}
 		}
-		unpack6(G.rcv_packet, c, &from, hoplimit);
+		c = unpack6(G.rcv_packet, c, &from, hoplimit);
 		if (pingcount && G.nreceived >= pingcount)
 			break;
+		if (c && (option_mask32 & OPT_A)) {
+			goto send_ping;
+		}
 	}
 }
 #endif
@@ -875,7 +891,7 @@ static int common_ping_main(int opt, char **argv)
 			OPT_STRING
 			/* exactly one arg; -v and -q don't mix */
 			"\0" "=1:q--v:v--q",
-			&pingcount, &str_s, &opt_ttl, &deadline, &timeout, &str_I, &str_p
+			&pingcount, &str_s, &opt_ttl, &G.deadline_ms, &timeout, &str_I, &str_p
 	);
 	if (opt & OPT_s)
 		datalen = xatou16(str_s); // -s
@@ -889,6 +905,10 @@ static int common_ping_main(int opt, char **argv)
 	}
 	if (opt & OPT_p)
 		G.pattern = xstrtou_range(str_p, 16, 0, 255);
+	if (G.deadline_ms) {
+		unsigned d = G.deadline_ms < INT_MAX/1000 ? G.deadline_ms : INT_MAX/1000;
+		G.deadline_ms = 1 | ((d * 1000) + monotonic_ms());
+	}
 
 	myid = (uint16_t) getpid();
 	hostname = argv[optind];
@@ -911,7 +931,7 @@ static int common_ping_main(int opt, char **argv)
 
 	dotted = xmalloc_sockaddr2dotted_noport(&lsa->u.sa);
 	ping(lsa);
-	print_stats_and_exit(EXIT_SUCCESS);
+	print_stats_and_exit(0);
 	/*return EXIT_SUCCESS;*/
 }
 #endif /* FEATURE_FANCY_PING */


More information about the busybox-cvs mailing list