mirror of
https://git.savannah.gnu.org/git/inetutils.git
synced 2026-01-26 16:29:08 +08:00
* libinetutils/Makefile.am (libinetutils_a_SOURCES): Order source names. * libinetutils/libinetutils.h (iu_argp_init): New macro. * ftp/main.c, ftpd/ftpd.c, hostname/hostname.c, inetd/inetd.c, logger/logger.c, ping/ping.c, ping/ping6.c, rcp/rcp.c, rexecd/rexecd.c, rlogin/rlogin.c, rlogind/rlogind.c, rsh/rsh.c, rshd/rshd.c, syslogd/syslogd.c, talk/talk.c, talkd/talkd.c, telnet/main.c, telnetd/telnetd.c, tftpd/tftpd.c, traceroute/traceroute.c, uucpd/uucpd.c: Use iu_argp_init. Rewrite option descriptions to conform to standards.texi. * ifconfig/Makefile.am (INCLUDES): Add libinetutils * ifconfig/changeif.c: Use error() for diagnostics. * ifconfig/printif.c: Likewise. * ifconfig/ifconfig.c: Likewise. (main): Call parse_cmdline. * ifconfig/ifconfig.h: Include error.h, argp.h and libinetutils.h. * ifconfig/options.c: Use argp. "check-existence": fix typo (was *check-existance). (usage): Remove. Use error() for diagnostics. * ifconfig/options.h (parse_opt): Rename to parse_cmdline. * ifconfig/system.h (system_argp_child): New extern. (system_help_options, SYSTEM_SHORT_OPTIONS) (SYSTEM_LONG_OPTIONS): Remove. * ifconfig/system/generic.c: Use new command line parsing method. * ifconfig/system/hpux.c: Likewise. * ifconfig/system/linux.c: Likewise. * ifconfig/system/linux.h: Likewise. * ifconfig/system/osf.c: Likewise. * ifconfig/system/qnx.c: Likewise. * ifconfig/system/solaris.c: Likewise. * gwhois/Makefile.am (INCLUDES): Add libinetutils. * gwhois/whois.c: Use argp to parse the command line. (is_ripe_server): New function. (queryformat): Use is_ripe_server to determine if the server supports RIPE syntax.
891 lines
20 KiB
C
891 lines
20 KiB
C
/* Copyright (C) 1998, 2001, 2002, 2004, 2005, 2006, 2007, 2008, 2009
|
|
Free Software Foundation, Inc.
|
|
|
|
This file is part of GNU Inetutils.
|
|
|
|
GNU Inetutils is free software; you can redistribute it and/or modify
|
|
it under the terms of the GNU General Public License as published by
|
|
the Free Software Foundation; either version 3, or (at your option)
|
|
any later version.
|
|
|
|
GNU Inetutils is distributed in the hope that it will be useful,
|
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
MERCHANTABILITY or FITNESS FOR PARTICULAR PURPOSE. See the
|
|
GNU General Public License for more details.
|
|
|
|
You should have received a copy of the GNU General Public License
|
|
along with GNU Inetutils; see the file COPYING. If not, write
|
|
to the Free Software Foundation, Inc., 51 Franklin Street,
|
|
Fifth Floor, Boston, MA 02110-1301 USA. */
|
|
|
|
#ifdef HAVE_CONFIG_H
|
|
# include <config.h>
|
|
#endif
|
|
|
|
#include <sys/socket.h>
|
|
#include <sys/time.h>
|
|
#include <signal.h>
|
|
|
|
#include <netinet/in.h>
|
|
#include <netinet/ip6.h>
|
|
#include <netinet/icmp6.h>
|
|
|
|
#include <netdb.h>
|
|
#include <unistd.h>
|
|
#include <stdlib.h>
|
|
#include <string.h>
|
|
#include <stdio.h>
|
|
#include <argp.h>
|
|
#include <ctype.h>
|
|
#include <errno.h>
|
|
#include <limits.h>
|
|
|
|
#include <xalloc.h>
|
|
#include "ping6.h"
|
|
#include "libinetutils.h"
|
|
|
|
static PING *ping;
|
|
bool is_root = false;
|
|
unsigned char *data_buffer;
|
|
u_char *patptr;
|
|
int one = 1;
|
|
int pattern_len = 16;
|
|
size_t data_length = PING_DATALEN;
|
|
size_t count = DEFAULT_PING_COUNT;
|
|
size_t interval;
|
|
int socket_type;
|
|
static unsigned int options;
|
|
static unsigned long preload = 0;
|
|
|
|
static int ping_echo (char *hostname);
|
|
static void ping_reset (PING * p);
|
|
static int send_echo (PING * ping);
|
|
|
|
const char args_doc[] = "HOST ...";
|
|
const char doc[] = "Send ICMP ECHO_REQUEST packets to network hosts."
|
|
"\vOptions marked with (root only) are available only to "
|
|
"superuser.";
|
|
const char *program_authors[] = {
|
|
"Jeroen Dekkers",
|
|
NULL
|
|
};
|
|
|
|
static struct argp_option argp_options[] = {
|
|
#define GRP 0
|
|
{NULL, 0, NULL, 0, "Options valid for all request types:", GRP},
|
|
{"count", 'c', "NUMBER", 0, "stop after sending NUMBER packets", GRP+1},
|
|
{"debug", 'd', NULL, 0, "set the SO_DEBUG option", GRP+1},
|
|
{"interval", 'i', "NUMBER", 0, "wait NUMBER seconds between sending each "
|
|
"packet", GRP+1},
|
|
{"numeric", 'n', NULL, 0, "so not resolve host addresses", GRP+1},
|
|
{"ignore-routing", 'r', NULL, 0, "send directly to a host on an attached "
|
|
"network", GRP+1},
|
|
#undef GRP
|
|
#define GRP 10
|
|
{NULL, 0, NULL, 0, "Options valid for --echo requests:", GRP},
|
|
{"flood", 'f', NULL, 0, "flood ping (root only)", GRP+1},
|
|
{"preload", 'l', "NUMBER", 0, "send NUMBER packets as fast as possible "
|
|
"before falling into normal mode of behavior (root only)", GRP+1},
|
|
{"pattern", 'p', "PATTERN", 0, "fill ICMP packet with given pattern (hex)",
|
|
GRP+1},
|
|
{"quiet", 'q', NULL, 0, "quiet output", GRP+1},
|
|
{"size", 's', "NUMBER", 0, "send NUMBER data octets", GRP+1},
|
|
#undef GRP
|
|
{NULL}
|
|
};
|
|
|
|
static error_t
|
|
parse_opt (int key, char *arg, struct argp_state *state)
|
|
{
|
|
char *endptr;
|
|
u_char pattern[16];
|
|
double v;
|
|
|
|
switch (key)
|
|
{
|
|
case 'c':
|
|
count = ping_cvt_number (arg, 0, 0);
|
|
break;
|
|
|
|
case 'd':
|
|
socket_type = SO_DEBUG;
|
|
break;
|
|
|
|
case 'f':
|
|
if (!is_root)
|
|
error (EXIT_FAILURE, errno, NULL);
|
|
|
|
options |= OPT_FLOOD;
|
|
setbuf (stdout, (char *) NULL);
|
|
break;
|
|
|
|
case 'i':
|
|
options |= OPT_INTERVAL;
|
|
interval = ping_cvt_number (arg, 0, 0);
|
|
break;
|
|
|
|
case 'l':
|
|
if (!is_root)
|
|
error (EXIT_FAILURE, errno, NULL);
|
|
|
|
preload = strtoul (arg, &endptr, 0);
|
|
if (*endptr || preload > INT_MAX)
|
|
error (EXIT_FAILURE, errno, NULL);
|
|
|
|
break;
|
|
|
|
case 'n':
|
|
options |= OPT_NUMERIC;
|
|
break;
|
|
|
|
case 'p':
|
|
decode_pattern (arg, &pattern_len, pattern);
|
|
patptr = pattern;
|
|
break;
|
|
|
|
case 'q':
|
|
options |= OPT_QUIET;
|
|
break;
|
|
|
|
case 'r':
|
|
socket_type = SO_DEBUG;
|
|
break;
|
|
|
|
case 's':
|
|
data_length = ping_cvt_number (arg, PING_MAX_DATALEN, 1);
|
|
break;
|
|
|
|
case ARGP_KEY_NO_ARGS:
|
|
argp_error (state, "missing host operand");
|
|
|
|
default:
|
|
return ARGP_ERR_UNKNOWN;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
static struct argp argp = {argp_options, parse_opt, args_doc, doc};
|
|
|
|
int
|
|
main (int argc, char **argv)
|
|
{
|
|
int index;
|
|
int status = 0;
|
|
|
|
set_program_name (argv[0]);
|
|
|
|
if (getuid () == 0)
|
|
is_root = true;
|
|
|
|
/* Parse command line */
|
|
iu_argp_init ("ping6", program_authors);
|
|
argp_parse (&argp, argc, argv, 0, &index, NULL);
|
|
|
|
ping = ping_init (0, getpid ());
|
|
if (ping == NULL)
|
|
/* ping_init() prints our error message. */
|
|
exit (1);
|
|
|
|
setsockopt (ping->ping_fd, SOL_SOCKET, SO_BROADCAST, (char *) &one, sizeof (one));
|
|
|
|
/* Reset root privileges */
|
|
setuid (getuid ());
|
|
|
|
argc -= index;
|
|
argv += index;
|
|
|
|
if (count != 0)
|
|
ping_set_count (ping, count);
|
|
|
|
if (socket_type != 0)
|
|
ping_set_sockopt (ping, socket_type, &one, sizeof (one));
|
|
|
|
if (options & OPT_INTERVAL)
|
|
ping_set_interval (ping, interval);
|
|
|
|
init_data_buffer (patptr, pattern_len);
|
|
|
|
while (argc--)
|
|
{
|
|
status |= ping_echo (*argv++);
|
|
ping_reset (ping);
|
|
}
|
|
|
|
return status;
|
|
}
|
|
|
|
static char *
|
|
ipaddr2str (struct sockaddr_in6 *from)
|
|
{
|
|
int err;
|
|
size_t len;
|
|
char *buf, ipstr[256], hoststr[256];
|
|
|
|
err = getnameinfo ((struct sockaddr *) from, sizeof (*from), ipstr,
|
|
sizeof (ipstr), NULL, 0, NI_NUMERICHOST);
|
|
if (err)
|
|
{
|
|
const char *errmsg;
|
|
|
|
if (err == EAI_SYSTEM)
|
|
errmsg = strerror (errno);
|
|
else
|
|
errmsg = gai_strerror (err);
|
|
|
|
fprintf (stderr, "ping: getnameinfo: %s\n", errmsg);
|
|
return xstrdup ("unknown");
|
|
}
|
|
|
|
if (options & OPT_NUMERIC)
|
|
return xstrdup (ipstr);
|
|
|
|
err = getnameinfo ((struct sockaddr *) from, sizeof (*from), hoststr,
|
|
sizeof (hoststr), NULL, 0, NI_NAMEREQD);
|
|
if (err)
|
|
return xstrdup (ipstr);
|
|
|
|
len = strlen (ipstr) + strlen (hoststr) + 4; /* Pair of parentheses, a space
|
|
and a NUL. */
|
|
buf = xmalloc (len);
|
|
sprintf (buf, "%s (%s)", hoststr, ipstr);
|
|
|
|
return buf;
|
|
}
|
|
|
|
static volatile int stop = 0;
|
|
|
|
static RETSIGTYPE
|
|
sig_int (int signal)
|
|
{
|
|
stop = 1;
|
|
}
|
|
|
|
static int
|
|
ping_run (PING * ping, int (*finish) ())
|
|
{
|
|
fd_set fdset;
|
|
int fdmax;
|
|
struct timeval timeout;
|
|
struct timeval last, intvl, now;
|
|
struct timeval *t = NULL;
|
|
int finishing = 0;
|
|
int nresp = 0;
|
|
int i;
|
|
|
|
signal (SIGINT, sig_int);
|
|
|
|
fdmax = ping->ping_fd + 1;
|
|
|
|
for (i = 0; i < preload; i++)
|
|
send_echo (ping);
|
|
|
|
if (options & OPT_FLOOD)
|
|
{
|
|
intvl.tv_sec = 0;
|
|
intvl.tv_usec = 10000;
|
|
}
|
|
else
|
|
PING_SET_INTERVAL (intvl, ping->ping_interval);
|
|
|
|
gettimeofday (&last, NULL);
|
|
send_echo (ping);
|
|
|
|
while (!stop)
|
|
{
|
|
int n;
|
|
|
|
FD_ZERO (&fdset);
|
|
FD_SET (ping->ping_fd, &fdset);
|
|
gettimeofday (&now, NULL);
|
|
timeout.tv_sec = last.tv_sec + intvl.tv_sec - now.tv_sec;
|
|
timeout.tv_usec = last.tv_usec + intvl.tv_usec - now.tv_usec;
|
|
|
|
while (timeout.tv_usec < 0)
|
|
{
|
|
timeout.tv_usec += 1000000;
|
|
timeout.tv_sec--;
|
|
}
|
|
while (timeout.tv_usec >= 1000000)
|
|
{
|
|
timeout.tv_usec -= 1000000;
|
|
timeout.tv_sec++;
|
|
}
|
|
|
|
if (timeout.tv_sec < 0)
|
|
timeout.tv_sec = timeout.tv_usec = 0;
|
|
|
|
n = select (fdmax, &fdset, NULL, NULL, &timeout);
|
|
if (n < 0)
|
|
{
|
|
if (errno != EINTR)
|
|
perror ("select");
|
|
continue;
|
|
}
|
|
else if (n == 1)
|
|
{
|
|
if (ping_recv (ping) == 0)
|
|
nresp++;
|
|
if (t == 0)
|
|
{
|
|
gettimeofday (&now, NULL);
|
|
t = &now;
|
|
}
|
|
if (ping->ping_count && nresp >= ping->ping_count)
|
|
break;
|
|
}
|
|
else
|
|
{
|
|
if (!ping->ping_count || ping->ping_num_xmit < ping->ping_count)
|
|
{
|
|
send_echo (ping);
|
|
if (!(options & OPT_QUIET) && options & OPT_FLOOD)
|
|
{
|
|
putchar ('.');
|
|
}
|
|
}
|
|
else if (finishing)
|
|
break;
|
|
else
|
|
{
|
|
finishing = 1;
|
|
|
|
intvl.tv_sec = MAXWAIT;
|
|
}
|
|
gettimeofday (&last, NULL);
|
|
}
|
|
}
|
|
|
|
ping_unset_data (ping);
|
|
|
|
if (finish)
|
|
return (*finish) ();
|
|
return 0;
|
|
}
|
|
|
|
static int
|
|
send_echo (PING * ping)
|
|
{
|
|
int off = 0;
|
|
|
|
if (PING_TIMING (data_length))
|
|
{
|
|
struct timeval tv;
|
|
gettimeofday (&tv, NULL);
|
|
ping_set_data (ping, &tv, 0, sizeof (tv), USE_IPV6);
|
|
off += sizeof (tv);
|
|
}
|
|
if (data_buffer)
|
|
ping_set_data (ping, data_buffer, off,
|
|
data_length > PING_HEADER_LEN ?
|
|
data_length - PING_HEADER_LEN : data_length, USE_IPV6);
|
|
return ping_xmit (ping);
|
|
}
|
|
|
|
static int
|
|
ping_finish ()
|
|
{
|
|
fflush (stdout);
|
|
printf ("--- %s ping statistics ---\n", ping->ping_hostname);
|
|
printf ("%ld packets transmitted, ", ping->ping_num_xmit);
|
|
printf ("%ld packets received, ", ping->ping_num_recv);
|
|
if (ping->ping_num_rept)
|
|
printf ("+%ld duplicates, ", ping->ping_num_rept);
|
|
if (ping->ping_num_xmit)
|
|
{
|
|
if (ping->ping_num_recv > ping->ping_num_xmit)
|
|
printf ("-- somebody's printing up packets!");
|
|
else
|
|
printf ("%d%% packet loss",
|
|
(int) (((ping->ping_num_xmit - ping->ping_num_recv) * 100) /
|
|
ping->ping_num_xmit));
|
|
|
|
}
|
|
printf ("\n");
|
|
return 0;
|
|
}
|
|
|
|
static int print_echo (int dup, int hops, struct ping_stat *stat,
|
|
struct sockaddr_in6 *dest, struct sockaddr_in6 *from,
|
|
struct icmp6_hdr *icmp6, int datalen);
|
|
static void print_icmp_error (struct sockaddr_in6 *from,
|
|
struct icmp6_hdr *icmp6, int len);
|
|
|
|
static int echo_finish (void);
|
|
|
|
static int
|
|
ping_echo (char *hostname)
|
|
{
|
|
int err;
|
|
char buffer[256];
|
|
struct ping_stat ping_stat;
|
|
int status;
|
|
|
|
if (options & OPT_FLOOD && options & OPT_INTERVAL)
|
|
error (EXIT_FAILURE, 0, "-f and -i incompatible options");
|
|
|
|
memset (&ping_stat, 0, sizeof (ping_stat));
|
|
ping_stat.tmin = 999999999.0;
|
|
|
|
ping->ping_datalen = data_length;
|
|
ping->ping_closure = &ping_stat;
|
|
|
|
if (ping_set_dest (ping, hostname))
|
|
error (EXIT_FAILURE, 0, "unknown host %s", hostname);
|
|
|
|
err = getnameinfo ((struct sockaddr *) &ping->ping_dest.ping_sockaddr6,
|
|
sizeof (ping->ping_dest.ping_sockaddr6), buffer,
|
|
sizeof (buffer), NULL, 0, NI_NUMERICHOST);
|
|
if (err)
|
|
{
|
|
const char *errmsg;
|
|
|
|
if (err == EAI_SYSTEM)
|
|
errmsg = strerror (errno);
|
|
else
|
|
errmsg = gai_strerror (err);
|
|
|
|
error (EXIT_FAILURE, 0, "getnameinfo: %s", errmsg);
|
|
}
|
|
|
|
printf ("PING %s (%s): %d data bytes\n",
|
|
ping->ping_hostname, buffer, data_length);
|
|
|
|
status = ping_run (ping, echo_finish);
|
|
free (ping->ping_hostname);
|
|
return status;
|
|
}
|
|
|
|
static void
|
|
ping_reset (PING * p)
|
|
{
|
|
p->ping_num_xmit = 0;
|
|
p->ping_num_recv = 0;
|
|
p->ping_num_rept = 0;
|
|
}
|
|
|
|
static int
|
|
print_echo (int dupflag, int hops, struct ping_stat *ping_stat,
|
|
struct sockaddr_in6 *dest, struct sockaddr_in6 *from,
|
|
struct icmp6_hdr *icmp6, int datalen)
|
|
{
|
|
int err;
|
|
char buf[256];
|
|
struct timeval tv;
|
|
int timing = 0;
|
|
double triptime = 0.0;
|
|
|
|
gettimeofday (&tv, NULL);
|
|
|
|
/* Do timing */
|
|
if (PING_TIMING (datalen - sizeof (struct icmp6_hdr)))
|
|
{
|
|
struct timeval tv1, *tp;
|
|
|
|
timing++;
|
|
tp = (struct timeval *) (icmp6 + 1);
|
|
|
|
/* Avoid unaligned data: */
|
|
memcpy (&tv1, tp, sizeof (tv1));
|
|
tvsub (&tv, &tv1);
|
|
|
|
triptime = ((double) tv.tv_sec) * 1000.0 +
|
|
((double) tv.tv_usec) / 1000.0;
|
|
ping_stat->tsum += triptime;
|
|
ping_stat->tsumsq += triptime * triptime;
|
|
if (triptime < ping_stat->tmin)
|
|
ping_stat->tmin = triptime;
|
|
if (triptime > ping_stat->tmax)
|
|
ping_stat->tmax = triptime;
|
|
}
|
|
|
|
if (options & OPT_QUIET)
|
|
return 0;
|
|
if (options & OPT_FLOOD)
|
|
{
|
|
putchar ('\b');
|
|
return 0;
|
|
}
|
|
|
|
err = getnameinfo ((struct sockaddr *) from, sizeof (*from), buf,
|
|
sizeof (buf), NULL, 0, 0);
|
|
if (err)
|
|
{
|
|
const char *errmsg;
|
|
|
|
if (err == EAI_SYSTEM)
|
|
errmsg = strerror (errno);
|
|
else
|
|
errmsg = gai_strerror (err);
|
|
|
|
fprintf (stderr, "ping: getnameinfo: %s\n", errmsg);
|
|
|
|
strcpy (buf, "unknown");
|
|
}
|
|
|
|
printf ("%d bytes from %s: icmp_seq=%u", datalen, buf,
|
|
htons (icmp6->icmp6_seq));
|
|
if (hops >= 0)
|
|
printf (" ttl=%d", hops);
|
|
if (timing)
|
|
printf (" time=%.3f ms", triptime);
|
|
if (dupflag)
|
|
printf (" (DUP!)");
|
|
|
|
putchar ('\n');
|
|
|
|
return 0;
|
|
}
|
|
|
|
#define NITEMS(a) sizeof(a)/sizeof((a)[0])
|
|
|
|
struct icmp_code_descr
|
|
{
|
|
int code;
|
|
char *diag;
|
|
};
|
|
|
|
static struct icmp_code_descr icmp_dest_unreach_desc[] = {
|
|
{ICMP6_DST_UNREACH_NOROUTE, "No route to destination"},
|
|
{ICMP6_DST_UNREACH_ADMIN, "Destination administratively prohibited"},
|
|
{ICMP6_DST_UNREACH_BEYONDSCOPE, "Beyond scope of source address"},
|
|
{ICMP6_DST_UNREACH_ADDR, "Address unreachable"},
|
|
{ICMP6_DST_UNREACH_NOPORT, "Port unreachable"},
|
|
};
|
|
|
|
static void
|
|
print_dst_unreach (struct icmp6_hdr *icmp6)
|
|
{
|
|
struct icmp_code_descr *p;
|
|
|
|
printf ("Destination unreachable: ");
|
|
for (p = icmp_dest_unreach_desc;
|
|
p < icmp_dest_unreach_desc + NITEMS (icmp_dest_unreach_desc); p++)
|
|
{
|
|
if (p->code == icmp6->icmp6_code)
|
|
{
|
|
puts (p->diag);
|
|
return;
|
|
}
|
|
}
|
|
|
|
printf ("Unknown code %d\n", icmp6->icmp6_code);
|
|
}
|
|
|
|
static void
|
|
print_packet_too_big (struct icmp6_hdr *icmp6)
|
|
{
|
|
printf ("Packet too big, mtu=%d\n", icmp6->icmp6_mtu);
|
|
}
|
|
|
|
static struct icmp_code_descr icmp_time_exceeded_desc[] = {
|
|
{ICMP6_TIME_EXCEED_TRANSIT, "Hop limit exceeded"},
|
|
{ICMP6_TIME_EXCEED_REASSEMBLY, "Fragment reassembly timeout"},
|
|
};
|
|
|
|
static void
|
|
print_time_exceeded (struct icmp6_hdr *icmp6)
|
|
{
|
|
struct icmp_code_descr *p;
|
|
|
|
printf ("Time exceeded: ");
|
|
for (p = icmp_time_exceeded_desc;
|
|
p < icmp_time_exceeded_desc + NITEMS (icmp_time_exceeded_desc); p++)
|
|
{
|
|
if (p->code == icmp6->icmp6_code)
|
|
{
|
|
puts (p->diag);
|
|
return;
|
|
}
|
|
}
|
|
|
|
printf ("Unknown code %d\n", icmp6->icmp6_code);
|
|
}
|
|
|
|
static struct icmp_code_descr icmp_param_prob_desc[] = {
|
|
{ICMP6_PARAMPROB_HEADER, "Erroneous header field"},
|
|
{ICMP6_PARAMPROB_NEXTHEADER, "Unrecognized Next Header type"},
|
|
{ICMP6_PARAMPROB_OPTION, "Unrecognized IPv6 option"},
|
|
};
|
|
|
|
static void
|
|
print_param_prob (struct icmp6_hdr *icmp6)
|
|
{
|
|
struct icmp_code_descr *p;
|
|
|
|
printf ("Parameter problem: ");
|
|
for (p = icmp_param_prob_desc;
|
|
p < icmp_param_prob_desc + NITEMS (icmp_param_prob_desc); p++)
|
|
{
|
|
if (p->code == icmp6->icmp6_code)
|
|
{
|
|
puts (p->diag);
|
|
return;
|
|
}
|
|
}
|
|
|
|
printf ("Unknown code %d\n", icmp6->icmp6_code);
|
|
}
|
|
|
|
static struct icmp_diag
|
|
{
|
|
int type;
|
|
void (*func) (struct icmp6_hdr *);
|
|
} icmp_diag[] =
|
|
{
|
|
{
|
|
ICMP6_DST_UNREACH, print_dst_unreach},
|
|
{
|
|
ICMP6_PACKET_TOO_BIG, print_packet_too_big},
|
|
{
|
|
ICMP6_TIME_EXCEEDED, print_time_exceeded},
|
|
{
|
|
ICMP6_PARAM_PROB, print_param_prob},};
|
|
|
|
static void
|
|
print_icmp_error (struct sockaddr_in6 *from, struct icmp6_hdr *icmp6, int len)
|
|
{
|
|
char *s;
|
|
struct icmp_diag *p;
|
|
|
|
s = ipaddr2str (from);
|
|
printf ("%d bytes from %s: ", len, s);
|
|
free (s);
|
|
|
|
for (p = icmp_diag; p < icmp_diag + NITEMS (icmp_diag); p++)
|
|
{
|
|
if (p->type == icmp6->icmp6_type)
|
|
{
|
|
p->func (icmp6);
|
|
return;
|
|
}
|
|
}
|
|
|
|
/* This should never happen because of the ICMP6_FILTER set in
|
|
ping_init(). */
|
|
printf ("Unknown ICMP type: %d\n", icmp6->icmp6_type);
|
|
}
|
|
|
|
static int
|
|
echo_finish ()
|
|
{
|
|
ping_finish ();
|
|
if (ping->ping_num_recv && PING_TIMING (data_length))
|
|
{
|
|
struct ping_stat *ping_stat = (struct ping_stat *) ping->ping_closure;
|
|
double total = ping->ping_num_recv + ping->ping_num_rept;
|
|
double avg = ping_stat->tsum / total;
|
|
double vari = ping_stat->tsumsq / total - avg * avg;
|
|
|
|
printf ("round-trip min/avg/max/stddev = %.3f/%.3f/%.3f/%.3f ms\n",
|
|
ping_stat->tmin, avg, ping_stat->tmax, nsqrt (vari, 0.0005));
|
|
}
|
|
return (ping->ping_num_recv == 0);
|
|
}
|
|
|
|
static PING *
|
|
ping_init (int type, int ident)
|
|
{
|
|
int fd, err;
|
|
const int on = 1;
|
|
PING *p;
|
|
struct icmp6_filter filter;
|
|
|
|
/* Initialize raw ICMPv6 socket */
|
|
fd = socket (PF_INET6, SOCK_RAW, IPPROTO_ICMPV6);
|
|
if (fd < 0)
|
|
{
|
|
if (errno == EPERM)
|
|
error (EXIT_FAILURE, errno, NULL);
|
|
|
|
return NULL;
|
|
}
|
|
|
|
/* Tell which ICMPs we are interested in. */
|
|
ICMP6_FILTER_SETBLOCKALL (&filter);
|
|
ICMP6_FILTER_SETPASS (ICMP6_ECHO_REPLY, &filter);
|
|
ICMP6_FILTER_SETPASS (ICMP6_DST_UNREACH, &filter);
|
|
ICMP6_FILTER_SETPASS (ICMP6_PACKET_TOO_BIG, &filter);
|
|
ICMP6_FILTER_SETPASS (ICMP6_TIME_EXCEEDED, &filter);
|
|
ICMP6_FILTER_SETPASS (ICMP6_PARAM_PROB, &filter);
|
|
|
|
err =
|
|
setsockopt (fd, IPPROTO_ICMPV6, ICMP6_FILTER, &filter, sizeof (filter));
|
|
if (err)
|
|
{
|
|
close (fd);
|
|
return NULL;
|
|
}
|
|
|
|
err = setsockopt (fd, IPPROTO_IPV6, IPV6_RECVHOPLIMIT, &on, sizeof (on));
|
|
if (err)
|
|
{
|
|
close (fd);
|
|
return NULL;
|
|
}
|
|
|
|
/* Allocate PING structure and initialize it to default values */
|
|
p = malloc (sizeof (*p));
|
|
if (!p)
|
|
{
|
|
close (fd);
|
|
return NULL;
|
|
}
|
|
|
|
memset (p, 0, sizeof (*p));
|
|
|
|
p->ping_fd = fd;
|
|
p->ping_count = DEFAULT_PING_COUNT;
|
|
p->ping_interval = PING_DEFAULT_INTERVAL;
|
|
p->ping_datalen = sizeof (struct icmp6_hdr);
|
|
/* Make sure we use only 16 bits in this field, id for icmp is a u_short. */
|
|
p->ping_ident = ident & 0xFFFF;
|
|
p->ping_cktab_size = PING_CKTABSIZE;
|
|
return p;
|
|
}
|
|
|
|
static int
|
|
ping_xmit (PING * p)
|
|
{
|
|
int i, buflen;
|
|
struct icmp6_hdr *icmp6;
|
|
|
|
if (_ping_setbuf (p, USE_IPV6))
|
|
return -1;
|
|
|
|
buflen = p->ping_datalen + sizeof (struct icmp6_hdr);
|
|
|
|
/* Mark sequence number as sent */
|
|
_PING_CLR (p, p->ping_num_xmit % p->ping_cktab_size);
|
|
|
|
icmp6 = (struct icmp6_hdr *) p->ping_buffer;
|
|
icmp6->icmp6_type = ICMP6_ECHO_REQUEST;
|
|
icmp6->icmp6_code = 0;
|
|
/* The checksum will be calculated by the TCP/IP stack. */
|
|
icmp6->icmp6_cksum = 0;
|
|
icmp6->icmp6_id = htons (p->ping_ident);
|
|
icmp6->icmp6_seq = htons (p->ping_num_xmit);
|
|
|
|
i = sendto (p->ping_fd, (char *) p->ping_buffer, buflen, 0,
|
|
(struct sockaddr *) &p->ping_dest.ping_sockaddr6, sizeof (p->ping_dest.ping_sockaddr6));
|
|
if (i < 0)
|
|
perror ("ping: sendto");
|
|
else
|
|
{
|
|
p->ping_num_xmit++;
|
|
if (i != buflen)
|
|
printf ("ping: wrote %s %d chars, ret=%d\n",
|
|
p->ping_hostname, buflen, i);
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int
|
|
my_echo_reply (PING * p, struct icmp6_hdr *icmp6)
|
|
{
|
|
struct ip6_hdr *orig_ip = (struct ip6_hdr *) (icmp6 + 1);
|
|
struct icmp6_hdr *orig_icmp = (struct icmp6_hdr *) (orig_ip + 1);
|
|
|
|
return IN6_ARE_ADDR_EQUAL (&orig_ip->ip6_dst, &ping->ping_dest.ping_sockaddr6.sin6_addr)
|
|
&& orig_ip->ip6_nxt == IPPROTO_ICMPV6
|
|
&& orig_icmp->icmp6_type == ICMP6_ECHO_REQUEST
|
|
&& orig_icmp->icmp6_id == htons (p->ping_ident);
|
|
}
|
|
|
|
static int
|
|
ping_recv (PING * p)
|
|
{
|
|
int dupflag, n;
|
|
int hops = -1;
|
|
struct msghdr msg;
|
|
struct iovec iov;
|
|
struct icmp6_hdr *icmp6;
|
|
struct cmsghdr *cmsg;
|
|
char cmsg_data[1024];
|
|
|
|
iov.iov_base = p->ping_buffer;
|
|
iov.iov_len = _PING_BUFLEN (p, USE_IPV6);
|
|
msg.msg_name = &p->ping_from.ping_sockaddr6;
|
|
msg.msg_namelen = sizeof (p->ping_from.ping_sockaddr6);
|
|
msg.msg_iov = &iov;
|
|
msg.msg_iovlen = 1;
|
|
msg.msg_control = cmsg_data;
|
|
msg.msg_controllen = sizeof (cmsg_data);
|
|
msg.msg_flags = 0;
|
|
|
|
n = recvmsg (p->ping_fd, &msg, 0);
|
|
if (n < 0)
|
|
return -1;
|
|
|
|
for (cmsg = CMSG_FIRSTHDR (&msg); cmsg; cmsg = CMSG_NXTHDR (&msg, cmsg))
|
|
{
|
|
if (cmsg->cmsg_level == IPPROTO_IPV6
|
|
&& cmsg->cmsg_type == IPV6_HOPLIMIT)
|
|
{
|
|
hops = *(int *) CMSG_DATA (cmsg);
|
|
break;
|
|
}
|
|
}
|
|
|
|
icmp6 = (struct icmp6_hdr *) p->ping_buffer;
|
|
if (icmp6->icmp6_type == ICMP6_ECHO_REPLY)
|
|
{
|
|
/* We got an echo reply. */
|
|
if (ntohs (icmp6->icmp6_id) != p->ping_ident)
|
|
return -1; /* It's not a response to us. */
|
|
|
|
if (_PING_TST (p, ntohs (icmp6->icmp6_seq) % p->ping_cktab_size))
|
|
{
|
|
/* We already got the reply for this echo request. */
|
|
p->ping_num_rept++;
|
|
dupflag = 1;
|
|
}
|
|
else
|
|
{
|
|
_PING_SET (p, ntohs (icmp6->icmp6_seq) % p->ping_cktab_size);
|
|
p->ping_num_recv++;
|
|
dupflag = 0;
|
|
}
|
|
|
|
print_echo (dupflag, hops, p->ping_closure, &p->ping_dest.ping_sockaddr6,
|
|
&p->ping_from.ping_sockaddr6, icmp6, n);
|
|
|
|
}
|
|
else
|
|
{
|
|
/* We got an error reply. */
|
|
if (!my_echo_reply (p, icmp6))
|
|
return -1; /* It's not for us. */
|
|
|
|
print_icmp_error (&p->ping_from.ping_sockaddr6, icmp6, n);
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int
|
|
ping_set_dest (PING * ping, char *host)
|
|
{
|
|
int err;
|
|
struct addrinfo *result, hints;
|
|
|
|
memset (&hints, 0, sizeof (hints));
|
|
|
|
hints.ai_family = AF_INET6;
|
|
|
|
err = getaddrinfo (host, NULL, &hints, &result);
|
|
if (err)
|
|
return 1;
|
|
|
|
memcpy (&ping->ping_dest.ping_sockaddr6, result->ai_addr, result->ai_addrlen);
|
|
|
|
freeaddrinfo (result);
|
|
|
|
ping->ping_hostname = strdup (host);
|
|
if (!ping->ping_hostname)
|
|
return 1;
|
|
|
|
return 0;
|
|
}
|