mirror of
https://git.savannah.gnu.org/git/inetutils.git
synced 2026-01-12 00:19:39 +08:00
642 lines
16 KiB
C
642 lines
16 KiB
C
/*
|
|
Copyright (C) 2001-2025 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 of the License, 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 A 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 this program. If not, see `http://www.gnu.org/licenses/'. */
|
|
|
|
#include <config.h>
|
|
|
|
#include <sys/param.h>
|
|
#include <sys/socket.h>
|
|
#include <sys/file.h>
|
|
#include <sys/time.h>
|
|
|
|
#include <netinet/in.h>
|
|
#include <arpa/inet.h>
|
|
|
|
/*#include <netinet/ip_icmp.h> -- deliberately not including this */
|
|
#ifdef HAVE_NETINET_IP_VAR_H
|
|
# include <netinet/ip_var.h>
|
|
#endif
|
|
|
|
#include <netdb.h>
|
|
#include <unistd.h>
|
|
#include <stdlib.h>
|
|
#include <string.h>
|
|
#include <stdio.h>
|
|
#include <ctype.h>
|
|
#include <errno.h>
|
|
#include <limits.h>
|
|
#include <attribute.h>
|
|
#include <timespec.h>
|
|
|
|
#include <ping.h>
|
|
#include "ping_impl.h"
|
|
|
|
#define NROUTES 9 /* number of record route slots */
|
|
#ifndef MAX_IPOPTLEN
|
|
# define MAX_IPOPTLEN 40
|
|
#endif
|
|
|
|
#include <xalloc.h>
|
|
|
|
static int handler (int code, void *closure,
|
|
struct sockaddr_in *dest, struct sockaddr_in *from,
|
|
struct ip *ip, icmphdr_t * icmp, int datalen);
|
|
int print_echo (int dup, struct ping_stat *stat,
|
|
struct sockaddr_in *dest, struct sockaddr_in *from,
|
|
struct ip *ip, icmphdr_t * icmp, int datalen);
|
|
static int echo_finish (void);
|
|
|
|
void print_icmp_header (struct sockaddr_in *from,
|
|
struct ip *ip, icmphdr_t * icmp, int len);
|
|
static void print_ip_opt (struct ip *ip, int hlen);
|
|
|
|
int
|
|
ping_echo (char *hostname)
|
|
{
|
|
#ifdef IP_OPTIONS
|
|
char rspace[MAX_IPOPTLEN]; /* Maximal IP option space. */
|
|
#endif
|
|
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_set_type (ping, ICMP_ECHO);
|
|
ping_set_packetsize (ping, data_length);
|
|
ping_set_event_handler (ping, handler, &ping_stat);
|
|
|
|
if (ping_set_dest (ping, hostname))
|
|
error (EXIT_FAILURE, 0, "unknown host");
|
|
|
|
if (options & OPT_RROUTE)
|
|
{
|
|
#ifdef IP_OPTIONS
|
|
memset (rspace, 0, sizeof (rspace));
|
|
rspace[IPOPT_OPTVAL] = IPOPT_RR;
|
|
rspace[IPOPT_OLEN] = sizeof (rspace) - 1;
|
|
rspace[IPOPT_OFFSET] = IPOPT_MINOFF;
|
|
if (setsockopt (ping->ping_fd, IPPROTO_IP,
|
|
IP_OPTIONS, rspace, sizeof (rspace)) < 0)
|
|
error (EXIT_FAILURE, errno, "setsockopt");
|
|
#else
|
|
error (EXIT_FAILURE, 0, "record route not available in this "
|
|
"implementation.");
|
|
#endif /* IP_OPTIONS */
|
|
}
|
|
else if (options & OPT_IPTIMESTAMP)
|
|
{
|
|
int type;
|
|
|
|
if (suboptions & SOPT_TSPRESPEC)
|
|
#ifdef IPOPT_TS_PRESPEC_RFC791
|
|
type = IPOPT_TS_PRESPEC_RFC791;
|
|
#else
|
|
type = IPOPT_TS_PRESPEC;
|
|
#endif
|
|
else if (suboptions & SOPT_TSADDR)
|
|
type = IPOPT_TS_TSANDADDR;
|
|
else
|
|
type = IPOPT_TS_TSONLY;
|
|
|
|
#ifdef IP_OPTIONS
|
|
memset (rspace, 0, sizeof (rspace));
|
|
rspace[IPOPT_OPTVAL] = IPOPT_TS;
|
|
rspace[IPOPT_OLEN] = sizeof (rspace);
|
|
if (type != IPOPT_TS_TSONLY)
|
|
rspace[IPOPT_OLEN] -= sizeof (n_time); /* Exsessive part. */
|
|
rspace[IPOPT_OFFSET] = IPOPT_MINOFF + 1;
|
|
|
|
# ifdef IPOPT_POS_OV_FLG
|
|
rspace[IPOPT_POS_OV_FLG] = type;
|
|
# else
|
|
rspace[3] = type;
|
|
# endif/* !IPOPT_POS_OV_FLG */
|
|
|
|
if (setsockopt (ping->ping_fd, IPPROTO_IP,
|
|
IP_OPTIONS, rspace, rspace[IPOPT_OLEN]) < 0)
|
|
error (EXIT_FAILURE, errno, "setsockopt");
|
|
#else /* !IP_OPTIONS */
|
|
error (EXIT_FAILURE, 0, "IP timestamp not available in this "
|
|
"implementation.");
|
|
#endif /* IP_OPTIONS */
|
|
}
|
|
|
|
printf ("PING %s (%s): %zu data bytes",
|
|
ping->ping_hostname,
|
|
inet_ntoa (ping->ping_dest.ping_sockaddr.sin_addr), data_length);
|
|
if (options & OPT_VERBOSE)
|
|
printf (", id 0x%04x = %u", ping->ping_ident, ping->ping_ident);
|
|
|
|
printf ("\n");
|
|
|
|
status = ping_run (ping, echo_finish);
|
|
free (ping->ping_hostname);
|
|
return status;
|
|
}
|
|
|
|
int
|
|
handler (int code, void *closure,
|
|
struct sockaddr_in *dest, struct sockaddr_in *from,
|
|
struct ip *ip, icmphdr_t *icmp, int datalen)
|
|
{
|
|
switch (code)
|
|
{
|
|
case PEV_RESPONSE:
|
|
case PEV_DUPLICATE:
|
|
print_echo (code == PEV_DUPLICATE,
|
|
(struct ping_stat *) closure, dest, from, ip, icmp,
|
|
datalen);
|
|
break;
|
|
case PEV_NOECHO:;
|
|
print_icmp_header (from, ip, icmp, datalen);
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
int
|
|
print_echo (int dupflag, struct ping_stat *ping_stat,
|
|
struct sockaddr_in *dest MAYBE_UNUSED,
|
|
struct sockaddr_in *from,
|
|
struct ip *ip, icmphdr_t *icmp, int datalen)
|
|
{
|
|
int hlen;
|
|
bool timing = false;
|
|
double triptime = 0.0;
|
|
|
|
/* Length of IP header */
|
|
hlen = ip->ip_hl << 2;
|
|
|
|
/* Length of ICMP header+payload */
|
|
datalen -= hlen;
|
|
|
|
/* Do timing */
|
|
if (PING_TIMING (datalen - PING_HEADER_LEN))
|
|
{
|
|
struct timeval tv;
|
|
struct timespec ts;
|
|
|
|
timing = true;
|
|
|
|
/* Avoid unaligned data. */
|
|
memcpy (&tv, icmp->icmp_data, sizeof (tv));
|
|
/* *INDENT-OFF* */
|
|
ts = timespec_sub (current_timespec (),
|
|
(struct timespec) { .tv_sec = tv.tv_sec,
|
|
.tv_nsec = tv.tv_usec * 1000 });
|
|
/* *INDENT-ON* */
|
|
|
|
triptime = timespectod (ts) * 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;
|
|
}
|
|
|
|
printf ("%d bytes from %s: icmp_seq=%u", datalen,
|
|
inet_ntoa (*(struct in_addr *) &from->sin_addr.s_addr),
|
|
ntohs (icmp->icmp_seq));
|
|
printf (" ttl=%d", ip->ip_ttl);
|
|
if (timing)
|
|
printf (" time=%.3f ms", triptime);
|
|
if (dupflag)
|
|
printf (" (DUP!)");
|
|
|
|
print_ip_opt (ip, hlen);
|
|
printf ("\n");
|
|
|
|
return 0;
|
|
}
|
|
|
|
#define NITEMS(a) sizeof(a)/sizeof((a)[0])
|
|
|
|
struct icmp_diag
|
|
{
|
|
int type;
|
|
char *text;
|
|
void (*fun) (icmphdr_t *, void *data);
|
|
void *data;
|
|
};
|
|
|
|
struct icmp_code_descr
|
|
{
|
|
int type;
|
|
int code;
|
|
char *diag;
|
|
} icmp_code_descr[] = {
|
|
{ICMP_DEST_UNREACH, ICMP_NET_UNREACH, "Destination Net Unreachable"},
|
|
{ICMP_DEST_UNREACH, ICMP_HOST_UNREACH, "Destination Host Unreachable"},
|
|
{ICMP_DEST_UNREACH, ICMP_PROT_UNREACH, "Destination Protocol Unreachable"},
|
|
{ICMP_DEST_UNREACH, ICMP_PORT_UNREACH, "Destination Port Unreachable"},
|
|
{ICMP_DEST_UNREACH, ICMP_FRAG_NEEDED, "Fragmentation needed and DF set"},
|
|
{ICMP_DEST_UNREACH, ICMP_SR_FAILED, "Source Route Failed"},
|
|
{ICMP_DEST_UNREACH, ICMP_NET_UNKNOWN, "Network Unknown"},
|
|
{ICMP_DEST_UNREACH, ICMP_HOST_UNKNOWN, "Host Unknown"},
|
|
{ICMP_DEST_UNREACH, ICMP_HOST_ISOLATED, "Host Isolated"},
|
|
{ICMP_DEST_UNREACH, ICMP_NET_UNR_TOS,
|
|
"Destination Network Unreachable At This TOS"},
|
|
{ICMP_DEST_UNREACH, ICMP_HOST_UNR_TOS,
|
|
"Destination Host Unreachable At This TOS"},
|
|
#ifdef ICMP_PKT_FILTERED
|
|
{ICMP_DEST_UNREACH, ICMP_PKT_FILTERED, "Packet Filtered"},
|
|
#endif
|
|
#ifdef ICMP_PREC_VIOLATION
|
|
{ICMP_DEST_UNREACH, ICMP_PREC_VIOLATION, "Precedence Violation"},
|
|
#endif
|
|
#ifdef ICMP_PREC_CUTOFF
|
|
{ICMP_DEST_UNREACH, ICMP_PREC_CUTOFF, "Precedence Cutoff"},
|
|
#endif
|
|
{ICMP_REDIRECT, ICMP_REDIR_NET, "Redirect Network"},
|
|
{ICMP_REDIRECT, ICMP_REDIR_HOST, "Redirect Host"},
|
|
{ICMP_REDIRECT, ICMP_REDIR_NETTOS, "Redirect Type of Service and Network"},
|
|
{ICMP_REDIRECT, ICMP_REDIR_HOSTTOS, "Redirect Type of Service and Host"},
|
|
{ICMP_TIME_EXCEEDED, ICMP_EXC_TTL, "Time to live exceeded"},
|
|
{ICMP_TIME_EXCEEDED, ICMP_EXC_FRAGTIME, "Frag reassembly time exceeded"}
|
|
};
|
|
|
|
static void
|
|
print_icmp_code (int type, int code, char *prefix)
|
|
{
|
|
struct icmp_code_descr *p;
|
|
|
|
for (p = icmp_code_descr; p < icmp_code_descr + NITEMS (icmp_code_descr);
|
|
p++)
|
|
if (p->type == type && p->code == code)
|
|
{
|
|
printf ("%s\n", p->diag);
|
|
return;
|
|
}
|
|
|
|
printf ("%s, Unknown Code: %d\n", prefix, code);
|
|
}
|
|
|
|
static void
|
|
print_ip_header (struct ip *ip)
|
|
{
|
|
size_t hlen;
|
|
unsigned char *cp;
|
|
|
|
hlen = ip->ip_hl << 2;
|
|
cp = (unsigned char *) ip + sizeof (*ip); /* point to options */
|
|
|
|
if (options & OPT_VERBOSE)
|
|
{
|
|
size_t j;
|
|
|
|
printf ("IP Hdr Dump:\n ");
|
|
for (j = 0; j < sizeof (*ip); ++j)
|
|
printf ("%02x%s", *((unsigned char *) ip + j), (j % 2) ? " " : ""); /* Group bytes two by two. */
|
|
printf ("\n");
|
|
}
|
|
|
|
printf ("Vr HL TOS Len ID Flg off TTL Pro cks Src\tDst\tData\n");
|
|
printf (" %1x %1x %02x", ip->ip_v, ip->ip_hl, ip->ip_tos);
|
|
/*
|
|
* The member `ip_len' is not portably reported in any byte order.
|
|
* Use a simple heuristic to print a reasonable value.
|
|
*/
|
|
printf (" %04x %04x",
|
|
(ip->ip_len > 0x2000) ? ntohs (ip->ip_len) : ip->ip_len,
|
|
ntohs (ip->ip_id));
|
|
printf (" %1x %04x", (ntohs (ip->ip_off) & 0xe000) >> 13,
|
|
ntohs (ip->ip_off) & 0x1fff);
|
|
printf (" %02x %02x %04x", ip->ip_ttl, ip->ip_p, ntohs (ip->ip_sum));
|
|
printf (" %s ", inet_ntoa (*((struct in_addr *) &ip->ip_src)));
|
|
printf (" %s ", inet_ntoa (*((struct in_addr *) &ip->ip_dst)));
|
|
while (hlen-- > sizeof (*ip))
|
|
printf ("%02x", *cp++);
|
|
|
|
printf ("\n");
|
|
}
|
|
|
|
void
|
|
print_ip_data (icmphdr_t *icmp, void *data MAYBE_UNUSED)
|
|
{
|
|
int hlen;
|
|
unsigned char *cp;
|
|
struct ip *ip = &icmp->icmp_ip;
|
|
|
|
print_ip_header (ip);
|
|
|
|
hlen = ip->ip_hl << 2;
|
|
cp = (unsigned char *) ip + hlen;
|
|
|
|
if (ip->ip_p == IPPROTO_TCP)
|
|
printf ("TCP: from port %u, to port %u (decimal)\n",
|
|
(*cp * 256 + *(cp + 1)), (*(cp + 2) * 256 + *(cp + 3)));
|
|
else if (ip->ip_p == IPPROTO_UDP)
|
|
printf ("UDP: from port %u, to port %u (decimal)\n",
|
|
(*cp * 256 + *(cp + 1)), (*(cp + 2) * 256 + *(cp + 3)));
|
|
else if (ip->ip_p == IPPROTO_ICMP)
|
|
{
|
|
int type = *cp;
|
|
int code = *(cp + 1);
|
|
|
|
printf ("ICMP: type %u, code %u, size %u", type, code,
|
|
ntohs (ip->ip_len) - hlen);
|
|
if (type == ICMP_ECHOREPLY || type == ICMP_ECHO)
|
|
printf (", id 0x%04x, seq 0x%04x", *(cp + 4) * 256 + *(cp + 5),
|
|
*(cp + 6) * 256 + *(cp + 7));
|
|
printf ("\n");
|
|
}
|
|
}
|
|
|
|
static void
|
|
print_icmp (icmphdr_t *icmp, void *data)
|
|
{
|
|
print_icmp_code (icmp->icmp_type, icmp->icmp_code, data);
|
|
if (options & OPT_VERBOSE)
|
|
print_ip_data (icmp, NULL);
|
|
}
|
|
|
|
static void
|
|
print_parameterprob (icmphdr_t *icmp, void *data)
|
|
{
|
|
printf ("Parameter problem: IP address = %s\n",
|
|
inet_ntoa (icmp->icmp_gwaddr));
|
|
print_ip_data (icmp, data);
|
|
}
|
|
|
|
struct icmp_diag icmp_diag[] = {
|
|
{ICMP_ECHOREPLY, "Echo Reply", NULL, NULL},
|
|
{ICMP_DEST_UNREACH, NULL, print_icmp, "Dest Unreachable"},
|
|
{ICMP_SOURCE_QUENCH, "Source Quench", print_ip_data, NULL},
|
|
{ICMP_REDIRECT, NULL, print_icmp, "Redirect"},
|
|
{ICMP_ECHO, "Echo Request", NULL, NULL},
|
|
{ICMP_ROUTERADV, "Router Advertisement", NULL, NULL},
|
|
{ICMP_ROUTERDISCOVERY, "Router Discovery", NULL, NULL},
|
|
{ICMP_TIME_EXCEEDED, NULL, print_icmp, "Time exceeded"},
|
|
{ICMP_PARAMETERPROB, NULL, print_parameterprob, NULL},
|
|
{ICMP_TIMESTAMP, "Timestamp", NULL, NULL},
|
|
{ICMP_TIMESTAMPREPLY, "Timestamp Reply", NULL, NULL},
|
|
{ICMP_INFO_REQUEST, "Information Request", NULL, NULL},
|
|
{ICMP_INFO_REPLY, "Information Reply", NULL, NULL},
|
|
{ICMP_ADDRESS, "Address Mask Request", NULL, NULL},
|
|
{ICMP_ADDRESSREPLY, "Address Mask Reply", NULL, NULL},
|
|
};
|
|
|
|
void
|
|
print_icmp_header (struct sockaddr_in *from,
|
|
struct ip *ip, icmphdr_t *icmp, int len)
|
|
{
|
|
int hlen;
|
|
struct ip *orig_ip;
|
|
char *s;
|
|
struct icmp_diag *p;
|
|
|
|
/* Length of the IP header */
|
|
hlen = ip->ip_hl << 2;
|
|
/* Original IP header */
|
|
orig_ip = &icmp->icmp_ip;
|
|
|
|
if (!(options & OPT_VERBOSE
|
|
|| orig_ip->ip_dst.s_addr ==
|
|
ping->ping_dest.ping_sockaddr.sin_addr.s_addr))
|
|
return;
|
|
|
|
s = ipaddr2str ((struct sockaddr *) from, sizeof (*from));
|
|
printf ("%d bytes from %s: ", len - hlen, s);
|
|
free (s);
|
|
|
|
for (p = icmp_diag; p < icmp_diag + NITEMS (icmp_diag); p++)
|
|
{
|
|
if (p->type == icmp->icmp_type)
|
|
{
|
|
if (p->text)
|
|
printf ("%s\n", p->text);
|
|
if (p->fun)
|
|
p->fun (icmp, p->data);
|
|
return;
|
|
}
|
|
}
|
|
printf ("Bad ICMP type: %d\n", icmp->icmp_type);
|
|
}
|
|
|
|
void
|
|
print_ip_opt (struct ip *ip, int hlen)
|
|
{
|
|
unsigned char *cp;
|
|
int i, j, k, l;
|
|
static int old_rrlen;
|
|
static char old_rr[MAX_IPOPTLEN];
|
|
|
|
cp = (unsigned char *) (ip + 1);
|
|
|
|
for (; hlen > (int) sizeof (struct ip); --hlen, ++cp)
|
|
switch (*cp)
|
|
{
|
|
case IPOPT_EOL:
|
|
hlen = 0;
|
|
break;
|
|
|
|
case IPOPT_LSRR:
|
|
case IPOPT_SSRR:
|
|
printf ("\n%cSRR: ", (*cp == IPOPT_LSRR) ? 'L' : 'S');
|
|
hlen -= 2;
|
|
j = *++cp;
|
|
++cp;
|
|
if (j > IPOPT_MINOFF)
|
|
for (;;)
|
|
{
|
|
/* Fetch in network byte order, calculate as host. */
|
|
l = *++cp;
|
|
l = (l << 8) + *++cp;
|
|
l = (l << 8) + *++cp;
|
|
l = (l << 8) + *++cp;
|
|
if (l == 0)
|
|
{
|
|
printf ("\t0.0.0.0");
|
|
}
|
|
else
|
|
{
|
|
struct in_addr ina;
|
|
char *s;
|
|
|
|
ina.s_addr = htonl (l);
|
|
printf ("\t%s", s = sinaddr2str (ina));
|
|
free (s);
|
|
}
|
|
hlen -= 4;
|
|
j -= 4;
|
|
if (j <= IPOPT_MINOFF)
|
|
break;
|
|
putchar ('\n');
|
|
}
|
|
break;
|
|
|
|
case IPOPT_RR:
|
|
j = *++cp;
|
|
i = *++cp;
|
|
hlen -= 2;
|
|
if (i > j)
|
|
i = j;
|
|
i -= IPOPT_MINOFF;
|
|
if (i <= 0)
|
|
break;
|
|
if (i == old_rrlen
|
|
&& cp == (unsigned char *) (ip + 1) + 2
|
|
&& !memcmp ((char *) cp, old_rr, i) && !(options & OPT_FLOOD))
|
|
{
|
|
printf ("\t (same route)");
|
|
i = ((i + 3) / 4) * 4;
|
|
hlen -= i;
|
|
cp += i;
|
|
break;
|
|
}
|
|
if (i < MAX_IPOPTLEN)
|
|
{
|
|
old_rrlen = i;
|
|
memmove (old_rr, cp, i);
|
|
}
|
|
else
|
|
old_rrlen = 0;
|
|
|
|
printf ("\nRR: ");
|
|
j = 0;
|
|
for (;;)
|
|
{
|
|
/* Fetch in network byte order, calculate as host. */
|
|
l = *++cp;
|
|
l = (l << 8) + *++cp;
|
|
l = (l << 8) + *++cp;
|
|
l = (l << 8) + *++cp;
|
|
if (l == 0)
|
|
{
|
|
printf ("\t0.0.0.0");
|
|
}
|
|
else
|
|
{
|
|
struct in_addr ina;
|
|
char *s;
|
|
|
|
ina.s_addr = htonl (l);
|
|
printf ("\t%s", s = sinaddr2str (ina));
|
|
free (s);
|
|
}
|
|
hlen -= 4;
|
|
i -= 4;
|
|
j += 4;
|
|
if (i <= 0)
|
|
break;
|
|
if (j >= MAX_IPOPTLEN)
|
|
{
|
|
printf ("\t (truncated route)");
|
|
break;
|
|
}
|
|
putchar ('\n');
|
|
}
|
|
break;
|
|
|
|
case IPOPT_TS:
|
|
j = *++cp; /* len */
|
|
i = *++cp; /* ptr */
|
|
hlen -= 2;
|
|
if (i > j)
|
|
i = j;
|
|
|
|
/* Check minimal sizing. */
|
|
if (j <= (int) (IPOPT_MINOFF + sizeof (n_time)))
|
|
break;
|
|
|
|
k = *++cp; /* OV, FL */
|
|
++cp; /* Points at first content. */
|
|
hlen -= 2;
|
|
|
|
printf ("\nTS:");
|
|
j = 5; /* First possible slot. */
|
|
for (;;)
|
|
{
|
|
char timestr[16];
|
|
|
|
if ((k & 0x0f) != IPOPT_TS_TSONLY && ((j / 4) % 2 == 1)) /* find 5, 13, 21, 29 */
|
|
{
|
|
/* IP addresses */
|
|
struct in_addr ina;
|
|
char *s;
|
|
|
|
ina.s_addr = *((in_addr_t *) cp);
|
|
printf ("\t%s", s = sinaddr2str (ina));
|
|
free (s);
|
|
|
|
hlen -= sizeof (in_addr_t);
|
|
cp += sizeof (in_addr_t);
|
|
j += sizeof (in_addr_t);
|
|
}
|
|
else
|
|
{
|
|
/* Timestamps */
|
|
printf ("\t%s ms",
|
|
ping_cvt_time (timestr, sizeof (timestr),
|
|
ntohl (*(n_time *) cp)));
|
|
if (options & OPT_VERBOSE)
|
|
printf (" = 0x%08x", ntohl (*(n_time *) cp));
|
|
|
|
hlen -= sizeof (n_time);
|
|
cp += sizeof (n_time);
|
|
j += sizeof (n_time);
|
|
|
|
putchar ('\n');
|
|
}
|
|
|
|
if (j >= i)
|
|
break;
|
|
}
|
|
|
|
if (k & 0xf0)
|
|
printf ("\t(%u overflowing hosts)", k >> 4);
|
|
break;
|
|
|
|
case IPOPT_NOP:
|
|
printf ("\nNOP");
|
|
break;
|
|
|
|
default:
|
|
printf ("\nunknown option %x", *cp);
|
|
break;
|
|
}
|
|
}
|
|
|
|
int
|
|
echo_finish (void)
|
|
{
|
|
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);
|
|
}
|