summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorLorenzo Colitti <lorenzo@google.com>2013-06-28 13:06:49 +0900
committerElliott Hughes <enh@google.com>2013-07-08 10:38:08 -0700
commite5ed2def5304e4142e65c1ceb3fb763e39822132 (patch)
treeb02895958736669c009f64800d0d0467aeebedbc
parent84b590fe10fda51eca755bc65a8eb00bd29ce702 (diff)
downloadping-e5ed2def5304e4142e65c1ceb3fb763e39822132.tar.gz
Update ping from 020927 to the latest s20121221.
Our current copy of ping is based on the 2002 snapshot plus a few local modifications, mostly to get it to compile on Android and to allow permissionless ping using the ping socket. This patch preserves the local modifications (except where they are obsolete) by applying the diff between 020927 and s2012221 to our local copy. As expected, diffing our copy with s2012221 shows our local modifications. This patch also includes the ping6 source code, but no attempt is made to compile it yet (more changes are needed for that). Tested on crespo using ToT master. As expected, "adb shell ping" works when the shell is not running as root and /system/bin/ping is not suid. (cherry-pick of b933d68e8e35f0a48ef826dda1afb0ce2890d6ab.) Bug: 9469682 Change-Id: I0cad3c38c449b3ab2e791350458511125ffbca94
-rw-r--r--Android.mk1
-rw-r--r--SNAPSHOT.h2
-rw-r--r--ping.c318
-rw-r--r--ping6.c1861
-rw-r--r--ping6_niquery.h49
-rw-r--r--ping_common.c322
-rw-r--r--ping_common.h113
7 files changed, 2529 insertions, 137 deletions
diff --git a/Android.mk b/Android.mk
index 5eaa892..6f72dd4 100644
--- a/Android.mk
+++ b/Android.mk
@@ -3,4 +3,5 @@ LOCAL_PATH:= $(call my-dir)
include $(CLEAR_VARS)
LOCAL_SRC_FILES:= ping.c ping_common.c
LOCAL_MODULE := ping
+LOCAL_CFLAGS := -DWITHOUT_IFADDRS
include $(BUILD_EXECUTABLE)
diff --git a/SNAPSHOT.h b/SNAPSHOT.h
index 6fc6b9c..702ccd3 100644
--- a/SNAPSHOT.h
+++ b/SNAPSHOT.h
@@ -1 +1 @@
-static char SNAPSHOT[] = "020927";
+static char SNAPSHOT[] = "s20121221";
diff --git a/ping.c b/ping.c
index 2b41f44..25bbc3c 100644
--- a/ping.c
+++ b/ping.c
@@ -62,6 +62,16 @@ char copyright[] =
#include <netinet/ip.h>
#include <netinet/ip_icmp.h>
+#ifndef WITHOUT_IFADDRS
+#include <ifaddrs.h>
+#endif
+
+#ifndef ICMP_FILTER
+#define ICMP_FILTER 1
+struct icmp_filter {
+ __u32 data;
+};
+#endif
#ifdef ANDROID
#include <sys/auxv.h>
@@ -72,6 +82,7 @@ char copyright[] =
#define MAXICMPLEN 76
#define NROUTES 9 /* number of record route slots */
#define TOS_MAX 255 /* 8-bit TOS field */
+#define MAX_HOSTNAMELEN NI_MAXHOST
static int ts_type;
@@ -116,7 +127,12 @@ main(int argc, char **argv)
int ch, hold, packlen;
int socket_errno;
u_char *packet;
- char *target, hnamebuf[MAXHOSTNAMELEN];
+ char *target;
+#ifdef USE_IDN
+ char *hnamebuf = NULL;
+#else
+ char hnamebuf[MAX_HOSTNAMELEN];
+#endif
char rspace[3 + 4 * NROUTES + 1]; /* record route space */
#ifdef ANDROID
@@ -126,6 +142,14 @@ main(int argc, char **argv)
}
#endif
+ limit_capabilities();
+
+#ifdef USE_IDN
+ setlocale(LC_ALL, "");
+#endif
+
+ enable_capability_raw();
+
icmp_sock = socket(AF_INET, SOCK_DGRAM, IPPROTO_ICMP);
if (icmp_sock != -1)
using_ping_socket = 1;
@@ -133,10 +157,7 @@ main(int argc, char **argv)
icmp_sock = socket(AF_INET, SOCK_RAW, IPPROTO_ICMP);
socket_errno = errno;
- uid = getuid();
-#ifndef ANDROID
- setuid(uid);
-#endif
+ disable_capability_raw();
source.sin_family = AF_INET;
@@ -144,7 +165,7 @@ main(int argc, char **argv)
while ((ch = getopt(argc, argv, COMMON_OPTSTR "bRT:")) != EOF) {
switch(ch) {
case 'b':
- broadcast_pings = 1;
+ broadcast_pings = 1;
break;
case 'Q':
settos = parsetos(optarg);
@@ -181,6 +202,7 @@ main(int argc, char **argv)
break;
case 'I':
{
+#if 0
char dummy;
int i1, i2, i3, i4;
@@ -196,6 +218,12 @@ main(int argc, char **argv)
} else {
device = optarg;
}
+#else
+ if (inet_pton(AF_INET, optarg, &source.sin_addr) > 0)
+ options |= F_STRICTSOURCE;
+ else
+ device = optarg;
+#endif
break;
}
case 'M':
@@ -211,7 +239,7 @@ main(int argc, char **argv)
}
break;
case 'V':
- printf("ping utility, iputils-ss%s\n", SNAPSHOT);
+ printf("ping utility, iputils-%s\n", SNAPSHOT);
exit(0);
COMMON_OPTIONS
common_options(ch);
@@ -223,7 +251,7 @@ main(int argc, char **argv)
argc -= optind;
argv += optind;
- if (argc == 0)
+ if (argc == 0)
usage();
if (argc > 1) {
if (options & F_RROUTE)
@@ -242,21 +270,51 @@ main(int argc, char **argv)
while (argc > 0) {
target = *argv;
- bzero((char *)&whereto, sizeof(whereto));
+ memset((char *)&whereto, 0, sizeof(whereto));
whereto.sin_family = AF_INET;
if (inet_aton(target, &whereto.sin_addr) == 1) {
hostname = target;
if (argc == 1)
options |= F_NUMERIC;
} else {
- hp = gethostbyname(target);
+ char *idn;
+#ifdef USE_IDN
+ int rc;
+
+ if (hnamebuf) {
+ free(hnamebuf);
+ hnamebuf = NULL;
+ }
+
+ rc = idna_to_ascii_lz(target, &idn, 0);
+ if (rc != IDNA_SUCCESS) {
+ fprintf(stderr, "ping: IDN encoding failed: %s\n", idna_strerror(rc));
+ exit(2);
+ }
+#else
+ idn = target;
+#endif
+ hp = gethostbyname(idn);
if (!hp) {
fprintf(stderr, "ping: unknown host %s\n", target);
exit(2);
}
+#ifdef USE_IDN
+ free(idn);
+#endif
memcpy(&whereto.sin_addr, hp->h_addr, 4);
+#ifdef USE_IDN
+ if (idna_to_unicode_lzlz(hp->h_name, &hnamebuf, 0) != IDNA_SUCCESS) {
+ hnamebuf = strdup(hp->h_name);
+ if (!hnamebuf) {
+ perror("ping: strdup");
+ exit(-1);
+ }
+ }
+#else
strncpy(hnamebuf, hp->h_name, sizeof(hnamebuf) - 1);
hnamebuf[sizeof(hnamebuf) - 1] = 0;
+#endif
hostname = hnamebuf;
}
if (argc > 1)
@@ -266,7 +324,7 @@ main(int argc, char **argv)
}
if (source.sin_addr.s_addr == 0) {
- int alen;
+ socklen_t alen;
struct sockaddr_in dst = whereto;
int probe_fd = socket(AF_INET, SOCK_DGRAM, 0);
@@ -276,9 +334,16 @@ main(int argc, char **argv)
}
if (device) {
struct ifreq ifr;
+ int rc;
+
memset(&ifr, 0, sizeof(ifr));
strncpy(ifr.ifr_name, device, IFNAMSIZ-1);
- if (setsockopt(probe_fd, SOL_SOCKET, SO_BINDTODEVICE, device, strlen(device)+1) == -1) {
+
+ enable_capability_raw();
+ rc = setsockopt(probe_fd, SOL_SOCKET, SO_BINDTODEVICE, device, strlen(device)+1);
+ disable_capability_raw();
+
+ if (rc == -1) {
if (IN_MULTICAST(ntohl(dst.sin_addr.s_addr))) {
struct ip_mreqn imr;
if (ioctl(probe_fd, SIOCGIFINDEX, &ifr) < 0) {
@@ -291,6 +356,9 @@ main(int argc, char **argv)
perror("ping: IP_MULTICAST_IF");
exit(2);
}
+ } else {
+ perror("ping: SO_BINDTODEVICE");
+ exit(2);
}
}
}
@@ -329,6 +397,30 @@ main(int argc, char **argv)
exit(2);
}
source.sin_port = 0;
+
+#ifndef WITHOUT_IFADDRS
+ if (device) {
+ struct ifaddrs *ifa0, *ifa;
+ int ret;
+
+ ret = getifaddrs(&ifa0);
+ if (ret) {
+ fprintf(stderr, "gatifaddrs() failed.\n");
+ exit(2);
+ }
+ for (ifa = ifa0; ifa; ifa = ifa->ifa_next) {
+ if (!ifa->ifa_addr || ifa->ifa_addr->sa_family != AF_INET)
+ continue;
+ if (!strncmp(ifa->ifa_name, device, sizeof(device) - 1) &&
+ !memcmp(&((struct sockaddr_in *)ifa->ifa_addr)->sin_addr,
+ &source.sin_addr, sizeof(source.sin_addr)))
+ break;
+ }
+ freeifaddrs(ifa0);
+ if (!ifa)
+ fprintf(stderr, "ping: Warning: source address might be selected on device other than %s.\n", device);
+ }
+#endif
close(probe_fd);
} while (0);
@@ -428,7 +520,7 @@ main(int argc, char **argv)
/* record route option */
if (options & F_RROUTE) {
- bzero(rspace, sizeof(rspace));
+ memset(rspace, 0, sizeof(rspace));
rspace[0] = IPOPT_NOP;
rspace[1+IPOPT_OPTVAL] = IPOPT_RR;
rspace[1+IPOPT_OLEN] = sizeof(rspace)-1;
@@ -440,7 +532,7 @@ main(int argc, char **argv)
}
}
if (options & F_TIMESTAMP) {
- bzero(rspace, sizeof(rspace));
+ memset(rspace, 0, sizeof(rspace));
rspace[0] = IPOPT_TIMESTAMP;
rspace[1] = (ts_type==IPOPT_TS_TSONLY ? 40 : 36);
rspace[2] = 5;
@@ -461,8 +553,8 @@ main(int argc, char **argv)
optlen = 40;
}
if (options & F_SOURCEROUTE) {
- int i;
- bzero(rspace, sizeof(rspace));
+ int i;
+ memset(rspace, 0, sizeof(rspace));
rspace[0] = IPOPT_NOOP;
rspace[1+IPOPT_OPTVAL] = (options & F_SO_DONTROUTE) ? IPOPT_SSRR
: IPOPT_LSRR;
@@ -470,7 +562,7 @@ main(int argc, char **argv)
rspace[1+IPOPT_OFFSET] = IPOPT_MINOFF;
for (i=0; i<nroute; i++)
*(__u32*)&rspace[4+i*4] = route[i];
-
+
if (setsockopt(icmp_sock, IPPROTO_IP, IP_OPTIONS, rspace, 4 + nroute*4) < 0) {
perror("ping: record route");
exit(2);
@@ -490,7 +582,7 @@ main(int argc, char **argv)
perror ("ping: can't set broadcasting");
exit(2);
}
- }
+ }
if (options & F_NOLOOP) {
int loop = 0;
@@ -585,7 +677,7 @@ int receive_error_msg()
if (options & F_QUIET)
goto out;
if (options & F_FLOOD)
- write(STDOUT_FILENO, "E", 1);
+ write_stdout("E", 1);
else if (e->ee_errno != EMSGSIZE)
fprintf(stderr, "ping: local error: %s\n", strerror(e->ee_errno));
else
@@ -630,8 +722,9 @@ int receive_error_msg()
goto out;
if (options & F_FLOOD) {
if (error_pkt)
- write(STDOUT_FILENO, "\bE", 2);
+ write_stdout("\bE", 2);
} else {
+ print_timestamp();
printf("From %s: icmp_seq=%u ", pr_addr(sin->sin_addr.s_addr), ntohs(icmph.un.echo.sequence));
pr_icmph(e->ee_type, e->ee_code, e->ee_info, NULL);
fflush(stdout);
@@ -664,16 +757,13 @@ int send_probe()
icp->un.echo.sequence = htons(ntransmitted+1);
icp->un.echo.id = ident; /* ID */
- CLR((ntransmitted+1) % mx_dup_ck);
+ rcvd_clear(ntransmitted+1);
if (timing) {
if (options&F_LATENCY) {
- static volatile int fake_fucked_egcs = sizeof(struct timeval);
struct timeval tmp_tv;
gettimeofday(&tmp_tv, NULL);
- /* egcs is crap or glibc is crap, but memcpy
- does not copy anything, if len is constant! */
- memcpy(icp+1, &tmp_tv, fake_fucked_egcs);
+ memcpy(icp+1, &tmp_tv, sizeof(tmp_tv));
} else {
memset(icp+1, 0, sizeof(struct timeval));
}
@@ -685,16 +775,13 @@ int send_probe()
icp->checksum = in_cksum((u_short *)icp, cc, 0);
if (timing && !(options&F_LATENCY)) {
- static volatile int fake_fucked_egcs = sizeof(struct timeval);
- struct timeval tmp_tv;
+ struct timeval tmp_tv;
gettimeofday(&tmp_tv, NULL);
- /* egcs is crap or glibc is crap, but memcpy
- does not copy anything, if len is constant! */
- memcpy(icp+1, &tmp_tv, fake_fucked_egcs);
- icp->checksum = in_cksum((u_short *)(icp+1), fake_fucked_egcs, ~icp->checksum);
+ memcpy(icp+1, &tmp_tv, sizeof(tmp_tv));
+ icp->checksum = in_cksum((u_short *)&tmp_tv, sizeof(tmp_tv), ~icp->checksum);
}
- do {
+ do {
static struct iovec iov = {outpack, 0};
static struct msghdr m = { &whereto, sizeof(whereto),
&iov, 1, &cmsg, 0, 0 };
@@ -715,6 +802,12 @@ int send_probe()
* which arrive ('tis only fair). This permits multiple copies of this
* program to be run without having intermingled output (or statistics!).
*/
+void pr_echo_reply(__u8 *_icp, int len)
+{
+ struct icmphdr *icp = (struct icmphdr *)_icp;
+ printf(" icmp_seq=%u", ntohs(icp->un.echo.sequence));
+}
+
int
parse_reply(struct msghdr *msg, int cc, void *addr, struct timeval *tv)
{
@@ -769,16 +862,17 @@ parse_reply(struct msghdr *msg, int cc, void *addr, struct timeval *tv)
if (icp->type == ICMP_ECHOREPLY) {
if (icp->un.echo.id != ident)
return 1; /* 'Twas not our ECHO */
- if (gather_statistics((__u8*)(icp+1), cc,
+ if (gather_statistics((__u8*)icp, sizeof(*icp), cc,
ntohs(icp->un.echo.sequence),
- ttl, 0, tv, pr_addr(from->sin_addr.s_addr)))
+ ttl, 0, tv, pr_addr(from->sin_addr.s_addr),
+ pr_echo_reply))
return 0;
} else {
/* We fall here when a redirect or source quench arrived.
* Also this branch processes icmp errors, when IP_RECVERR
* is broken. */
-
- switch (icp->type) {
+
+ switch (icp->type) {
case ICMP_ECHO:
/* MUST NOT */
return 1;
@@ -821,9 +915,10 @@ parse_reply(struct msghdr *msg, int cc, void *addr, struct timeval *tv)
return !error_pkt;
if (options & F_FLOOD) {
if (error_pkt)
- write(STDOUT_FILENO, "\bE", 2);
+ write_stdout("\bE", 2);
return !error_pkt;
}
+ print_timestamp();
printf("From %s: icmp_seq=%u ",
pr_addr(from->sin_addr.s_addr),
ntohs(icp1->un.echo.sequence));
@@ -832,19 +927,24 @@ parse_reply(struct msghdr *msg, int cc, void *addr, struct timeval *tv)
pr_icmph(icp->type, icp->code, ntohl(icp->un.gateway), icp);
return !error_pkt;
}
- default:
+ default:
/* MUST NOT */
break;
}
if ((options & F_FLOOD) && !(options & (F_VERBOSE|F_QUIET))) {
if (!csfailed)
- write(STDOUT_FILENO, "!E", 2);
+ write_stdout("!E", 2);
else
- write(STDOUT_FILENO, "!EC", 3);
+ write_stdout("!EC", 3);
return 0;
}
if (!(options & F_VERBOSE) || uid)
return 0;
+ if (options & F_PTIMEOFDAY) {
+ struct timeval recv_time;
+ gettimeofday(&recv_time, NULL);
+ printf("%lu.%06lu ", (unsigned long)recv_time.tv_sec, (unsigned long)recv_time.tv_usec);
+ }
printf("From %s: ", pr_addr(from->sin_addr.s_addr));
if (csfailed) {
printf("(BAD CHECKSUM)\n");
@@ -861,10 +961,22 @@ parse_reply(struct msghdr *msg, int cc, void *addr, struct timeval *tv)
putchar('\a');
putchar('\n');
fflush(stdout);
+ } else {
+ putchar('\a');
+ fflush(stdout);
}
return 0;
}
+
+#if BYTE_ORDER == LITTLE_ENDIAN
+# define ODDBYTE(v) (v)
+#elif BYTE_ORDER == BIG_ENDIAN
+# define ODDBYTE(v) ((u_short)(v) << 8)
+#else
+# define ODDBYTE(v) htons((u_short)(v) << 8)
+#endif
+
u_short
in_cksum(const u_short *addr, register int len, u_short csum)
{
@@ -886,7 +998,7 @@ in_cksum(const u_short *addr, register int len, u_short csum)
/* mop up an odd byte, if necessary */
if (nleft == 1)
- sum += htons(*(u_char *)w << 8);
+ sum += ODDBYTE(*(u_char *)w); /* le16toh() may be unavailable on old systems */
/*
* add back carry outs from top 16 bits to low 16 bits
@@ -928,9 +1040,36 @@ void pr_icmph(__u8 type, __u8 code, __u32 info, struct icmphdr *icp)
case ICMP_SR_FAILED:
printf("Source Route Failed\n");
break;
+ case ICMP_NET_UNKNOWN:
+ printf("Destination Net Unknown\n");
+ break;
+ case ICMP_HOST_UNKNOWN:
+ printf("Destination Host Unknown\n");
+ break;
+ case ICMP_HOST_ISOLATED:
+ printf("Source Host Isolated\n");
+ break;
+ case ICMP_NET_ANO:
+ printf("Destination Net Prohibited\n");
+ break;
+ case ICMP_HOST_ANO:
+ printf("Destination Host Prohibited\n");
+ break;
+ case ICMP_NET_UNR_TOS:
+ printf("Destination Net Unreachable for Type of Service\n");
+ break;
+ case ICMP_HOST_UNR_TOS:
+ printf("Destination Host Unreachable for Type of Service\n");
+ break;
case ICMP_PKT_FILTERED:
printf("Packet filtered\n");
break;
+ case ICMP_PREC_VIOLATION:
+ printf("Precedence Violation\n");
+ break;
+ case ICMP_PREC_CUTOFF:
+ printf("Precedence Cutoff\n");
+ break;
default:
printf("Dest Unreachable, Bad Code: %d\n", code);
break;
@@ -1076,9 +1215,9 @@ void pr_options(unsigned char * cp, int hlen)
i = j;
i -= IPOPT_MINOFF;
if (i <= 0)
- continue;
+ break;
if (i == old_rrlen
- && !bcmp((char *)cp, old_rr, i)
+ && !memcmp(cp, old_rr, i)
&& !(options & F_FLOOD)) {
printf("\t(same route)");
i = ((i + 3) / 4) * 4;
@@ -1086,7 +1225,7 @@ void pr_options(unsigned char * cp, int hlen)
break;
}
old_rrlen = i;
- bcopy((char *)cp, old_rr, i);
+ memcpy(old_rr, (char *)cp, i);
printf("\nRR: ");
cp++;
for (;;) {
@@ -1113,7 +1252,7 @@ void pr_options(unsigned char * cp, int hlen)
i = j;
i -= 5;
if (i <= 0)
- continue;
+ break;
flags = *++cp;
printf("\nTS: ");
cp++;
@@ -1204,12 +1343,28 @@ pr_addr(__u32 addr)
struct hostent *hp;
static char buf[4096];
- if ((options & F_NUMERIC) ||
+ in_pr_addr = !setjmp(pr_addr_jmp);
+
+ if (exiting || (options & F_NUMERIC) ||
!(hp = gethostbyaddr((char *)&addr, 4, AF_INET)))
sprintf(buf, "%s", inet_ntoa(*(struct in_addr *)&addr));
- else
- snprintf(buf, sizeof(buf), "%s (%s)", hp->h_name,
+ else {
+ char *s;
+#if USE_IDN
+ if (idna_to_unicode_lzlz(hp->h_name, &s, 0) != IDNA_SUCCESS)
+ s = NULL;
+#else
+ s = NULL;
+#endif
+ snprintf(buf, sizeof(buf), "%s (%s)", s ? s : hp->h_name,
inet_ntoa(*(struct in_addr *)&addr));
+#if USE_IDN
+ free(s);
+#endif
+ }
+
+ in_pr_addr = 0;
+
return(buf);
}
@@ -1217,27 +1372,27 @@ pr_addr(__u32 addr)
/* Set Type of Service (TOS) and other Quality of Service relating bits */
int parsetos(char *str)
{
- const char *cp;
- int tos;
- char *ep;
+ const char *cp;
+ int tos;
+ char *ep;
- /* handle both hex and decimal values */
- if (str[0] == '0' && (str[1] == 'x' || str[1] == 'X')) {
+ /* handle both hex and decimal values */
+ if (str[0] == '0' && (str[1] == 'x' || str[1] == 'X')) {
cp = str + 2;
tos = (int)strtol(cp, &ep, 16);
- } else
- tos = (int)strtol(str, &ep, 10);
-
- /* doesn't look like decimal or hex, eh? */
- if (*ep != '\0') {
- fprintf(stderr, "ping: \"%s\" bad value for TOS\n", str);
- exit(2);
- }
-
- if (tos > TOS_MAX) {
- fprintf(stderr, "ping: the decimal value of TOS bits must be 0-254 (or zero)\n");
- exit(2);
- }
+ } else
+ tos = (int)strtol(str, &ep, 10);
+
+ /* doesn't look like decimal or hex, eh? */
+ if (*ep != '\0') {
+ fprintf(stderr, "ping: \"%s\" bad value for TOS\n", str);
+ exit(2);
+ }
+
+ if (tos > TOS_MAX) {
+ fprintf(stderr, "ping: the decimal value of TOS bits must be 0-254 (or zero)\n");
+ exit(2);
+ }
return(tos);
}
@@ -1266,19 +1421,40 @@ void install_filter(void)
once = 1;
/* Patch bpflet for current identifier. */
- insns[2] = (struct sock_filter)BPF_JUMP(BPF_JMP|BPF_JEQ|BPF_K, __constant_htons(ident), 0, 1);
+ insns[2] = (struct sock_filter)BPF_JUMP(BPF_JMP|BPF_JEQ|BPF_K, htons(ident), 0, 1);
if (setsockopt(icmp_sock, SOL_SOCKET, SO_ATTACH_FILTER, &filter, sizeof(filter)))
perror("WARNING: failed to install socket filter\n");
}
+#define USAGE_NEWLINE "\n "
void usage(void)
{
fprintf(stderr,
-"Usage: ping [-LRUbdfnqrvVaA] [-c count] [-i interval] [-w deadline]\n"
-" [-p pattern] [-s packetsize] [-t ttl] [-I interface or address]\n"
-" [-M mtu discovery hint] [-S sndbuf]\n"
-" [ -T timestamp option ] [ -Q tos ] [hop1 ...] destination\n");
+ "Usage: ping"
+ " [-"
+ "aAbBdDfhLnOqrRUvV"
+ "]"
+ " [-c count]"
+ " [-i interval]"
+ " [-I interface]"
+ USAGE_NEWLINE
+ " [-m mark]"
+ " [-M pmtudisc_option]"
+ " [-l preload]"
+ " [-p pattern]"
+ " [-Q tos]"
+ USAGE_NEWLINE
+ " [-s packetsize]"
+ " [-S sndbuf]"
+ " [-t ttl]"
+ " [-T timestamp_option]"
+ USAGE_NEWLINE
+ " [-w deadline]"
+ " [-W timeout]"
+ " [hop1 ...] destination"
+ "\n"
+ );
exit(2);
}
diff --git a/ping6.c b/ping6.c
new file mode 100644
index 0000000..c39864d
--- /dev/null
+++ b/ping6.c
@@ -0,0 +1,1861 @@
+/*
+ *
+ * Modified for AF_INET6 by Pedro Roque
+ *
+ * <roque@di.fc.ul.pt>
+ *
+ * Original copyright notice included bellow
+ */
+
+/*
+ * Copyright (c) 1989 The Regents of the University of California.
+ * All rights reserved.
+ *
+ * This code is derived from software contributed to Berkeley by
+ * Mike Muuss.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#ifndef lint
+char copyright[] =
+"@(#) Copyright (c) 1989 The Regents of the University of California.\n\
+ All rights reserved.\n";
+#endif /* not lint */
+
+/*
+ * P I N G . C
+ *
+ * Using the InterNet Control Message Protocol (ICMP) "ECHO" facility,
+ * measure round-trip-delays and packet loss across network paths.
+ *
+ * Author -
+ * Mike Muuss
+ * U. S. Army Ballistic Research Laboratory
+ * December, 1983
+ *
+ * Status -
+ * Public Domain. Distribution Unlimited.
+ * Bugs -
+ * More statistics could always be gathered.
+ * This program has to run SUID to ROOT to access the ICMP socket.
+ */
+#include "ping_common.h"
+
+#include <linux/filter.h>
+#include <netinet/ip6.h>
+#include <netinet/icmp6.h>
+#include <resolv.h>
+#ifndef WITHOUT_IFADDRS
+#include <ifaddrs.h>
+#endif
+
+#ifdef USE_IDN
+#include <stringprep.h>
+#endif
+
+#include "ping6_niquery.h"
+#include "in6_flowlabel.h"
+
+#ifndef SOL_IPV6
+#define SOL_IPV6 IPPROTO_IPV6
+#endif
+
+#ifndef SOL_ICMPV6
+#define SOL_ICMPV6 IPPROTO_ICMPV6
+#endif
+
+/* RFC3542 */
+#ifndef ICMP6_DST_UNREACH_BEYONDSCOPE
+#define ICMP6_DST_UNREACH_BEYONDSCOPE ICMP6_DST_UNREACH_NOTNEIGHBOR
+#endif
+
+#if defined(ENABLE_PING6_RTHDR) && !defined(ENABLE_PING6_RTHDR_RFC3542)
+#ifndef IPV6_SRCRT_TYPE_0
+#define IPV6_SRCRT_TYPE_0 0
+#endif
+#endif
+
+#ifndef MLD_LISTENER_QUERY
+#define MLD_LISTENER_QUERY 130
+#define MLD_LISTENER_REPORT 131
+#define MLD_LISTENER_REDUCTION 132
+#endif
+
+#define BIT_CLEAR(nr, addr) do { ((__u32 *)(addr))[(nr) >> 5] &= ~(1U << ((nr) & 31)); } while(0)
+#define BIT_SET(nr, addr) do { ((__u32 *)(addr))[(nr) >> 5] |= (1U << ((nr) & 31)); } while(0)
+#define BIT_TEST(nr, addr) do { (__u32 *)(addr))[(nr) >> 5] & (1U << ((nr) & 31)); } while(0)
+
+#ifndef ICMP6_FILTER_WILLPASS
+#define ICMP6_FILTER_WILLPASS(type, filterp) \
+ (BIT_TEST((type), filterp) == 0)
+
+#define ICMP6_FILTER_WILLBLOCK(type, filterp) \
+ BIT_TEST((type), filterp)
+
+#define ICMP6_FILTER_SETPASS(type, filterp) \
+ BIT_CLEAR((type), filterp)
+
+#define ICMP6_FILTER_SETBLOCK(type, filterp) \
+ BIT_SET((type), filterp)
+
+#define ICMP6_FILTER_SETPASSALL(filterp) \
+ memset(filterp, 0, sizeof(struct icmp6_filter));
+
+#define ICMP6_FILTER_SETBLOCKALL(filterp) \
+ memset(filterp, 0xFF, sizeof(struct icmp6_filter));
+#endif
+
+#define MAXPACKET 128000 /* max packet size */
+
+#ifdef SO_TIMESTAMP
+#define HAVE_SIN6_SCOPEID 1
+#endif
+
+#ifndef SCOPE_DELIMITER
+# define SCOPE_DELIMITER '%'
+#endif
+
+__u32 flowlabel;
+__u32 tclass;
+#ifdef ENABLE_PING6_RTHDR
+struct cmsghdr *srcrt;
+#endif
+
+struct sockaddr_in6 whereto; /* who to ping */
+u_char outpack[MAXPACKET];
+int maxpacket = sizeof(outpack);
+
+static unsigned char cmsgbuf[4096];
+static int cmsglen = 0;
+
+static char * pr_addr(struct in6_addr *addr);
+static char * pr_addr_n(struct in6_addr *addr);
+static int pr_icmph(__u8 type, __u8 code, __u32 info);
+static void usage(void) __attribute((noreturn));
+
+struct sockaddr_in6 source;
+char *device;
+int pmtudisc=-1;
+
+static int icmp_sock;
+
+#ifdef USE_GNUTLS
+# include <gnutls/openssl.h>
+#else
+# include <openssl/md5.h>
+#endif
+
+/* Node Information query */
+int ni_query = -1;
+int ni_flag = 0;
+void *ni_subject = NULL;
+int ni_subject_len = 0;
+int ni_subject_type = -1;
+char *ni_group;
+
+static inline int ntohsp(__u16 *p)
+{
+ __u16 v;
+ memcpy(&v, p, sizeof(v));
+ return ntohs(v);
+}
+
+#if defined(ENABLE_PING6_RTHDR) && !defined(ENABLE_PING6_RTHDR_RFC3542)
+size_t inet6_srcrt_space(int type, int segments)
+{
+ if (type != 0 || segments > 24)
+ return 0;
+
+ return (sizeof(struct cmsghdr) + sizeof(struct ip6_rthdr0) +
+ segments * sizeof(struct in6_addr));
+}
+
+extern struct cmsghdr * inet6_srcrt_init(void *bp, int type)
+{
+ struct cmsghdr *cmsg;
+
+ if (type)
+ return NULL;
+
+ memset(bp, 0, sizeof(struct cmsghdr) + sizeof(struct ip6_rthdr0));
+ cmsg = (struct cmsghdr *) bp;
+
+ cmsg->cmsg_len = sizeof(struct cmsghdr) + sizeof(struct ip6_rthdr0);
+ cmsg->cmsg_level = SOL_IPV6;
+ cmsg->cmsg_type = IPV6_RTHDR;
+
+ return cmsg;
+}
+
+int inet6_srcrt_add(struct cmsghdr *cmsg, const struct in6_addr *addr)
+{
+ struct ip6_rthdr0 *hdr;
+
+ hdr = (struct ip6_rthdr0 *) CMSG_DATA(cmsg);
+
+ cmsg->cmsg_len += sizeof(struct in6_addr);
+ hdr->ip6r0_len += sizeof(struct in6_addr) / 8;
+
+ memcpy(&hdr->ip6r0_addr[hdr->ip6r0_segleft++], addr,
+ sizeof(struct in6_addr));
+
+ return 0;
+}
+#endif
+
+unsigned int if_name2index(const char *ifname)
+{
+ unsigned int i = if_nametoindex(ifname);
+ if (!i) {
+ fprintf(stderr, "ping: unknown iface %s\n", ifname);
+ exit(2);
+ }
+ return i;
+}
+
+struct niquery_option {
+ char *name;
+ int namelen;
+ int has_arg;
+ int data;
+ int (*handler)(int index, const char *arg);
+};
+
+#define NIQUERY_OPTION(_name, _has_arg, _data, _handler) \
+ { \
+ .name = _name, \
+ .namelen = sizeof(_name) - 1, \
+ .has_arg = _has_arg, \
+ .data = _data, \
+ .handler = _handler \
+ }
+
+static int niquery_option_name_handler(int index, const char *arg);
+static int niquery_option_ipv6_handler(int index, const char *arg);
+static int niquery_option_ipv6_flag_handler(int index, const char *arg);
+static int niquery_option_ipv4_handler(int index, const char *arg);
+static int niquery_option_ipv4_flag_handler(int index, const char *arg);
+static int niquery_option_subject_addr_handler(int index, const char *arg);
+static int niquery_option_subject_name_handler(int index, const char *arg);
+static int niquery_option_help_handler(int index, const char *arg);
+
+struct niquery_option niquery_options[] = {
+ NIQUERY_OPTION("name", 0, 0, niquery_option_name_handler),
+ NIQUERY_OPTION("fqdn", 0, 0, niquery_option_name_handler),
+ NIQUERY_OPTION("ipv6", 0, 0, niquery_option_ipv6_handler),
+ NIQUERY_OPTION("ipv6-all", 0, NI_IPV6ADDR_F_ALL, niquery_option_ipv6_flag_handler),
+ NIQUERY_OPTION("ipv6-compatible", 0, NI_IPV6ADDR_F_COMPAT, niquery_option_ipv6_flag_handler),
+ NIQUERY_OPTION("ipv6-linklocal", 0, NI_IPV6ADDR_F_LINKLOCAL, niquery_option_ipv6_flag_handler),
+ NIQUERY_OPTION("ipv6-sitelocal", 0, NI_IPV6ADDR_F_SITELOCAL, niquery_option_ipv6_flag_handler),
+ NIQUERY_OPTION("ipv6-global", 0, NI_IPV6ADDR_F_GLOBAL, niquery_option_ipv6_flag_handler),
+ NIQUERY_OPTION("ipv4", 0, 0, niquery_option_ipv4_handler),
+ NIQUERY_OPTION("ipv4-all", 0, NI_IPV4ADDR_F_ALL, niquery_option_ipv4_flag_handler),
+ NIQUERY_OPTION("subject-ipv6", 1, NI_SUBJ_IPV6, niquery_option_subject_addr_handler),
+ NIQUERY_OPTION("subject-ipv4", 1, NI_SUBJ_IPV4, niquery_option_subject_addr_handler),
+ NIQUERY_OPTION("subject-name", 1, 0, niquery_option_subject_name_handler),
+ NIQUERY_OPTION("subject-fqdn", 1, -1, niquery_option_subject_name_handler),
+ NIQUERY_OPTION("help", 0, 0, niquery_option_help_handler),
+ {},
+};
+
+static inline int niquery_is_enabled(void)
+{
+ return ni_query >= 0;
+}
+
+#if PING6_NONCE_MEMORY
+__u8 *ni_nonce_ptr;
+#else
+struct {
+ struct timeval tv;
+ pid_t pid;
+} ni_nonce_secret;
+#endif
+
+static void niquery_init_nonce(void)
+{
+#if PING6_NONCE_MEMORY
+ struct timeval tv;
+ unsigned long seed;
+
+ seed = (unsigned long)getpid();
+ if (!gettimeofday(&tv, NULL))
+ seed ^= tv.tv_usec;
+ srand(seed);
+
+ ni_nonce_ptr = calloc(NI_NONCE_SIZE, MAX_DUP_CHK);
+ if (!ni_nonce_ptr) {
+ perror("ping6: calloc");
+ exit(2);
+ }
+
+ ni_nonce_ptr[0] = ~0;
+#else
+ gettimeofday(&ni_nonce_secret.tv, NULL);
+ ni_nonce_secret.pid = getpid();
+#endif
+}
+
+#if !PING6_NONCE_MEMORY
+static int niquery_nonce(__u8 *nonce, int fill)
+{
+ static __u8 digest[MD5_DIGEST_LENGTH];
+ static int seq = -1;
+
+ if (fill || seq != *(__u16 *)nonce || seq < 0) {
+ MD5_CTX ctxt;
+
+ MD5_Init(&ctxt);
+ MD5_Update(&ctxt, &ni_nonce_secret, sizeof(ni_nonce_secret));
+ MD5_Update(&ctxt, nonce, sizeof(__u16));
+ MD5_Final(digest, &ctxt);
+
+ seq = *(__u16 *)nonce;
+ }
+
+ if (fill) {
+ memcpy(nonce + sizeof(__u16), digest, NI_NONCE_SIZE - sizeof(__u16));
+ return 0;
+ } else {
+ if (memcmp(nonce + sizeof(__u16), digest, NI_NONCE_SIZE - sizeof(__u16)))
+ return -1;
+ return ntohsp((__u16 *)nonce);
+ }
+}
+#endif
+
+static inline void niquery_fill_nonce(__u16 seq, __u8 *nonce)
+{
+ __u16 v = htons(seq);
+#if PING6_NONCE_MEMORY
+ int i;
+
+ memcpy(&ni_nonce_ptr[NI_NONCE_SIZE * (seq % MAX_DUP_CHK)], &v, sizeof(v));
+
+ for (i = sizeof(v); i < NI_NONCE_SIZE; i++)
+ ni_nonce_ptr[NI_NONCE_SIZE * (seq % MAX_DUP_CHK) + i] = 0x100 * (rand() / (RAND_MAX + 1.0));
+
+ memcpy(nonce, &ni_nonce_ptr[NI_NONCE_SIZE * (seq % MAX_DUP_CHK)], NI_NONCE_SIZE);
+#else
+ memcpy(nonce, &v, sizeof(v));
+ niquery_nonce(nonce, 1);
+#endif
+}
+
+static inline int niquery_check_nonce(__u8 *nonce)
+{
+#if PING6_NONCE_MEMORY
+ __u16 seq = ntohsp((__u16 *)nonce);
+ if (memcmp(nonce, &ni_nonce_ptr[NI_NONCE_SIZE * (seq % MAX_DUP_CHK)], NI_NONCE_SIZE))
+ return -1;
+ return seq;
+#else
+ return niquery_nonce(nonce, 0);
+#endif
+}
+
+static int niquery_set_qtype(int type)
+{
+ if (niquery_is_enabled() && ni_query != type) {
+ printf("Qtype conflict\n");
+ return -1;
+ }
+ ni_query = type;
+ return 0;
+}
+
+static int niquery_option_name_handler(int index, const char *arg)
+{
+ if (niquery_set_qtype(NI_QTYPE_NAME) < 0)
+ return -1;
+ return 0;
+}
+
+static int niquery_option_ipv6_handler(int index, const char *arg)
+{
+ if (niquery_set_qtype(NI_QTYPE_IPV6ADDR) < 0)
+ return -1;
+ return 0;
+}
+
+static int niquery_option_ipv6_flag_handler(int index, const char *arg)
+{
+ if (niquery_set_qtype(NI_QTYPE_IPV6ADDR) < 0)
+ return -1;
+ ni_flag |= niquery_options[index].data;
+ return 0;
+}
+
+static int niquery_option_ipv4_handler(int index, const char *arg)
+{
+ if (niquery_set_qtype(NI_QTYPE_IPV4ADDR) < 0)
+ return -1;
+ return 0;
+}
+
+static int niquery_option_ipv4_flag_handler(int index, const char *arg)
+{
+ if (niquery_set_qtype(NI_QTYPE_IPV4ADDR) < 0)
+ return -1;
+ ni_flag |= niquery_options[index].data;
+ return 0;
+}
+
+static inline int niquery_is_subject_valid(void)
+{
+ return ni_subject_type >= 0 && ni_subject;
+}
+
+static int niquery_set_subject_type(int type)
+{
+ if (niquery_is_subject_valid() && ni_subject_type != type) {
+ printf("Subject type conflict\n");
+ return -1;
+ }
+ ni_subject_type = type;
+ return 0;
+}
+
+#define ARRAY_SIZE(array) (sizeof(array) / sizeof(array[0]))
+#define OFFSET_OF(type,elem) ((size_t)&((type *)0)->elem)
+
+static int niquery_option_subject_addr_handler(int index, const char *arg)
+{
+ struct addrinfo hints, *ai0, *ai;
+ int offset;
+ int gai;
+
+ if (niquery_set_subject_type(niquery_options[index].data) < 0)
+ return -1;
+
+ ni_subject_type = niquery_options[index].data;
+
+ memset(&hints, 0, sizeof(hints));
+
+ switch (niquery_options[index].data) {
+ case NI_SUBJ_IPV6:
+ ni_subject_len = sizeof(struct in6_addr);
+ offset = OFFSET_OF(struct sockaddr_in6, sin6_addr);
+ hints.ai_family = AF_INET6;
+ break;
+ case NI_SUBJ_IPV4:
+ ni_subject_len = sizeof(struct in_addr);
+ offset = OFFSET_OF(struct sockaddr_in, sin_addr);
+ hints.ai_family = AF_INET;
+ break;
+ default:
+ /* should not happen. */
+ offset = -1;
+ }
+
+ hints.ai_socktype = SOCK_DGRAM;
+#ifdef USE_IDN
+ hints.ai_flags = AI_IDN;
+#endif
+
+ gai = getaddrinfo(arg, 0, &hints, &ai0);
+ if (gai) {
+ fprintf(stderr, "Unknown host: %s\n", arg);
+ return -1;
+ }
+
+ for (ai = ai0; ai; ai = ai->ai_next) {
+ void *p = malloc(ni_subject_len);
+ if (!p)
+ continue;
+ memcpy(p, (__u8 *)ai->ai_addr + offset, ni_subject_len);
+ free(ni_subject);
+ ni_subject = p;
+ break;
+ }
+ freeaddrinfo(ai0);
+
+ return 0;
+}
+
+static int niquery_option_subject_name_handler(int index, const char *arg)
+{
+ static char nigroup_buf[INET6_ADDRSTRLEN + 1 + IFNAMSIZ];
+ unsigned char *dnptrs[2], **dpp, **lastdnptr;
+ int n;
+ int i;
+ char *name, *p;
+ char *canonname = NULL, *idn = NULL;
+ unsigned char *buf = NULL;
+ size_t namelen;
+ size_t buflen;
+ int dots, fqdn = niquery_options[index].data;
+ MD5_CTX ctxt;
+ __u8 digest[MD5_DIGEST_LENGTH];
+#ifdef USE_IDN
+ int rc;
+#endif
+
+ if (niquery_set_subject_type(NI_SUBJ_NAME) < 0)
+ return -1;
+
+#ifdef USE_IDN
+ name = stringprep_locale_to_utf8(arg);
+ if (!name) {
+ fprintf(stderr, "ping6: IDN support failed.\n");
+ exit(2);
+ }
+#else
+ name = strdup(arg);
+ if (!name)
+ goto oomexit;
+#endif
+
+ p = strchr(name, SCOPE_DELIMITER);
+ if (p) {
+ *p = '\0';
+ if (strlen(p + 1) >= IFNAMSIZ) {
+ fprintf(stderr, "ping6: too long scope name.\n");
+ exit(1);
+ }
+ }
+
+#ifdef USE_IDN
+ rc = idna_to_ascii_8z(name, &idn, 0);
+ if (rc) {
+ fprintf(stderr, "ping6: IDN encoding error: %s\n",
+ idna_strerror(rc));
+ exit(2);
+ }
+#else
+ idn = strdup(name);
+ if (!idn)
+ goto oomexit;
+#endif
+
+ namelen = strlen(idn);
+ canonname = malloc(namelen + 1);
+ if (!canonname)
+ goto oomexit;
+
+ dots = 0;
+ for (i = 0; i < namelen + 1; i++) {
+ canonname[i] = isupper(idn[i]) ? tolower(idn[i]) : idn[i];
+ if (idn[i] == '.')
+ dots++;
+ }
+
+ if (fqdn == 0) {
+ /* guess if hostname is FQDN */
+ fqdn = dots ? 1 : -1;
+ }
+
+ buflen = namelen + 3 + 1; /* dn_comp() requrires strlen() + 3,
+ plus non-fqdn indicator. */
+ buf = malloc(buflen);
+ if (!buf) {
+ fprintf(stderr, "ping6: out of memory.\n");
+ goto errexit;
+ }
+
+ dpp = dnptrs;
+ lastdnptr = &dnptrs[ARRAY_SIZE(dnptrs)];
+
+ *dpp++ = (unsigned char *)buf;
+ *dpp++ = NULL;
+
+ n = dn_comp(canonname, (unsigned char *)buf, buflen, dnptrs, lastdnptr);
+ if (n < 0) {
+ fprintf(stderr, "ping6: Inappropriate subject name: %s\n", canonname);
+ goto errexit;
+ } else if (n >= buflen) {
+ fprintf(stderr, "ping6: dn_comp() returned too long result.\n");
+ goto errexit;
+ }
+
+ MD5_Init(&ctxt);
+ MD5_Update(&ctxt, buf, buf[0]);
+ MD5_Final(digest, &ctxt);
+
+ sprintf(nigroup_buf, "ff02::2:%02x%02x:%02x%02x%s%s",
+ digest[0], digest[1], digest[2], digest[3],
+ p ? "%" : "",
+ p ? p + 1 : "");
+
+ if (fqdn < 0)
+ buf[n] = 0;
+
+ free(ni_subject);
+
+ ni_group = nigroup_buf;
+ ni_subject = buf;
+ ni_subject_len = n + (fqdn < 0);
+ ni_group = nigroup_buf;
+
+ free(canonname);
+ free(idn);
+ free(name);
+
+ return 0;
+oomexit:
+ fprintf(stderr, "ping6: out of memory.\n");
+errexit:
+ free(buf);
+ free(canonname);
+ free(idn);
+ free(name);
+ exit(1);
+}
+
+int niquery_option_help_handler(int index, const char *arg)
+{
+ fprintf(stderr, "ping6 -N suboptions\n"
+ "\tHelp:\n"
+ "\t\thelp\n"
+ "\tQuery:\n"
+ "\t\tname,\n"
+ "\t\tipv6,ipv6-all,ipv6-compatible,ipv6-linklocal,ipv6-sitelocal,ipv6-global,\n"
+ "\t\tipv4,ipv4-all,\n"
+ "\tSubject:\n"
+ "\t\tsubject-ipv6=addr,subject-ipv4=addr,subject-name=name,subject-fqdn=name,\n"
+ );
+ exit(2);
+}
+
+int niquery_option_handler(const char *opt_arg)
+{
+ struct niquery_option *p;
+ int i;
+ int ret = -1;
+ for (i = 0, p = niquery_options; p->name; i++, p++) {
+ if (strncmp(p->name, opt_arg, p->namelen))
+ continue;
+ if (!p->has_arg) {
+ if (opt_arg[p->namelen] == '\0') {
+ ret = p->handler(i, NULL);
+ if (ret >= 0)
+ break;
+ }
+ } else {
+ if (opt_arg[p->namelen] == '=') {
+ ret = p->handler(i, &opt_arg[p->namelen] + 1);
+ if (ret >= 0)
+ break;
+ }
+ }
+ }
+ if (!p->name)
+ ret = niquery_option_help_handler(0, NULL);
+ return ret;
+}
+
+static int hextoui(const char *str)
+{
+ unsigned long val;
+ char *ep;
+
+ errno = 0;
+ val = strtoul(str, &ep, 16);
+ if (*ep) {
+ if (!errno)
+ errno = EINVAL;
+ return -1;
+ }
+
+ if (val > UINT_MAX) {
+ errno = ERANGE;
+ return UINT_MAX;
+ }
+
+ return val;
+}
+
+int main(int argc, char *argv[])
+{
+ int ch, hold, packlen;
+ u_char *packet;
+ char *target;
+ struct addrinfo hints, *ai;
+ int gai;
+ struct sockaddr_in6 firsthop;
+ int socket_errno;
+ struct icmp6_filter filter;
+ int err;
+#ifdef __linux__
+ int csum_offset, sz_opt;
+#endif
+ static uint32_t scope_id = 0;
+
+ limit_capabilities();
+
+#ifdef USE_IDN
+ setlocale(LC_ALL, "");
+#endif
+
+ enable_capability_raw();
+
+ icmp_sock = socket(AF_INET6, SOCK_RAW, IPPROTO_ICMPV6);
+ socket_errno = errno;
+
+ disable_capability_raw();
+
+ source.sin6_family = AF_INET6;
+ memset(&firsthop, 0, sizeof(firsthop));
+ firsthop.sin6_family = AF_INET6;
+
+ preload = 1;
+ while ((ch = getopt(argc, argv, COMMON_OPTSTR "F:N:")) != EOF) {
+ switch(ch) {
+ case 'F':
+ flowlabel = hextoui(optarg);
+ if (errno || (flowlabel & ~IPV6_FLOWINFO_FLOWLABEL)) {
+ fprintf(stderr, "ping: Invalid flowinfo %s\n", optarg);
+ exit(2);
+ }
+ options |= F_FLOWINFO;
+ break;
+ case 'Q':
+ tclass = hextoui(optarg);
+ if (errno || (tclass & ~0xff)) {
+ fprintf(stderr, "ping: Invalid tclass %s\n", optarg);
+ exit(2);
+ }
+ options |= F_TCLASS;
+ break;
+ case 'I':
+ if (strchr(optarg, ':')) {
+ char *p, *addr = strdup(optarg);
+
+ if (!addr) {
+ fprintf(stderr, "ping: out of memory\n");
+ exit(2);
+ }
+
+ p = strchr(addr, SCOPE_DELIMITER);
+ if (p) {
+ *p = '\0';
+ device = optarg + (p - addr) + 1;
+ }
+
+ if (inet_pton(AF_INET6, addr, (char*)&source.sin6_addr) <= 0) {
+ fprintf(stderr, "ping: invalid source address %s\n", optarg);
+ exit(2);
+ }
+
+ options |= F_STRICTSOURCE;
+
+ free(addr);
+ } else {
+ device = optarg;
+ }
+ break;
+ case 'M':
+ if (strcmp(optarg, "do") == 0)
+ pmtudisc = IPV6_PMTUDISC_DO;
+ else if (strcmp(optarg, "dont") == 0)
+ pmtudisc = IPV6_PMTUDISC_DONT;
+ else if (strcmp(optarg, "want") == 0)
+ pmtudisc = IPV6_PMTUDISC_WANT;
+ else {
+ fprintf(stderr, "ping: wrong value for -M: do, dont, want are valid ones.\n");
+ exit(2);
+ }
+ break;
+ case 'V':
+ printf("ping6 utility, iputils-%s\n", SNAPSHOT);
+ exit(0);
+ case 'N':
+ if (niquery_option_handler(optarg) < 0) {
+ usage();
+ break;
+ }
+ break;
+ COMMON_OPTIONS
+ common_options(ch);
+ break;
+ default:
+ usage();
+ }
+ }
+ argc -= optind;
+ argv += optind;
+
+#ifdef ENABLE_PING6_RTHDR
+ while (argc > 1) {
+ struct in6_addr *addr;
+
+ if (srcrt == NULL) {
+ int space;
+
+ fprintf(stderr, "ping6: Warning: "
+ "Source routing is deprecated by RFC5095.\n");
+
+#ifdef ENABLE_PING6_RTHDR_RFC3542
+ space = inet6_rth_space(IPV6_RTHDR_TYPE_0, argc - 1);
+#else
+ space = inet6_srcrt_space(IPV6_SRCRT_TYPE_0, argc - 1);
+#endif
+ if (space == 0) {
+ fprintf(stderr, "srcrt_space failed\n");
+ exit(2);
+ }
+#ifdef ENABLE_PING6_RTHDR_RFC3542
+ if (cmsglen + CMSG_SPACE(space) > sizeof(cmsgbuf)) {
+ fprintf(stderr, "no room for options\n");
+ exit(2);
+ }
+#else
+ if (space + cmsglen > sizeof(cmsgbuf)) {
+ fprintf(stderr, "no room for options\n");
+ exit(2);
+ }
+#endif
+ srcrt = (struct cmsghdr*)(cmsgbuf+cmsglen);
+#ifdef ENABLE_PING6_RTHDR_RFC3542
+ memset(srcrt, 0, CMSG_SPACE(0));
+ srcrt->cmsg_len = CMSG_LEN(space);
+ srcrt->cmsg_level = IPPROTO_IPV6;
+ srcrt->cmsg_type = IPV6_RTHDR;
+ inet6_rth_init(CMSG_DATA(srcrt), space, IPV6_RTHDR_TYPE_0, argc - 1);
+ cmsglen += CMSG_SPACE(space);
+#else
+ cmsglen += CMSG_ALIGN(space);
+ inet6_srcrt_init(srcrt, IPV6_SRCRT_TYPE_0);
+#endif
+ }
+
+ target = *argv;
+
+ memset(&hints, 0, sizeof(hints));
+ hints.ai_family = AF_INET6;
+#ifdef USE_IDN
+ hints.ai_flags = AI_IDN;
+#endif
+ gai = getaddrinfo(target, NULL, &hints, &ai);
+ if (gai) {
+ fprintf(stderr, "unknown host\n");
+ exit(2);
+ }
+ addr = &((struct sockaddr_in6 *)(ai->ai_addr))->sin6_addr;
+#ifdef ENABLE_PING6_RTHDR_RFC3542
+ inet6_rth_add(CMSG_DATA(srcrt), addr);
+#else
+ inet6_srcrt_add(srcrt, addr);
+#endif
+ if (IN6_IS_ADDR_UNSPECIFIED(&firsthop.sin6_addr)) {
+ memcpy(&firsthop.sin6_addr, addr, 16);
+#ifdef HAVE_SIN6_SCOPEID
+ firsthop.sin6_scope_id = ((struct sockaddr_in6 *)(ai->ai_addr))->sin6_scope_id;
+ /* Verify scope_id is the same as previous nodes */
+ if (firsthop.sin6_scope_id && scope_id && firsthop.sin6_scope_id != scope_id) {
+ fprintf(stderr, "scope discrepancy among the nodes\n");
+ exit(2);
+ } else if (!scope_id) {
+ scope_id = firsthop.sin6_scope_id;
+ }
+#endif
+ }
+ freeaddrinfo(ai);
+
+ argv++;
+ argc--;
+ }
+#endif
+
+ if (niquery_is_enabled()) {
+ niquery_init_nonce();
+
+ if (!niquery_is_subject_valid()) {
+ ni_subject = &whereto.sin6_addr;
+ ni_subject_len = sizeof(whereto.sin6_addr);
+ ni_subject_type = NI_SUBJ_IPV6;
+ }
+ }
+
+ if (argc > 1) {
+#ifndef ENABLE_PING6_RTHDR
+ fprintf(stderr, "ping6: Source routing is deprecated by RFC5095.\n");
+#endif
+ usage();
+ } else if (argc == 1) {
+ target = *argv;
+ } else {
+ if (ni_query < 0 && ni_subject_type != NI_SUBJ_NAME)
+ usage();
+ target = ni_group;
+ }
+
+ memset(&hints, 0, sizeof(hints));
+ hints.ai_family = AF_INET6;
+#ifdef USE_IDN
+ hints.ai_flags = AI_IDN;
+#endif
+ gai = getaddrinfo(target, NULL, &hints, &ai);
+ if (gai) {
+ fprintf(stderr, "unknown host\n");
+ exit(2);
+ }
+
+ memcpy(&whereto, ai->ai_addr, sizeof(whereto));
+ whereto.sin6_port = htons(IPPROTO_ICMPV6);
+
+ if (memchr(target, ':', strlen(target)))
+ options |= F_NUMERIC;
+
+ freeaddrinfo(ai);
+
+ if (IN6_IS_ADDR_UNSPECIFIED(&firsthop.sin6_addr)) {
+ memcpy(&firsthop.sin6_addr, &whereto.sin6_addr, 16);
+#ifdef HAVE_SIN6_SCOPEID
+ firsthop.sin6_scope_id = whereto.sin6_scope_id;
+ /* Verify scope_id is the same as intermediate nodes */
+ if (firsthop.sin6_scope_id && scope_id && firsthop.sin6_scope_id != scope_id) {
+ fprintf(stderr, "scope discrepancy among the nodes\n");
+ exit(2);
+ } else if (!scope_id) {
+ scope_id = firsthop.sin6_scope_id;
+ }
+#endif
+ }
+
+ hostname = target;
+
+ if (IN6_IS_ADDR_UNSPECIFIED(&source.sin6_addr)) {
+ socklen_t alen;
+ int probe_fd = socket(AF_INET6, SOCK_DGRAM, 0);
+
+ if (probe_fd < 0) {
+ perror("socket");
+ exit(2);
+ }
+ if (device) {
+#if defined(IPV6_RECVPKTINFO) || defined(HAVE_SIN6_SCOPEID)
+ unsigned int iface = if_name2index(device);
+#endif
+#ifdef IPV6_RECVPKTINFO
+ struct in6_pktinfo ipi;
+
+ memset(&ipi, 0, sizeof(ipi));
+ ipi.ipi6_ifindex = iface;
+#endif
+
+#ifdef HAVE_SIN6_SCOPEID
+ if (IN6_IS_ADDR_LINKLOCAL(&firsthop.sin6_addr) ||
+ IN6_IS_ADDR_MC_LINKLOCAL(&firsthop.sin6_addr))
+ firsthop.sin6_scope_id = iface;
+#endif
+ enable_capability_raw();
+ if (
+#ifdef IPV6_RECVPKTINFO
+ setsockopt(probe_fd, IPPROTO_IPV6, IPV6_PKTINFO, &ipi, sizeof(ipi)) == -1 &&
+#endif
+ setsockopt(probe_fd, SOL_SOCKET, SO_BINDTODEVICE, device, strlen(device)+1) == -1) {
+ perror("setsockopt(SO_BINDTODEVICE)");
+ exit(2);
+ }
+ disable_capability_raw();
+ }
+ firsthop.sin6_port = htons(1025);
+ if (connect(probe_fd, (struct sockaddr*)&firsthop, sizeof(firsthop)) == -1) {
+ perror("connect");
+ exit(2);
+ }
+ alen = sizeof(source);
+ if (getsockname(probe_fd, (struct sockaddr*)&source, &alen) == -1) {
+ perror("getsockname");
+ exit(2);
+ }
+ source.sin6_port = 0;
+ close(probe_fd);
+
+#ifndef WITHOUT_IFADDRS
+ if (device) {
+ struct ifaddrs *ifa0, *ifa;
+
+ if (getifaddrs(&ifa0)) {
+ perror("getifaddrs");
+ exit(2);
+ }
+
+ for (ifa = ifa0; ifa; ifa = ifa->ifa_next) {
+ if (!ifa->ifa_addr || ifa->ifa_addr->sa_family != AF_INET6)
+ continue;
+ if (!strncmp(ifa->ifa_name, device, sizeof(device) - 1) &&
+ IN6_ARE_ADDR_EQUAL(&((struct sockaddr_in6 *)ifa->ifa_addr)->sin6_addr,
+ &source.sin6_addr))
+ break;
+ }
+ if (!ifa)
+ fprintf(stderr, "ping6: Warning: source address might be selected on device other than %s.\n", device);
+
+ freeifaddrs(ifa0);
+ }
+#endif
+ }
+#ifdef HAVE_SIN6_SCOPEID
+ else if (device && (IN6_IS_ADDR_LINKLOCAL(&source.sin6_addr) ||
+ IN6_IS_ADDR_MC_LINKLOCAL(&source.sin6_addr)))
+ source.sin6_scope_id = if_name2index(device);
+#endif
+
+ if (icmp_sock < 0) {
+ errno = socket_errno;
+ perror("ping: icmp open socket");
+ exit(2);
+ }
+
+ if (device) {
+ struct cmsghdr *cmsg;
+ struct in6_pktinfo *ipi;
+
+ cmsg = (struct cmsghdr*)(cmsgbuf+cmsglen);
+ cmsglen += CMSG_SPACE(sizeof(*ipi));
+ cmsg->cmsg_len = CMSG_LEN(sizeof(*ipi));
+ cmsg->cmsg_level = SOL_IPV6;
+ cmsg->cmsg_type = IPV6_PKTINFO;
+
+ ipi = (struct in6_pktinfo*)CMSG_DATA(cmsg);
+ memset(ipi, 0, sizeof(*ipi));
+ ipi->ipi6_ifindex = if_name2index(device);
+ }
+
+ if ((whereto.sin6_addr.s6_addr16[0]&htons(0xff00)) == htons (0xff00)) {
+ if (uid) {
+ if (interval < 1000) {
+ fprintf(stderr, "ping: multicast ping with too short interval.\n");
+ exit(2);
+ }
+ if (pmtudisc >= 0 && pmtudisc != IPV6_PMTUDISC_DO) {
+ fprintf(stderr, "ping: multicast ping does not fragment.\n");
+ exit(2);
+ }
+ }
+ if (pmtudisc < 0)
+ pmtudisc = IPV6_PMTUDISC_DO;
+ }
+
+ if (pmtudisc >= 0) {
+ if (setsockopt(icmp_sock, SOL_IPV6, IPV6_MTU_DISCOVER, &pmtudisc, sizeof(pmtudisc)) == -1) {
+ perror("ping: IPV6_MTU_DISCOVER");
+ exit(2);
+ }
+ }
+
+ if ((options&F_STRICTSOURCE) &&
+ bind(icmp_sock, (struct sockaddr*)&source, sizeof(source)) == -1) {
+ perror("ping: bind icmp socket");
+ exit(2);
+ }
+
+ if (datalen >= sizeof(struct timeval) && (ni_query < 0)) {
+ /* can we time transfer */
+ timing = 1;
+ }
+ packlen = datalen + 8 + 4096 + 40 + 8; /* 4096 for rthdr */
+ if (!(packet = (u_char *)malloc((u_int)packlen))) {
+ fprintf(stderr, "ping: out of memory.\n");
+ exit(2);
+ }
+
+ working_recverr = 1;
+ hold = 1;
+ if (setsockopt(icmp_sock, SOL_IPV6, IPV6_RECVERR, (char *)&hold, sizeof(hold))) {
+ fprintf(stderr, "WARNING: your kernel is veeery old. No problems.\n");
+ working_recverr = 0;
+ }
+
+ /* Estimate memory eaten by single packet. It is rough estimate.
+ * Actually, for small datalen's it depends on kernel side a lot. */
+ hold = datalen+8;
+ hold += ((hold+511)/512)*(40+16+64+160);
+ sock_setbufs(icmp_sock, hold);
+
+#ifdef __linux__
+ csum_offset = 2;
+ sz_opt = sizeof(int);
+
+ err = setsockopt(icmp_sock, SOL_RAW, IPV6_CHECKSUM, &csum_offset, sz_opt);
+ if (err < 0) {
+ /* checksum should be enabled by default and setting this
+ * option might fail anyway.
+ */
+ fprintf(stderr, "setsockopt(RAW_CHECKSUM) failed - try to continue.");
+ }
+#endif
+
+ /*
+ * select icmp echo reply as icmp type to receive
+ */
+
+ ICMP6_FILTER_SETBLOCKALL(&filter);
+
+ if (!working_recverr) {
+ 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);
+ }
+
+ if (niquery_is_enabled())
+ ICMP6_FILTER_SETPASS(ICMPV6_NI_REPLY, &filter);
+ else
+ ICMP6_FILTER_SETPASS(ICMP6_ECHO_REPLY, &filter);
+
+ err = setsockopt(icmp_sock, IPPROTO_ICMPV6, ICMP6_FILTER, &filter,
+ sizeof(struct icmp6_filter));
+
+ if (err < 0) {
+ perror("setsockopt(ICMP6_FILTER)");
+ exit(2);
+ }
+
+ if (options & F_NOLOOP) {
+ int loop = 0;
+ if (setsockopt(icmp_sock, IPPROTO_IPV6, IPV6_MULTICAST_LOOP,
+ &loop, sizeof(loop)) == -1) {
+ perror ("can't disable multicast loopback");
+ exit(2);
+ }
+ }
+ if (options & F_TTL) {
+ if (setsockopt(icmp_sock, IPPROTO_IPV6, IPV6_MULTICAST_HOPS,
+ &ttl, sizeof(ttl)) == -1) {
+ perror ("can't set multicast hop limit");
+ exit(2);
+ }
+ if (setsockopt(icmp_sock, IPPROTO_IPV6, IPV6_UNICAST_HOPS,
+ &ttl, sizeof(ttl)) == -1) {
+ perror ("can't set unicast hop limit");
+ exit(2);
+ }
+ }
+
+ if (1) {
+ int on = 1;
+ if (
+#ifdef IPV6_RECVHOPLIMIT
+ setsockopt(icmp_sock, IPPROTO_IPV6, IPV6_RECVHOPLIMIT,
+ &on, sizeof(on)) == -1 &&
+ setsockopt(icmp_sock, IPPROTO_IPV6, IPV6_2292HOPLIMIT,
+ &on, sizeof(on)) == -1
+#else
+ setsockopt(icmp_sock, IPPROTO_IPV6, IPV6_HOPLIMIT,
+ &on, sizeof(on)) == -1
+#endif
+ ){
+ perror ("can't receive hop limit");
+ exit(2);
+ }
+ }
+
+ if (options & F_TCLASS) {
+#ifdef IPV6_TCLASS
+ if (setsockopt(icmp_sock, IPPROTO_IPV6, IPV6_TCLASS,
+ &tclass, sizeof(tclass)) == -1) {
+ perror ("setsockopt(IPV6_TCLASS)");
+ exit(2);
+ }
+#else
+ fprintf(stderr, "Traffic class is not supported.\n");
+#endif
+ }
+
+ if (options&F_FLOWINFO) {
+#ifdef IPV6_FLOWINFO_SEND
+ int on = 1;
+#endif
+#ifdef IPV6_FLOWLABEL_MGR
+ char freq_buf[CMSG_ALIGN(sizeof(struct in6_flowlabel_req)) + cmsglen];
+ struct in6_flowlabel_req *freq = (struct in6_flowlabel_req *)freq_buf;
+ int freq_len = sizeof(*freq);
+#ifdef ENABLE_PING6_RTHDR
+ if (srcrt)
+ freq_len = CMSG_ALIGN(sizeof(*freq)) + srcrt->cmsg_len;
+#endif
+ memset(freq, 0, sizeof(*freq));
+ freq->flr_label = htonl(flowlabel & IPV6_FLOWINFO_FLOWLABEL);
+ freq->flr_action = IPV6_FL_A_GET;
+ freq->flr_flags = IPV6_FL_F_CREATE;
+ freq->flr_share = IPV6_FL_S_EXCL;
+ memcpy(&freq->flr_dst, &whereto.sin6_addr, 16);
+#ifdef ENABLE_PING6_RTHDR
+ if (srcrt)
+ memcpy(freq_buf + CMSG_ALIGN(sizeof(*freq)), srcrt, srcrt->cmsg_len);
+#endif
+ if (setsockopt(icmp_sock, IPPROTO_IPV6, IPV6_FLOWLABEL_MGR,
+ freq, freq_len) == -1) {
+ perror ("can't set flowlabel");
+ exit(2);
+ }
+ flowlabel = freq->flr_label;
+#ifdef ENABLE_PING6_RTHDR
+ if (srcrt) {
+ cmsglen = (char*)srcrt - (char*)cmsgbuf;
+ srcrt = NULL;
+ }
+#endif
+#else
+ fprintf(stderr, "Flow labels are not supported.\n");
+ exit(2);
+#endif
+
+#ifdef IPV6_FLOWINFO_SEND
+ whereto.sin6_flowinfo = flowlabel;
+ if (setsockopt(icmp_sock, IPPROTO_IPV6, IPV6_FLOWINFO_SEND,
+ &on, sizeof(on)) == -1) {
+ perror ("can't send flowinfo");
+ exit(2);
+ }
+#else
+ fprintf(stderr, "Flowinfo is not supported.\n");
+ exit(2);
+#endif
+ }
+
+ printf("PING %s(%s) ", hostname, pr_addr(&whereto.sin6_addr));
+ if (flowlabel)
+ printf(", flow 0x%05x, ", (unsigned)ntohl(flowlabel));
+ if (device || (options&F_STRICTSOURCE)) {
+ printf("from %s %s: ",
+ pr_addr_n(&source.sin6_addr), device ? : "");
+ }
+ printf("%d data bytes\n", datalen);
+
+ setup(icmp_sock);
+
+ drop_capabilities();
+
+ main_loop(icmp_sock, packet, packlen);
+}
+
+int receive_error_msg()
+{
+ int res;
+ char cbuf[512];
+ struct iovec iov;
+ struct msghdr msg;
+ struct cmsghdr *cmsg;
+ struct sock_extended_err *e;
+ struct icmp6_hdr icmph;
+ struct sockaddr_in6 target;
+ int net_errors = 0;
+ int local_errors = 0;
+ int saved_errno = errno;
+
+ iov.iov_base = &icmph;
+ iov.iov_len = sizeof(icmph);
+ msg.msg_name = (void*)&target;
+ msg.msg_namelen = sizeof(target);
+ msg.msg_iov = &iov;
+ msg.msg_iovlen = 1;
+ msg.msg_flags = 0;
+ msg.msg_control = cbuf;
+ msg.msg_controllen = sizeof(cbuf);
+
+ res = recvmsg(icmp_sock, &msg, MSG_ERRQUEUE|MSG_DONTWAIT);
+ if (res < 0)
+ goto out;
+
+ e = NULL;
+ for (cmsg = CMSG_FIRSTHDR(&msg); cmsg; cmsg = CMSG_NXTHDR(&msg, cmsg)) {
+ if (cmsg->cmsg_level == SOL_IPV6) {
+ if (cmsg->cmsg_type == IPV6_RECVERR)
+ e = (struct sock_extended_err *)CMSG_DATA(cmsg);
+ }
+ }
+ if (e == NULL)
+ abort();
+
+ if (e->ee_origin == SO_EE_ORIGIN_LOCAL) {
+ local_errors++;
+ if (options & F_QUIET)
+ goto out;
+ if (options & F_FLOOD)
+ write_stdout("E", 1);
+ else if (e->ee_errno != EMSGSIZE)
+ fprintf(stderr, "ping: local error: %s\n", strerror(e->ee_errno));
+ else
+ fprintf(stderr, "ping: local error: Message too long, mtu=%u\n", e->ee_info);
+ nerrors++;
+ } else if (e->ee_origin == SO_EE_ORIGIN_ICMP6) {
+ struct sockaddr_in6 *sin6 = (struct sockaddr_in6*)(e+1);
+
+ if (res < sizeof(icmph) ||
+ memcmp(&target.sin6_addr, &whereto.sin6_addr, 16) ||
+ icmph.icmp6_type != ICMP6_ECHO_REQUEST ||
+ icmph.icmp6_id != ident) {
+ /* Not our error, not an error at all. Clear. */
+ saved_errno = 0;
+ goto out;
+ }
+
+ net_errors++;
+ nerrors++;
+ if (options & F_QUIET)
+ goto out;
+ if (options & F_FLOOD) {
+ write_stdout("\bE", 2);
+ } else {
+ print_timestamp();
+ printf("From %s icmp_seq=%u ", pr_addr(&sin6->sin6_addr), ntohs(icmph.icmp6_seq));
+ pr_icmph(e->ee_type, e->ee_code, e->ee_info);
+ putchar('\n');
+ fflush(stdout);
+ }
+ }
+
+out:
+ errno = saved_errno;
+ return net_errors ? : -local_errors;
+}
+
+/*
+ * pinger --
+ * Compose and transmit an ICMP ECHO REQUEST packet. The IP packet
+ * will be added on by the kernel. The ID field is our UNIX process ID,
+ * and the sequence number is an ascending integer. The first 8 bytes
+ * of the data portion are used to hold a UNIX "timeval" struct in VAX
+ * byte-order, to compute the round-trip time.
+ */
+int build_echo(__u8 *_icmph)
+{
+ struct icmp6_hdr *icmph;
+ int cc;
+
+ icmph = (struct icmp6_hdr *)_icmph;
+ icmph->icmp6_type = ICMP6_ECHO_REQUEST;
+ icmph->icmp6_code = 0;
+ icmph->icmp6_cksum = 0;
+ icmph->icmp6_seq = htons(ntransmitted+1);
+ icmph->icmp6_id = ident;
+
+ if (timing)
+ gettimeofday((struct timeval *)&outpack[8],
+ (struct timezone *)NULL);
+
+ cc = datalen + 8; /* skips ICMP portion */
+
+ return cc;
+}
+
+
+int build_niquery(__u8 *_nih)
+{
+ struct ni_hdr *nih;
+ int cc;
+
+ nih = (struct ni_hdr *)_nih;
+ nih->ni_cksum = 0;
+
+ nih->ni_type = ICMPV6_NI_QUERY;
+ cc = sizeof(*nih);
+ datalen = 0;
+
+ niquery_fill_nonce(ntransmitted + 1, nih->ni_nonce);
+ nih->ni_code = ni_subject_type;
+ nih->ni_qtype = htons(ni_query);
+ nih->ni_flags = ni_flag;
+ memcpy(nih + 1, ni_subject, ni_subject_len);
+ cc += ni_subject_len;
+
+ return cc;
+}
+
+int send_probe(void)
+{
+ int len, cc;
+
+ rcvd_clear(ntransmitted + 1);
+
+ if (niquery_is_enabled())
+ len = build_niquery(outpack);
+ else
+ len = build_echo(outpack);
+
+ if (cmsglen == 0) {
+ cc = sendto(icmp_sock, (char *)outpack, len, confirm,
+ (struct sockaddr *) &whereto,
+ sizeof(struct sockaddr_in6));
+ } else {
+ struct msghdr mhdr;
+ struct iovec iov;
+
+ iov.iov_len = len;
+ iov.iov_base = outpack;
+
+ memset(&mhdr, 0, sizeof(mhdr));
+ mhdr.msg_name = &whereto;
+ mhdr.msg_namelen = sizeof(struct sockaddr_in6);
+ mhdr.msg_iov = &iov;
+ mhdr.msg_iovlen = 1;
+ mhdr.msg_control = cmsgbuf;
+ mhdr.msg_controllen = cmsglen;
+
+ cc = sendmsg(icmp_sock, &mhdr, confirm);
+ }
+ confirm = 0;
+
+ return (cc == len ? 0 : cc);
+}
+
+void pr_echo_reply(__u8 *_icmph, int cc)
+{
+ struct icmp6_hdr *icmph = (struct icmp6_hdr *) _icmph;
+ printf(" icmp_seq=%u", ntohs(icmph->icmp6_seq));
+};
+
+static void putchar_safe(char c)
+{
+ if (isprint(c))
+ putchar(c);
+ else
+ printf("\\%03o", c);
+}
+
+void pr_niquery_reply_name(struct ni_hdr *nih, int len)
+{
+ __u8 *h = (__u8 *)(nih + 1);
+ __u8 *p = h + 4;
+ __u8 *end = (__u8 *)nih + len;
+ int continued = 0;
+ char buf[1024];
+ int ret;
+
+ len -= sizeof(struct ni_hdr) + 4;
+
+ if (len < 0) {
+ printf(" parse error (too short)");
+ return;
+ }
+ while (p < end) {
+ int fqdn = 1;
+ int i;
+
+ memset(buf, 0xff, sizeof(buf));
+
+ if (continued)
+ putchar(',');
+
+ ret = dn_expand(h, end, p, buf, sizeof(buf));
+ if (ret < 0) {
+ printf(" parse error (truncated)");
+ break;
+ }
+ if (p + ret < end && *(p + ret) == '\0')
+ fqdn = 0;
+
+ putchar(' ');
+ for (i = 0; i < strlen(buf); i++)
+ putchar_safe(buf[i]);
+ if (fqdn)
+ putchar('.');
+
+ p += ret + !fqdn;
+
+ continued = 1;
+ }
+}
+
+void pr_niquery_reply_addr(struct ni_hdr *nih, int len)
+{
+ __u8 *h = (__u8 *)(nih + 1);
+ __u8 *p = h + 4;
+ __u8 *end = (__u8 *)nih + len;
+ int af;
+ int aflen;
+ int continued = 0;
+ int truncated;
+ char buf[1024];
+
+ switch (ntohs(nih->ni_qtype)) {
+ case NI_QTYPE_IPV4ADDR:
+ af = AF_INET;
+ aflen = sizeof(struct in_addr);
+ truncated = nih->ni_flags & NI_IPV6ADDR_F_TRUNCATE;
+ break;
+ case NI_QTYPE_IPV6ADDR:
+ af = AF_INET6;
+ aflen = sizeof(struct in6_addr);
+ truncated = nih->ni_flags & NI_IPV4ADDR_F_TRUNCATE;
+ break;
+ default:
+ /* should not happen */
+ af = aflen = truncated = 0;
+ }
+ p = h;
+ if (len < 0) {
+ printf(" parse error (too short)");
+ return;
+ }
+
+ while (p < end) {
+ if (continued)
+ putchar(',');
+
+ if (p + sizeof(__u32) + aflen > end) {
+ printf(" parse error (truncated)");
+ break;
+ }
+ if (!inet_ntop(af, p + sizeof(__u32), buf, sizeof(buf)))
+ printf(" unexpeced error in inet_ntop(%s)",
+ strerror(errno));
+ else
+ printf(" %s", buf);
+ p += sizeof(__u32) + aflen;
+
+ continued = 1;
+ }
+ if (truncated)
+ printf(" (truncated)");
+}
+
+void pr_niquery_reply(__u8 *_nih, int len)
+{
+ struct ni_hdr *nih = (struct ni_hdr *)_nih;
+
+ switch (nih->ni_code) {
+ case NI_SUCCESS:
+ switch (ntohs(nih->ni_qtype)) {
+ case NI_QTYPE_NAME:
+ pr_niquery_reply_name(nih, len);
+ break;
+ case NI_QTYPE_IPV4ADDR:
+ case NI_QTYPE_IPV6ADDR:
+ pr_niquery_reply_addr(nih, len);
+ break;
+ default:
+ printf(" unknown qtype(0x%02x)", ntohs(nih->ni_qtype));
+ }
+ break;
+ case NI_REFUSED:
+ printf(" refused");
+ break;
+ case NI_UNKNOWN:
+ printf(" unknown");
+ break;
+ default:
+ printf(" unknown code(%02x)", ntohs(nih->ni_code));
+ }
+ printf("; seq=%u;", ntohsp((__u16*)nih->ni_nonce));
+}
+
+/*
+ * parse_reply --
+ * Print out the packet, if it came from us. This logic is necessary
+ * because ALL readers of the ICMP socket get a copy of ALL ICMP packets
+ * which arrive ('tis only fair). This permits multiple copies of this
+ * program to be run without having intermingled output (or statistics!).
+ */
+int
+parse_reply(struct msghdr *msg, int cc, void *addr, struct timeval *tv)
+{
+ struct sockaddr_in6 *from = addr;
+ __u8 *buf = msg->msg_iov->iov_base;
+ struct cmsghdr *c;
+ struct icmp6_hdr *icmph;
+ int hops = -1;
+
+ for (c = CMSG_FIRSTHDR(msg); c; c = CMSG_NXTHDR(msg, c)) {
+ if (c->cmsg_level != SOL_IPV6)
+ continue;
+ switch(c->cmsg_type) {
+ case IPV6_HOPLIMIT:
+#ifdef IPV6_2292HOPLIMIT
+ case IPV6_2292HOPLIMIT:
+#endif
+ if (c->cmsg_len < CMSG_LEN(sizeof(int)))
+ continue;
+ memcpy(&hops, CMSG_DATA(c), sizeof(hops));
+ }
+ }
+
+
+ /* Now the ICMP part */
+
+ icmph = (struct icmp6_hdr *) buf;
+ if (cc < 8) {
+ if (options & F_VERBOSE)
+ fprintf(stderr, "ping: packet too short (%d bytes)\n", cc);
+ return 1;
+ }
+
+ if (icmph->icmp6_type == ICMP6_ECHO_REPLY) {
+ if (icmph->icmp6_id != ident)
+ return 1;
+ if (gather_statistics((__u8*)icmph, sizeof(*icmph), cc,
+ ntohs(icmph->icmp6_seq),
+ hops, 0, tv, pr_addr(&from->sin6_addr),
+ pr_echo_reply))
+ return 0;
+ } else if (icmph->icmp6_type == ICMPV6_NI_REPLY) {
+ struct ni_hdr *nih = (struct ni_hdr *)icmph;
+ int seq = niquery_check_nonce(nih->ni_nonce);
+ if (seq < 0)
+ return 1;
+ if (gather_statistics((__u8*)icmph, sizeof(*icmph), cc,
+ seq,
+ hops, 0, tv, pr_addr(&from->sin6_addr),
+ pr_niquery_reply))
+ return 0;
+ } else {
+ int nexthdr;
+ struct ip6_hdr *iph1 = (struct ip6_hdr*)(icmph+1);
+ struct icmp6_hdr *icmph1 = (struct icmp6_hdr *)(iph1+1);
+
+ /* We must not ever fall here. All the messages but
+ * echo reply are blocked by filter and error are
+ * received with IPV6_RECVERR. Ugly code is preserved
+ * however, just to remember what crap we avoided
+ * using RECVRERR. :-)
+ */
+
+ if (cc < 8+sizeof(struct ip6_hdr)+8)
+ return 1;
+
+ if (memcmp(&iph1->ip6_dst, &whereto.sin6_addr, 16))
+ return 1;
+
+ nexthdr = iph1->ip6_nxt;
+
+ if (nexthdr == 44) {
+ nexthdr = *(__u8*)icmph1;
+ icmph1++;
+ }
+ if (nexthdr == IPPROTO_ICMPV6) {
+ if (icmph1->icmp6_type != ICMP6_ECHO_REQUEST ||
+ icmph1->icmp6_id != ident)
+ return 1;
+ acknowledge(ntohs(icmph1->icmp6_seq));
+ if (working_recverr)
+ return 0;
+ nerrors++;
+ if (options & F_FLOOD) {
+ write_stdout("\bE", 2);
+ return 0;
+ }
+ print_timestamp();
+ printf("From %s: icmp_seq=%u ", pr_addr(&from->sin6_addr), ntohs(icmph1->icmp6_seq));
+ } else {
+ /* We've got something other than an ECHOREPLY */
+ if (!(options & F_VERBOSE) || uid)
+ return 1;
+ print_timestamp();
+ printf("From %s: ", pr_addr(&from->sin6_addr));
+ }
+ pr_icmph(icmph->icmp6_type, icmph->icmp6_code, ntohl(icmph->icmp6_mtu));
+ }
+
+ if (!(options & F_FLOOD)) {
+ if (options & F_AUDIBLE)
+ putchar('\a');
+ putchar('\n');
+ fflush(stdout);
+ } else {
+ putchar('\a');
+ fflush(stdout);
+ }
+ return 0;
+}
+
+
+int pr_icmph(__u8 type, __u8 code, __u32 info)
+{
+ switch(type) {
+ case ICMP6_DST_UNREACH:
+ printf("Destination unreachable: ");
+ switch (code) {
+ case ICMP6_DST_UNREACH_NOROUTE:
+ printf("No route");
+ break;
+ case ICMP6_DST_UNREACH_ADMIN:
+ printf("Administratively prohibited");
+ break;
+ case ICMP6_DST_UNREACH_BEYONDSCOPE:
+ printf("Beyond scope of source address");
+ break;
+ case ICMP6_DST_UNREACH_ADDR:
+ printf("Address unreachable");
+ break;
+ case ICMP6_DST_UNREACH_NOPORT:
+ printf("Port unreachable");
+ break;
+ default:
+ printf("Unknown code %d", code);
+ break;
+ }
+ break;
+ case ICMP6_PACKET_TOO_BIG:
+ printf("Packet too big: mtu=%u", info);
+ if (code)
+ printf(", code=%d", code);
+ break;
+ case ICMP6_TIME_EXCEEDED:
+ printf("Time exceeded: ");
+ if (code == ICMP6_TIME_EXCEED_TRANSIT)
+ printf("Hop limit");
+ else if (code == ICMP6_TIME_EXCEED_REASSEMBLY)
+ printf("Defragmentation failure");
+ else
+ printf("code %d", code);
+ break;
+ case ICMP6_PARAM_PROB:
+ printf("Parameter problem: ");
+ if (code == ICMP6_PARAMPROB_HEADER)
+ printf("Wrong header field ");
+ else if (code == ICMP6_PARAMPROB_NEXTHEADER)
+ printf("Unknown header ");
+ else if (code == ICMP6_PARAMPROB_OPTION)
+ printf("Unknown option ");
+ else
+ printf("code %d ", code);
+ printf ("at %u", info);
+ break;
+ case ICMP6_ECHO_REQUEST:
+ printf("Echo request");
+ break;
+ case ICMP6_ECHO_REPLY:
+ printf("Echo reply");
+ break;
+ case MLD_LISTENER_QUERY:
+ printf("MLD Query");
+ break;
+ case MLD_LISTENER_REPORT:
+ printf("MLD Report");
+ break;
+ case MLD_LISTENER_REDUCTION:
+ printf("MLD Reduction");
+ break;
+ default:
+ printf("unknown icmp type: %u", type);
+
+ }
+ return 0;
+}
+
+#include <linux/filter.h>
+
+void install_filter(void)
+{
+ static int once;
+ static struct sock_filter insns[] = {
+ BPF_STMT(BPF_LD|BPF_H|BPF_ABS, 4), /* Load icmp echo ident */
+ BPF_JUMP(BPF_JMP|BPF_JEQ|BPF_K, 0xAAAA, 0, 1), /* Ours? */
+ BPF_STMT(BPF_RET|BPF_K, ~0U), /* Yes, it passes. */
+ BPF_STMT(BPF_LD|BPF_B|BPF_ABS, 0), /* Load icmp type */
+ BPF_JUMP(BPF_JMP|BPF_JEQ|BPF_K, ICMP6_ECHO_REPLY, 1, 0), /* Echo? */
+ BPF_STMT(BPF_RET|BPF_K, ~0U), /* No. It passes. This must not happen. */
+ BPF_STMT(BPF_RET|BPF_K, 0), /* Echo with wrong ident. Reject. */
+ };
+ static struct sock_fprog filter = {
+ sizeof insns / sizeof(insns[0]),
+ insns
+ };
+
+ if (once)
+ return;
+ once = 1;
+
+ /* Patch bpflet for current identifier. */
+ insns[1] = (struct sock_filter)BPF_JUMP(BPF_JMP|BPF_JEQ|BPF_K, htons(ident), 0, 1);
+
+ if (setsockopt(icmp_sock, SOL_SOCKET, SO_ATTACH_FILTER, &filter, sizeof(filter)))
+ perror("WARNING: failed to install socket filter\n");
+}
+
+
+/*
+ * pr_addr --
+ * Return an ascii host address as a dotted quad and optionally with
+ * a hostname.
+ */
+char * pr_addr(struct in6_addr *addr)
+{
+ struct hostent *hp = NULL;
+ static char *s;
+
+#ifdef USE_IDN
+ free(s);
+#endif
+
+ in_pr_addr = !setjmp(pr_addr_jmp);
+
+ if (!(exiting || options&F_NUMERIC))
+ hp = gethostbyaddr((__u8*)addr, sizeof(struct in6_addr), AF_INET6);
+
+ in_pr_addr = 0;
+
+ if (!hp
+#ifdef USE_IDN
+ || idna_to_unicode_lzlz(hp->h_name, &s, 0) != IDNA_SUCCESS
+#endif
+ )
+ s = NULL;
+
+ return hp ? (s ? s : hp->h_name) : pr_addr_n(addr);
+}
+
+char * pr_addr_n(struct in6_addr *addr)
+{
+ static char str[64];
+ inet_ntop(AF_INET6, addr, str, sizeof(str));
+ return str;
+}
+
+#define USAGE_NEWLINE "\n "
+
+void usage(void)
+{
+ fprintf(stderr,
+ "Usage: ping6"
+ " [-"
+ "aAbBdDfhLnOqrRUvV"
+ "]"
+ " [-c count]"
+ " [-i interval]"
+ " [-I interface]"
+ USAGE_NEWLINE
+ " [-l preload]"
+ " [-m mark]"
+ " [-M pmtudisc_option]"
+ USAGE_NEWLINE
+ " [-N nodeinfo_option]"
+ " [-p pattern]"
+ " [-Q tclass]"
+ " [-s packetsize]"
+ USAGE_NEWLINE
+ " [-S sndbuf]"
+ " [-t ttl]"
+ " [-T timestamp_option]"
+ " [-w deadline]"
+ USAGE_NEWLINE
+ " [-W timeout]"
+#ifdef ENABLE_PING6_RTHDR
+ " [hop1 ...]"
+#endif
+ " destination"
+ "\n"
+ );
+ exit(2);
+}
diff --git a/ping6_niquery.h b/ping6_niquery.h
new file mode 100644
index 0000000..fa6624a
--- /dev/null
+++ b/ping6_niquery.h
@@ -0,0 +1,49 @@
+#include <asm/byteorder.h>
+
+#define NI_NONCE_SIZE 8
+
+/* Node Information Query */
+struct ni_hdr {
+ struct icmp6_hdr ni_u;
+ __u8 ni_nonce[NI_NONCE_SIZE];
+};
+
+#define ni_type ni_u.icmp6_type
+#define ni_code ni_u.icmp6_code
+#define ni_cksum ni_u.icmp6_cksum
+#define ni_qtype ni_u.icmp6_data16[0]
+#define ni_flags ni_u.icmp6_data16[1]
+
+/* Types */
+#ifndef ICMPV6_NI_QUERY
+# define ICMPV6_NI_QUERY 139
+# define ICMPV6_NI_REPLY 140
+#endif
+
+/* Query Codes */
+#define NI_SUBJ_IPV6 0
+#define NI_SUBJ_NAME 1
+#define NI_SUBJ_IPV4 2
+
+/* Reply Codes */
+#define NI_SUCCESS 0
+#define NI_REFUSED 1
+#define NI_UNKNOWN 2
+
+/* Qtypes */
+#define NI_QTYPE_NOOP 0
+#define NI_QTYPE_NAME 2
+#define NI_QTYPE_IPV6ADDR 3
+#define NI_QTYPE_IPV4ADDR 4
+
+/* Flags */
+#define NI_IPV6ADDR_F_TRUNCATE __constant_cpu_to_be16(0x0001)
+#define NI_IPV6ADDR_F_ALL __constant_cpu_to_be16(0x0002)
+#define NI_IPV6ADDR_F_COMPAT __constant_cpu_to_be16(0x0004)
+#define NI_IPV6ADDR_F_LINKLOCAL __constant_cpu_to_be16(0x0008)
+#define NI_IPV6ADDR_F_SITELOCAL __constant_cpu_to_be16(0x0010)
+#define NI_IPV6ADDR_F_GLOBAL __constant_cpu_to_be16(0x0020)
+
+#define NI_IPV4ADDR_F_TRUNCATE NI_IPV6ADDR_F_TRUNCATE
+#define NI_IPV4ADDR_F_ALL NI_IPV6ADDR_F_ALL
+
diff --git a/ping_common.c b/ping_common.c
index e8ae6b8..5d00a69 100644
--- a/ping_common.c
+++ b/ping_common.c
@@ -1,17 +1,18 @@
#include "ping_common.h"
#include <ctype.h>
#include <sched.h>
+#include <math.h>
int options;
+int mark;
int sndbuf;
int ttl;
int rtt;
int rtt_addend;
__u16 acked;
-int mx_dup_ck = MAX_DUP_CHK;
-char rcvd_tbl[MAX_DUP_CHK / 8];
+struct rcvd_table rcvd_tbl;
/* counters */
@@ -29,6 +30,8 @@ struct timeval start_time, cur_time;
volatile int exiting;
volatile int status_snapshot;
int confirm = 0;
+volatile int in_pr_addr = 0; /* pr_addr() is executing */
+jmp_buf pr_addr_jmp;
/* Stupid workarounds for bugs/missing functionality in older linuces.
* confirm_flag fixes refusing service of kernels without MSG_CONFIRM.
@@ -55,10 +58,145 @@ int datalen = DEFDATALEN;
char *hostname;
int uid;
+uid_t euid;
int ident; /* process id to identify our packets */
static int screen_width = INT_MAX;
+#define ARRAY_SIZE(a) (sizeof(a) / sizeof(a[0]))
+
+#ifdef CAPABILITIES
+static cap_value_t cap_raw = CAP_NET_RAW;
+static cap_value_t cap_admin = CAP_NET_ADMIN;
+#endif
+
+void limit_capabilities(void)
+{
+#ifdef CAPABILITIES
+ cap_t cap_cur_p;
+ cap_t cap_p;
+ cap_flag_value_t cap_ok;
+
+ cap_cur_p = cap_get_proc();
+ if (!cap_cur_p) {
+ perror("ping: cap_get_proc");
+ exit(-1);
+ }
+
+ cap_p = cap_init();
+ if (!cap_p) {
+ perror("ping: cap_init");
+ exit(-1);
+ }
+
+ cap_ok = CAP_CLEAR;
+ cap_get_flag(cap_cur_p, CAP_NET_ADMIN, CAP_PERMITTED, &cap_ok);
+
+ if (cap_ok != CAP_CLEAR)
+ cap_set_flag(cap_p, CAP_PERMITTED, 1, &cap_admin, CAP_SET);
+
+ cap_ok = CAP_CLEAR;
+ cap_get_flag(cap_cur_p, CAP_NET_RAW, CAP_PERMITTED, &cap_ok);
+
+ if (cap_ok != CAP_CLEAR)
+ cap_set_flag(cap_p, CAP_PERMITTED, 1, &cap_raw, CAP_SET);
+
+ if (cap_set_proc(cap_p) < 0) {
+ perror("ping: cap_set_proc");
+ exit(-1);
+ }
+
+ if (prctl(PR_SET_KEEPCAPS, 1) < 0) {
+ perror("ping: prctl");
+ exit(-1);
+ }
+
+ if (setuid(getuid()) < 0) {
+ perror("setuid");
+ exit(-1);
+ }
+
+ if (prctl(PR_SET_KEEPCAPS, 0) < 0) {
+ perror("ping: prctl");
+ exit(-1);
+ }
+
+ cap_free(cap_p);
+ cap_free(cap_cur_p);
+#endif
+ uid = getuid();
+ euid = geteuid();
+#ifndef CAPABILITIES
+ if (seteuid(uid)) {
+ perror("ping: setuid");
+ exit(-1);
+ }
+#endif
+}
+
+#ifdef CAPABILITIES
+int modify_capability(cap_value_t cap, cap_flag_value_t on)
+{
+ cap_t cap_p = cap_get_proc();
+ cap_flag_value_t cap_ok;
+ int rc = -1;
+
+ if (!cap_p) {
+ perror("ping: cap_get_proc");
+ goto out;
+ }
+
+ cap_ok = CAP_CLEAR;
+ cap_get_flag(cap_p, cap, CAP_PERMITTED, &cap_ok);
+ if (cap_ok == CAP_CLEAR) {
+ rc = on ? -1 : 0;
+ goto out;
+ }
+
+ cap_set_flag(cap_p, CAP_EFFECTIVE, 1, &cap, on);
+
+ if (cap_set_proc(cap_p) < 0) {
+ perror("ping: cap_set_proc");
+ goto out;
+ }
+
+ cap_free(cap_p);
+
+ rc = 0;
+out:
+ if (cap_p)
+ cap_free(cap_p);
+ return rc;
+}
+#else
+int modify_capability(int on)
+{
+ if (seteuid(on ? euid : getuid())) {
+ perror("seteuid");
+ return -1;
+ }
+
+ return 0;
+}
+#endif
+
+void drop_capabilities(void)
+{
+#ifdef CAPABILITIES
+ cap_t cap = cap_init();
+ if (cap_set_proc(cap) < 0) {
+ perror("ping: cap_set_proc");
+ exit(-1);
+ }
+ cap_free(cap);
+#else
+ if (setuid(getuid())) {
+ perror("ping: setuid");
+ exit(-1);
+ }
+#endif
+}
+
/* Fills all the outpack, excluding ICMP header, but _including_
* timestamp area with supplied pattern.
*/
@@ -67,7 +205,11 @@ static void fill(char *patp)
int ii, jj, kk;
int pat[16];
char *cp;
- char *bp = outpack+8;
+ u_char *bp = outpack+8;
+
+#ifdef USE_IDN
+ setlocale(LC_ALL, "C");
+#endif
for (cp = patp; *cp; cp++) {
if (!isxdigit(*cp)) {
@@ -93,6 +235,10 @@ static void fill(char *patp)
printf("%02x", bp[jj] & 0xFF);
printf("\n");
}
+
+#ifdef USE_IDN
+ setlocale(LC_ALL, "");
+#endif
}
void common_options(int ch)
@@ -114,31 +260,37 @@ void common_options(int ch)
case 'd':
options |= F_SO_DEBUG;
break;
- case 'f':
- options |= F_FLOOD;
- setbuf(stdout, (char *)NULL);
+ case 'D':
+ options |= F_PTIMEOFDAY;
break;
case 'i': /* wait between sending packets */
{
- if (strchr(optarg, '.')) {
- float t;
- if (sscanf(optarg, "%f", &t) != 1) {
- fprintf(stderr, "ping: bad timing interval.\n");
- exit(2);
- }
- interval = (int)(t*1000);
- } else if (sscanf(optarg, "%d", &interval) == 1) {
- interval *= 1000;
- } else {
- fprintf(stderr, "ping: bad timing interval.\n");
+ double dbl;
+ char *ep;
+
+ errno = 0;
+ dbl = strtod(optarg, &ep);
+
+ if (errno || *ep != '\0' ||
+ !finite(dbl) || dbl < 0.0 || dbl >= (double)INT_MAX / 1000 - 1.0) {
+ fprintf(stderr, "ping: bad timing interval\n");
exit(2);
}
- if (interval < 0) {
- fprintf(stderr, "ping: bad timing interval.\n");
+ interval = (int)(dbl * 1000);
+
+ options |= F_INTERVAL;
+ break;
+ }
+ case 'm':
+ {
+ char *endp;
+ mark = (int)strtoul(optarg, &endp, 10);
+ if (mark < 0 || *endp != '\0') {
+ fprintf(stderr, "mark cannot be negative\n");
exit(2);
}
- options |= F_INTERVAL;
+ options |= F_MARK;
break;
}
case 'w':
@@ -151,16 +303,19 @@ void common_options(int ch)
case 'l':
preload = atoi(optarg);
if (preload <= 0) {
- fprintf(stderr, "ping: bad preload value, should be 1..%d\n", mx_dup_ck);
+ fprintf(stderr, "ping: bad preload value, should be 1..%d\n", MAX_DUP_CHK);
exit(2);
}
- if (preload > mx_dup_ck)
- preload = mx_dup_ck;
+ if (preload > MAX_DUP_CHK)
+ preload = MAX_DUP_CHK;
if (uid && preload > 3) {
fprintf(stderr, "ping: cannot set preload to value > 3\n");
exit(2);
}
break;
+ case 'O':
+ options |= F_OUTSTANDING;
+ break;
case 'S':
sndbuf = atoi(optarg);
if (sndbuf <= 0) {
@@ -168,6 +323,10 @@ void common_options(int ch)
exit(2);
}
break;
+ case 'f':
+ options |= F_FLOOD;
+ setbuf(stdout, (char *)NULL);
+ /* fallthrough to numeric - avoid gethostbyaddr during flood */
case 'n':
options |= F_NUMERIC;
break;
@@ -187,6 +346,11 @@ void common_options(int ch)
fprintf(stderr, "ping: illegal negative packet size %d.\n", datalen);
exit(2);
}
+ if (datalen > maxpacket - 8) {
+ fprintf(stderr, "ping: packet size too large: %d\n",
+ datalen);
+ exit(2);
+ }
break;
case 'v':
options |= F_VERBOSE;
@@ -217,7 +381,7 @@ void common_options(int ch)
lingertime *= 1000;
break;
case 'V':
- printf("ping utility, iputils-ss%s\n", SNAPSHOT);
+ printf("ping utility, iputils-%s\n", SNAPSHOT);
exit(0);
default:
abort();
@@ -228,6 +392,8 @@ void common_options(int ch)
static void sigexit(int signo)
{
exiting = 1;
+ if (in_pr_addr)
+ longjmp(pr_addr_jmp, 0);
}
static void sigstatus(int signo)
@@ -264,7 +430,7 @@ int __schedule_exit(int next)
static inline void update_interval(void)
{
- int est = rtt ? rtt/8 : interval*1000;
+ int est = rtt ? rtt/8 : interval*1000;
interval = (est+rtt_addend+500)/1000;
if (uid && interval < MINUSERINTERVAL)
@@ -272,6 +438,19 @@ static inline void update_interval(void)
}
/*
+ * Print timestamp
+ */
+void print_timestamp(void)
+{
+ if (options & F_PTIMEOFDAY) {
+ struct timeval tv;
+ gettimeofday(&tv, NULL);
+ printf("[%lu.%06lu] ",
+ (unsigned long)tv.tv_sec, (unsigned long)tv.tv_usec);
+ }
+}
+
+/*
* pinger --
* Compose and transmit an ICMP ECHO REQUEST packet. The IP packet
* will be added on by the kernel. The ID field is our UNIX process ID,
@@ -285,7 +464,7 @@ int pinger(void)
static int tokens;
int i;
- /* Have we already sent enough? If we have, return an arbitrary positive value. */
+ /* Have we already sent enough? If we have, return an arbitrary positive value. */
if (exiting || (npackets && ntransmitted >= npackets && !deadline))
return 1000;
@@ -316,6 +495,14 @@ int pinger(void)
tokens = ntokens - interval;
}
+ if (options & F_OUTSTANDING) {
+ if (ntransmitted > 0 && !rcvd_test(ntransmitted)) {
+ print_timestamp();
+ printf("no answer yet for icmp_seq=%lu\n", (ntransmitted % MAX_DUP_CHK));
+ fflush(stdout);
+ }
+ }
+
resend:
i = send_probe();
@@ -327,7 +514,7 @@ resend:
* high preload or pipe size is very confusing. */
if ((preload < screen_width && pipesize < screen_width) ||
in_flight() < screen_width)
- write(STDOUT_FILENO, ".", 1);
+ write_stdout(".", 1);
}
return interval - tokens;
}
@@ -380,7 +567,7 @@ resend:
if (i == 0 && !(options & F_QUIET)) {
if (options & F_FLOOD)
- write(STDOUT_FILENO, "E", 1);
+ write_stdout("E", 1);
else
perror("ping: sendmsg");
}
@@ -393,7 +580,7 @@ resend:
void sock_setbufs(int icmp_sock, int alloc)
{
int rcvbuf, hold;
- int tmplen = sizeof(hold);
+ socklen_t tmplen = sizeof(hold);
if (!sndbuf)
sndbuf = alloc;
@@ -415,6 +602,7 @@ void setup(int icmp_sock)
{
int hold;
struct timeval tv;
+ sigset_t sset;
if ((options & F_FLOOD) && !(options & F_INTERVAL))
interval = 0;
@@ -442,6 +630,20 @@ void setup(int icmp_sock)
fprintf(stderr, "Warning: no SO_TIMESTAMP support, falling back to SIOCGSTAMP\n");
}
#endif
+ if (options & F_MARK) {
+ int ret;
+
+ enable_capability_admin();
+ ret = setsockopt(icmp_sock, SOL_SOCKET, SO_MARK, &mark, sizeof(mark));
+ disable_capability_admin();
+
+ if (ret == -1) {
+ /* we probably dont wanna exit since old kernels
+ * dont support mark ..
+ */
+ fprintf(stderr, "Warning: Failed to set mark %d\n", mark);
+ }
+ }
/* Set some SNDTIMEO to prevent blocking forever
* on sends, when device is too slow or stalls. Just put limit
@@ -464,7 +666,7 @@ void setup(int icmp_sock)
if (!(options & F_PINGFILLED)) {
int i;
- char *p = outpack+8;
+ u_char *p = outpack+8;
/* Do not forget about case of small datalen,
* fill timestamp area too!
@@ -474,12 +676,15 @@ void setup(int icmp_sock)
}
if (!ident)
- ident = getpid() & 0xFFFF;
+ ident = htons(getpid() & 0xFFFF);
set_signal(SIGINT, sigexit);
set_signal(SIGALRM, sigexit);
set_signal(SIGQUIT, sigstatus);
+ sigemptyset(&sset);
+ sigprocmask(SIG_SETMASK, &sset, NULL);
+
gettimeofday(&start_time, NULL);
if (deadline) {
@@ -539,7 +744,7 @@ void main_loop(int icmp_sock, __u8 *packet, int packlen)
* If next<=0 send now or as soon as possible. */
/* Technical part. Looks wicked. Could be dropped,
- * if everyone used the newest kernel. :-)
+ * if everyone used the newest kernel. :-)
* Its purpose is:
* 1. Provide intervals less than resolution of scheduler.
* Solution: spinning.
@@ -550,8 +755,8 @@ void main_loop(int icmp_sock, __u8 *packet, int packlen)
int recv_expected = in_flight();
/* If we are here, recvmsg() is unable to wait for
- * required timeout. */
- if (1000*next <= 1000000/(int)HZ) {
+ * required timeout. */
+ if (1000 % HZ == 0 ? next <= 1000 / HZ : (next < INT_MAX / HZ && next * HZ <= 1000)) {
/* Very short timeout... So, if we wait for
* something, we sleep for MININTERVAL.
* Otherwise, spin! */
@@ -587,6 +792,7 @@ void main_loop(int icmp_sock, __u8 *packet, int packlen)
* destined to other running pings. */
iov.iov_len = packlen;
+ memset(&msg, 0, sizeof(msg));
msg.msg_name = addrbuf;
msg.msg_namelen = sizeof(addrbuf);
msg.msg_iov = &iov;
@@ -630,7 +836,7 @@ void main_loop(int icmp_sock, __u8 *packet, int packlen)
not_ours = parse_reply(&msg, cc, addrbuf, recv_timep);
}
- /* See? ... someone runs another ping on this host. */
+ /* See? ... someone runs another ping on this host. */
if (not_ours)
install_filter();
@@ -647,11 +853,14 @@ void main_loop(int icmp_sock, __u8 *packet, int packlen)
finish();
}
-int gather_statistics(__u8 *ptr, int cc, __u16 seq, int hops,
- int csfailed, struct timeval *tv, char *from)
+int gather_statistics(__u8 *icmph, int icmplen,
+ int cc, __u16 seq, int hops,
+ int csfailed, struct timeval *tv, char *from,
+ void (*pr_reply)(__u8 *icmph, int cc))
{
int dupflag = 0;
long triptime = 0;
+ __u8 *ptr = icmph + icmplen;
++nreceived;
if (!csfailed)
@@ -692,12 +901,12 @@ restamp:
if (csfailed) {
++nchecksum;
--nreceived;
- } else if (TST(seq % mx_dup_ck)) {
+ } else if (rcvd_test(seq)) {
++nrepeats;
--nreceived;
dupflag = 1;
} else {
- SET(seq % mx_dup_ck);
+ rcvd_set(seq);
dupflag = 0;
}
confirm = confirm_flag;
@@ -707,13 +916,18 @@ restamp:
if (options & F_FLOOD) {
if (!csfailed)
- write(STDOUT_FILENO, "\b \b", 3);
+ write_stdout("\b \b", 3);
else
- write(STDOUT_FILENO, "\bC", 1);
+ write_stdout("\bC", 2);
} else {
int i;
__u8 *cp, *dp;
- printf("%d bytes from %s: icmp_seq=%u", cc, from, seq);
+
+ print_timestamp();
+ printf("%d bytes from %s:", cc, from);
+
+ if (pr_reply)
+ pr_reply(icmph, cc);
if (hops >= 0)
printf(" ttl=%d", hops);
@@ -782,6 +996,7 @@ static long llsqrt(long long a)
void finish(void)
{
struct timeval tv = cur_time;
+ char *comma = "";
tvsub(&tv, &start_time);
@@ -812,18 +1027,21 @@ void finish(void)
tmdev = llsqrt(tsum2 - tsum * tsum);
printf("rtt min/avg/max/mdev = %ld.%03ld/%lu.%03ld/%ld.%03ld/%ld.%03ld ms",
- tmin/1000, tmin%1000,
+ (long)tmin/1000, (long)tmin%1000,
(unsigned long)(tsum/1000), (long)(tsum%1000),
- tmax/1000, tmax%1000,
- tmdev/1000, tmdev%1000
+ (long)tmax/1000, (long)tmax%1000,
+ (long)tmdev/1000, (long)tmdev%1000
);
+ comma = ", ";
+ }
+ if (pipesize > 1) {
+ printf("%spipe %d", comma, pipesize);
+ comma = ", ";
}
- if (pipesize > 1)
- printf(", pipe %d", pipesize);
- if (ntransmitted > 1 && (!interval || (options&(F_FLOOD|F_ADAPTIVE)))) {
+ if (nreceived && (!interval || (options&(F_FLOOD|F_ADAPTIVE))) && ntransmitted > 1) {
int ipg = (1000000*(long long)tv.tv_sec+tv.tv_usec)/(ntransmitted-1);
- printf(", ipg/ewma %d.%03d/%d.%03d ms",
- ipg/1000, ipg%1000, rtt/8000, (rtt/8)%1000);
+ printf("%sipg/ewma %d.%03d/%d.%03d ms",
+ comma, ipg/1000, ipg%1000, rtt/8000, (rtt/8)%1000);
}
putchar('\n');
exit(!nreceived || (deadline && nreceived < npackets));
@@ -846,10 +1064,10 @@ void status(void)
tavg = tsum / (nreceived + nrepeats);
fprintf(stderr, ", min/avg/ewma/max = %ld.%03ld/%lu.%03ld/%d.%03d/%ld.%03ld ms",
- tmin/1000, tmin%1000,
+ (long)tmin/1000, (long)tmin%1000,
tavg/1000, tavg%1000,
rtt/8000, (rtt/8)%1000,
- tmax/1000, tmax%1000
+ (long)tmax/1000, (long)tmax%1000
);
}
fprintf(stderr, "\n");
diff --git a/ping_common.h b/ping_common.h
index 39e28a6..9cdb07b 100644
--- a/ping_common.h
+++ b/ping_common.h
@@ -16,9 +16,21 @@
#include <errno.h>
#include <string.h>
#include <netdb.h>
+#include <setjmp.h>
+
+#ifdef CAPABILITIES
+#include <sys/prctl.h>
+#include <sys/capability.h>
+#endif
+
+#ifdef USE_IDN
+#include <locale.h>
+#include <idna.h>
+#endif
#include <netinet/in.h>
#include <arpa/inet.h>
+#include <linux/types.h>
#include <linux/errqueue.h>
#ifdef ANDROID
@@ -35,12 +47,6 @@
#define SCHINT(a) (((a) <= MININTERVAL) ? MININTERVAL : (a))
-#define A(bit) rcvd_tbl[(bit)>>3] /* identify byte in array */
-#define B(bit) (1 << ((bit) & 0x07)) /* identify bit in byte */
-#define SET(bit) (A(bit) |= B(bit))
-#define CLR(bit) (A(bit) &= (~B(bit)))
-#define TST(bit) (A(bit) & B(bit))
-
/* various options */
extern int options;
#define F_FLOOD 0x001
@@ -63,15 +69,58 @@ extern int options;
#define F_STRICTSOURCE 0x8000
#define F_NOLOOP 0x10000
#define F_TTL 0x20000
+#define F_MARK 0x40000
+#define F_PTIMEOFDAY 0x80000
+#define F_OUTSTANDING 0x100000
/*
* MAX_DUP_CHK is the number of bits in received table, i.e. the maximum
* number of received sequence numbers we can keep track of.
*/
#define MAX_DUP_CHK 0x10000
-extern int mx_dup_ck;
-extern char rcvd_tbl[MAX_DUP_CHK / 8];
+#if defined(__WORDSIZE) && __WORDSIZE == 64
+# define USE_BITMAP64
+#endif
+
+#ifdef USE_BITMAP64
+typedef __u64 bitmap_t;
+# define BITMAP_SHIFT 6
+#else
+typedef __u32 bitmap_t;
+# define BITMAP_SHIFT 5
+#endif
+
+#if ((MAX_DUP_CHK >> (BITMAP_SHIFT + 3)) << (BITMAP_SHIFT + 3)) != MAX_DUP_CHK
+# error Please MAX_DUP_CHK and/or BITMAP_SHIFT
+#endif
+
+struct rcvd_table {
+ bitmap_t bitmap[MAX_DUP_CHK / (sizeof(bitmap_t) * 8)];
+};
+
+extern struct rcvd_table rcvd_tbl;
+
+#define A(bit) (rcvd_tbl.bitmap[(bit) >> BITMAP_SHIFT]) /* identify word in array */
+#define B(bit) (((bitmap_t)1) << ((bit) & ((1 << BITMAP_SHIFT) - 1))) /* identify bit in word */
+
+static inline void rcvd_set(__u16 seq)
+{
+ unsigned bit = seq % MAX_DUP_CHK;
+ A(bit) |= B(bit);
+}
+
+static inline void rcvd_clear(__u16 seq)
+{
+ unsigned bit = seq % MAX_DUP_CHK;
+ A(bit) &= ~B(bit);
+}
+
+static inline bitmap_t rcvd_test(__u16 seq)
+{
+ unsigned bit = seq % MAX_DUP_CHK;
+ return A(bit) & B(bit);
+}
extern u_char outpack[];
extern int maxpacket;
@@ -101,6 +150,9 @@ extern int confirm;
extern int confirm_flag;
extern int working_recverr;
+extern volatile int in_pr_addr; /* pr_addr() is executing */
+extern jmp_buf pr_addr_jmp;
+
#ifndef MSG_CONFIRM
#define MSG_CONFIRM 0
#endif
@@ -121,10 +173,23 @@ case 'a': case 'U': case 'c': case 'd': \
case 'f': case 'i': case 'w': case 'l': \
case 'S': case 'n': case 'p': case 'q': \
case 'r': case 's': case 'v': case 'L': \
-case 't': case 'A': case 'W': case 'B':
+case 't': case 'A': case 'W': case 'B': case 'm': \
+case 'D': case 'O':
-#define COMMON_OPTSTR "h?VQ:I:M:aUc:dfi:w:l:S:np:qrs:vLt:AW:B"
+#define COMMON_OPTSTR "h?VQ:I:M:aUc:dfi:w:l:S:np:qrs:vLt:AW:Bm:DO"
+/*
+ * Write to stdout
+ */
+static inline void write_stdout(const char *str, size_t len)
+{
+ size_t o = 0;
+ ssize_t cc;
+ do {
+ cc = write(STDOUT_FILENO, str + o, len - o);
+ o += cc;
+ } while (len > o || cc < 0);
+}
/*
* tvsub --
@@ -169,7 +234,7 @@ static inline int in_flight(void)
}
static inline void acknowledge(__u16 seq)
-{
+{
__u16 diff = (__u16)ntransmitted - seq;
if (diff <= 0x7FFF) {
if ((int)diff+1 > pipesize)
@@ -188,6 +253,25 @@ static inline void advance_ntransmitted(void)
acked = (__u16)ntransmitted + 1;
}
+extern void limit_capabilities(void);
+static int enable_capability_raw(void);
+static int disable_capability_raw(void);
+static int enable_capability_admin(void);
+static int disable_capability_admin(void);
+#ifdef CAPABILITIES
+extern int modify_capability(cap_value_t, cap_flag_value_t);
+static inline int enable_capability_raw(void) { return modify_capability(CAP_NET_RAW, CAP_SET); };
+static inline int disable_capability_raw(void) { return modify_capability(CAP_NET_RAW, CAP_CLEAR); };
+static inline int enable_capability_admin(void) { return modify_capability(CAP_NET_ADMIN, CAP_SET); };
+static inline int disable_capability_admin(void) { return modify_capability(CAP_NET_ADMIN, CAP_CLEAR); };
+#else
+extern int modify_capability(int);
+static inline int enable_capability_raw(void) { return modify_capability(1); };
+static inline int disable_capability_raw(void) { return modify_capability(0); };
+static inline int enable_capability_admin(void) { return modify_capability(1); };
+static inline int disable_capability_admin(void) { return modify_capability(0); };
+#endif
+extern void drop_capabilities(void);
extern int send_probe(void);
extern int receive_error_msg(void);
@@ -201,5 +285,8 @@ extern void main_loop(int icmp_sock, __u8 *buf, int buflen) __attribute__((noret
extern void finish(void) __attribute__((noreturn));
extern void status(void);
extern void common_options(int ch);
-extern int gather_statistics(__u8 *ptr, int cc, __u16 seq, int hops,
- int csfailed, struct timeval *tv, char *from);
+extern int gather_statistics(__u8 *ptr, int icmplen,
+ int cc, __u16 seq, int hops,
+ int csfailed, struct timeval *tv, char *from,
+ void (*pr_reply)(__u8 *ptr, int cc));
+extern void print_timestamp(void);