2012-07-15 09:54:00 +02:00
|
|
|
/*
|
2015-09-30 11:48:59 +02:00
|
|
|
* Copyright (c) 2012-2015 Matthieu Herrb <matthieu@herrb.eu>
|
2012-07-15 09:54:00 +02:00
|
|
|
*
|
|
|
|
* Permission to use, copy, modify, and distribute this software for any
|
|
|
|
* purpose with or without fee is hereby granted, provided that the above
|
|
|
|
* copyright notice and this permission notice appear in all copies.
|
|
|
|
*
|
|
|
|
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
|
|
|
|
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
|
|
|
|
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
|
|
|
|
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
|
|
|
|
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
|
|
|
|
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
|
|
|
|
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
|
|
|
*/
|
|
|
|
#include <sys/types.h>
|
2012-07-30 20:49:29 +02:00
|
|
|
#include <sys/ioctl.h>
|
2012-07-15 09:54:00 +02:00
|
|
|
#include <sys/socket.h>
|
|
|
|
#include <sys/time.h>
|
|
|
|
|
2015-09-30 11:10:16 +02:00
|
|
|
#include <limits.h>
|
2015-09-30 11:31:37 +02:00
|
|
|
#ifdef HAVE_BSD_STDLIB_H
|
2015-09-30 11:10:16 +02:00
|
|
|
#include <bsd/stdlib.h>
|
2015-09-30 11:31:37 +02:00
|
|
|
#endif
|
2015-09-30 11:10:16 +02:00
|
|
|
|
2012-07-15 09:54:00 +02:00
|
|
|
#include <err.h>
|
|
|
|
#include <errno.h>
|
|
|
|
#include <netdb.h>
|
|
|
|
#include <poll.h>
|
|
|
|
#include <signal.h>
|
2012-07-30 20:49:29 +02:00
|
|
|
#include <stdbool.h>
|
2012-07-15 09:54:00 +02:00
|
|
|
#include <stdio.h>
|
|
|
|
#include <stdlib.h>
|
|
|
|
#include <string.h>
|
|
|
|
#include <time.h>
|
|
|
|
#include <unistd.h>
|
|
|
|
|
|
|
|
int sock = -1;
|
|
|
|
int verbose = 0;
|
|
|
|
struct sockaddr *server;
|
|
|
|
socklen_t serverlen;
|
|
|
|
unsigned int seq = 0;
|
2015-09-29 15:28:22 +02:00
|
|
|
size_t len = 10;
|
2012-07-15 09:54:00 +02:00
|
|
|
|
2013-04-06 16:25:30 +02:00
|
|
|
static void
|
|
|
|
usage(void)
|
|
|
|
{
|
2015-09-29 18:53:51 +02:00
|
|
|
errx(2, "usage: echoc [-c nbr][-d][-i ms][-l len][-p port][-t ms][-v] server");
|
2013-04-06 16:25:30 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
static void
|
2012-07-31 08:26:05 +02:00
|
|
|
send_packet(int unused)
|
2012-07-30 20:49:29 +02:00
|
|
|
{
|
2015-09-30 11:10:16 +02:00
|
|
|
char *buf;
|
2015-09-29 15:28:22 +02:00
|
|
|
|
2015-09-30 11:10:16 +02:00
|
|
|
buf = malloc(len);
|
|
|
|
if (buf == NULL)
|
|
|
|
return;
|
2015-09-29 15:28:22 +02:00
|
|
|
snprintf(buf, len, "%d", seq);
|
|
|
|
if (sendto(sock, buf, len, 0, server,
|
|
|
|
serverlen) != len) {
|
2012-07-30 20:49:29 +02:00
|
|
|
if (verbose)
|
|
|
|
warn("sendto");
|
|
|
|
}
|
2013-01-09 14:25:11 +01:00
|
|
|
if (verbose)
|
2012-07-30 20:49:29 +02:00
|
|
|
printf("sent %d\n", seq);
|
|
|
|
seq++;
|
|
|
|
}
|
|
|
|
|
2012-07-15 09:54:00 +02:00
|
|
|
int
|
|
|
|
main(int argc, char *argv[])
|
|
|
|
{
|
|
|
|
char name[NI_MAXHOST];
|
2015-09-30 11:10:16 +02:00
|
|
|
char *recvbuf;
|
|
|
|
const char *errstr;
|
|
|
|
char date[80];
|
2015-09-29 15:28:22 +02:00
|
|
|
char *port = "echo";
|
2012-07-15 09:54:00 +02:00
|
|
|
struct sockaddr_storage client;
|
|
|
|
struct addrinfo hints, *res, *res0;
|
2012-07-31 08:26:05 +02:00
|
|
|
struct itimerval itv;
|
2012-07-31 11:26:45 +02:00
|
|
|
struct timeval last_ts;
|
2012-08-03 11:13:16 +02:00
|
|
|
struct timeval now, diff, timeout_tv;
|
2012-07-15 11:29:43 +02:00
|
|
|
struct tm *tm;
|
2012-07-15 09:54:00 +02:00
|
|
|
struct pollfd pfd[1];
|
|
|
|
socklen_t addrlen;
|
2012-07-31 11:24:50 +02:00
|
|
|
long interval = 100; /* default interval (ms) */
|
|
|
|
long timeout = 500; /* default timeout (ms) */
|
2012-07-15 09:54:00 +02:00
|
|
|
int ch;
|
|
|
|
int nfds, received = 0;
|
2015-09-30 11:10:16 +02:00
|
|
|
int error, last = -1;
|
2012-07-31 11:26:45 +02:00
|
|
|
int disconnected;
|
2015-09-29 18:53:51 +02:00
|
|
|
int nofragment = 0;
|
2015-09-30 11:29:41 +02:00
|
|
|
int we_count = 0, counter = 0; /* don't loop forever */
|
2012-07-15 09:54:00 +02:00
|
|
|
extern int optind;
|
|
|
|
|
|
|
|
setbuf(stdout, NULL);
|
2015-09-29 18:53:51 +02:00
|
|
|
while ((ch = getopt(argc, argv, "c:di:l:p:t:v")) != -1) {
|
2012-07-15 09:54:00 +02:00
|
|
|
switch (ch) {
|
2013-01-06 17:55:38 +01:00
|
|
|
case 'c':
|
|
|
|
we_count++;
|
|
|
|
counter = atoi(optarg);
|
|
|
|
break;
|
2015-09-29 18:53:51 +02:00
|
|
|
case 'd':
|
|
|
|
nofragment++;
|
|
|
|
break;
|
2012-07-31 11:24:50 +02:00
|
|
|
case 'i':
|
|
|
|
interval = atoi(optarg);
|
|
|
|
break;
|
2015-09-29 15:28:22 +02:00
|
|
|
case 'l':
|
|
|
|
len = atoi(optarg);
|
|
|
|
break;
|
|
|
|
case 'p':
|
|
|
|
port = optarg;
|
|
|
|
break;
|
2012-07-31 11:24:50 +02:00
|
|
|
case 't':
|
|
|
|
timeout = atoi(optarg);
|
|
|
|
break;
|
2012-07-15 09:54:00 +02:00
|
|
|
case 'v':
|
2012-07-30 20:49:29 +02:00
|
|
|
verbose++;
|
2012-07-15 09:54:00 +02:00
|
|
|
break;
|
|
|
|
default:
|
|
|
|
usage();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
argc -= optind;
|
|
|
|
argv += optind;
|
|
|
|
if (argc != 1)
|
|
|
|
usage();
|
|
|
|
|
2012-07-31 11:24:50 +02:00
|
|
|
if (interval <= 0 || timeout <= 0)
|
|
|
|
errx(2, "interval and timeout must be > 0");
|
2014-04-29 18:59:46 +02:00
|
|
|
if (we_count && counter < 2)
|
2013-01-06 17:55:38 +01:00
|
|
|
errx(2, "can't count down from nothing");
|
2012-07-31 11:24:50 +02:00
|
|
|
/* force timeout >= interval */
|
|
|
|
if (timeout < interval) {
|
|
|
|
timeout = interval;
|
|
|
|
warnx("adjusting timeout to %ld ms "
|
|
|
|
"(must be greater than interval)", timeout);
|
|
|
|
}
|
2012-08-03 11:13:16 +02:00
|
|
|
timeout_tv.tv_sec = timeout / 1000;
|
|
|
|
timeout_tv.tv_usec = (timeout % 1000) * 1000;
|
2012-07-15 09:54:00 +02:00
|
|
|
memset(&hints, 0, sizeof(hints));
|
|
|
|
hints.ai_family = PF_UNSPEC;
|
|
|
|
hints.ai_socktype = SOCK_DGRAM;
|
2015-09-29 15:28:22 +02:00
|
|
|
error = getaddrinfo(argv[0], port, &hints, &res0);
|
2012-07-15 09:54:00 +02:00
|
|
|
if (error)
|
2012-07-31 11:27:03 +02:00
|
|
|
errx(1, "%s: %s", argv[0], gai_strerror(error));
|
2012-07-15 09:54:00 +02:00
|
|
|
|
|
|
|
for (res = res0; res != NULL; res = res->ai_next) {
|
|
|
|
sock = socket(res->ai_family, res->ai_socktype,
|
|
|
|
res->ai_protocol);
|
|
|
|
if (sock != -1) {
|
|
|
|
server = res->ai_addr;
|
|
|
|
serverlen = res->ai_addrlen;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if (sock == -1)
|
|
|
|
err(1, "socket");
|
|
|
|
|
2012-07-31 08:44:18 +02:00
|
|
|
disconnected = 0;
|
2012-07-15 09:54:00 +02:00
|
|
|
memset(&client, 0, sizeof(client));
|
2012-07-30 20:49:29 +02:00
|
|
|
|
2015-09-30 11:10:16 +02:00
|
|
|
recvbuf = malloc(len);
|
|
|
|
if (recvbuf == NULL)
|
2015-09-29 15:28:22 +02:00
|
|
|
err(2, "malloc receive buffer");
|
|
|
|
|
2015-09-29 19:18:07 +02:00
|
|
|
#ifdef IP_MTU_DISCOVER
|
2015-09-29 18:53:51 +02:00
|
|
|
/* set the DF flag ? */
|
|
|
|
if (nofragment)
|
|
|
|
ch = IP_PMTUDISC_DO;
|
|
|
|
else
|
|
|
|
ch = IP_PMTUDISC_DONT;
|
|
|
|
if (setsockopt(sock, IPPROTO_IP, IP_MTU_DISCOVER, &ch, sizeof(ch)) < 0)
|
|
|
|
err(2, "setsockopt IP_MTU_DISCOVER");
|
2015-09-29 19:18:07 +02:00
|
|
|
#endif
|
2018-07-18 10:56:06 +02:00
|
|
|
signal(SIGALRM, send_packet);
|
|
|
|
|
2018-07-18 10:50:33 +02:00
|
|
|
/* timer values */
|
|
|
|
itv.it_interval.tv_usec = interval*1000;
|
|
|
|
itv.it_interval.tv_sec = 0;
|
|
|
|
itv.it_value.tv_usec = interval*1000;
|
|
|
|
itv.it_value.tv_sec = 0;
|
|
|
|
if (setitimer(ITIMER_REAL, &itv, NULL) == -1)
|
|
|
|
err(2, "setitimer");
|
|
|
|
|
|
|
|
gettimeofday(&last_ts, NULL);
|
2018-07-18 11:05:54 +02:00
|
|
|
tm = localtime((time_t *)&last_ts.tv_sec);
|
|
|
|
strftime(date, sizeof(date), "%F %T", tm);
|
|
|
|
printf("%s.%06ld: starting\n", date, last_ts.tv_usec);
|
2018-07-18 10:50:33 +02:00
|
|
|
|
2012-07-15 09:54:00 +02:00
|
|
|
while (1) {
|
2012-07-31 11:26:45 +02:00
|
|
|
/* poll() loop to handle interruptions by SIGALRM */
|
2012-07-30 20:49:29 +02:00
|
|
|
while (1) {
|
|
|
|
pfd[0].fd = sock;
|
|
|
|
pfd[0].events = POLLIN;
|
2012-07-31 11:24:50 +02:00
|
|
|
nfds = poll(pfd, 1, timeout);
|
2012-07-31 08:26:05 +02:00
|
|
|
gettimeofday(&now, NULL);
|
2012-07-31 11:26:45 +02:00
|
|
|
timersub(&now, &last_ts, &diff);
|
2012-07-30 20:49:29 +02:00
|
|
|
if (nfds > 0)
|
|
|
|
break;
|
|
|
|
if (nfds == -1 && errno != EINTR)
|
|
|
|
warn("poll error");
|
2012-08-03 11:13:16 +02:00
|
|
|
if (!timercmp(&diff, &timeout_tv, <)) {
|
2012-07-30 20:49:29 +02:00
|
|
|
disconnected++;
|
|
|
|
nfds = 0;
|
|
|
|
break;
|
|
|
|
}
|
2012-07-15 09:54:00 +02:00
|
|
|
}
|
2012-07-30 20:49:29 +02:00
|
|
|
if ((nfds == 0)) {
|
2013-01-09 14:25:11 +01:00
|
|
|
if (verbose)
|
2012-07-31 08:44:18 +02:00
|
|
|
printf("%d packet(s) dropped in %ld.%06ld s\n",
|
2015-09-30 11:29:41 +02:00
|
|
|
seq - last, (long)diff.tv_sec, diff.tv_usec);
|
2013-01-09 14:25:11 +01:00
|
|
|
|
2012-07-30 20:49:29 +02:00
|
|
|
if (disconnected == 1) {
|
2012-07-31 14:15:27 +02:00
|
|
|
tm = localtime((time_t *)&last_ts.tv_sec);
|
2015-09-30 11:10:16 +02:00
|
|
|
strftime(date, sizeof(date), "%F %T", tm);
|
2012-07-30 20:49:29 +02:00
|
|
|
printf("%s.%06ld: lost connection\n",
|
2015-09-30 11:10:16 +02:00
|
|
|
date, last_ts.tv_usec);
|
2012-07-15 09:54:00 +02:00
|
|
|
}
|
|
|
|
continue;
|
|
|
|
}
|
2012-07-30 20:49:29 +02:00
|
|
|
addrlen = sizeof(client);
|
2015-09-30 11:10:16 +02:00
|
|
|
if ((received = recvfrom(sock, recvbuf, len,
|
2012-07-15 09:54:00 +02:00
|
|
|
MSG_DONTWAIT,
|
|
|
|
(struct sockaddr *) &client,
|
2015-09-30 11:10:16 +02:00
|
|
|
&addrlen)) != len) {
|
2012-07-15 09:54:00 +02:00
|
|
|
warn("recvfrom");
|
|
|
|
}
|
|
|
|
|
|
|
|
if (verbose && (serverlen != addrlen ||
|
|
|
|
memcmp(&client, server, addrlen) != 0)) {
|
|
|
|
if ((error = getnameinfo((struct sockaddr *)&client,
|
|
|
|
addrlen, name, sizeof(name),
|
|
|
|
NULL, 0, NI_DGRAM)) != 0) {
|
|
|
|
warnx("%s", gai_strerror(error));
|
|
|
|
} else {
|
|
|
|
warnx("received data from unknown host %s",
|
|
|
|
name);
|
|
|
|
}
|
|
|
|
}
|
2012-07-31 08:44:18 +02:00
|
|
|
if (disconnected) {
|
2012-07-30 20:49:29 +02:00
|
|
|
tm = localtime((time_t *)&now.tv_sec);
|
2015-09-30 11:10:16 +02:00
|
|
|
strftime(date, sizeof(date), "%F %T", tm);
|
2012-07-31 14:17:35 +02:00
|
|
|
printf("%s.%06ld: connection is back "
|
2012-07-31 14:38:58 +02:00
|
|
|
"dropped %d packets\n",
|
2015-09-30 11:10:16 +02:00
|
|
|
date, now.tv_usec, seq - last);
|
2012-07-31 08:44:18 +02:00
|
|
|
disconnected = 0;
|
2012-07-15 09:54:00 +02:00
|
|
|
}
|
2015-09-30 11:10:16 +02:00
|
|
|
last = strtonum(recvbuf, 0, INT_MAX, &errstr);
|
|
|
|
if (errstr)
|
|
|
|
errx(1, "invalid reply %s", errstr);
|
2012-07-31 14:39:15 +02:00
|
|
|
memcpy(&last_ts, &now, sizeof(struct timeval));
|
2012-07-31 08:44:18 +02:00
|
|
|
if (verbose)
|
2015-09-30 11:10:16 +02:00
|
|
|
printf("received %d %ld.%06ld\n", last,
|
2012-07-31 08:44:18 +02:00
|
|
|
(long)diff.tv_sec, diff.tv_usec);
|
2013-01-06 17:55:38 +01:00
|
|
|
if (we_count) {
|
|
|
|
counter--;
|
|
|
|
if (counter == 0) {
|
|
|
|
printf("all job done\n");
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
2012-07-15 09:54:00 +02:00
|
|
|
}
|
|
|
|
close(sock);
|
|
|
|
exit(0);
|
|
|
|
}
|