/*
* 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 <netinet/ip.h>
#include <netinet/ip_icmp.h>
#ifndef ICMP_FILTER
#define ICMP_FILTER 1
struct icmp_filter {
__u32 data;
};
#endif
#define MAXIPLEN 60
#define MAXICMPLEN 76
#define NROUTES 9 /* number of record route slots */
#define TOS_MAX 255 /* 8-bit TOS field */
static int ts_type;
static int nroute = 0;
static __u32 route[10];
struct sockaddr_in whereto; /* who to ping */
int optlen = 0;
int settos = 0; /* Set TOS, Precendence or other QOS options */
int icmp_sock; /* socket file descriptor */
u_char outpack[0x10000];
int maxpacket = sizeof(outpack);
static int broadcast_pings = 0;
static char *pr_addr(__u32);
static void pr_options(unsigned char * cp, int hlen);
static void pr_iph(struct iphdr *ip);
static void usage(void) __attribute__((noreturn));
static u_short in_cksum(const u_short *addr, int len, u_short salt);
static void pr_icmph(__u8 type, __u8 code, __u32 info, struct icmphdr *icp);
static int parsetos(char *str);
static struct {
struct cmsghdr cm;
struct in_pktinfo ipi;
} cmsg = { {sizeof(struct cmsghdr) + sizeof(struct in_pktinfo), SOL_IP, IP_PKTINFO},
{0, }};
int cmsg_len;
struct sockaddr_in source;
char *device;
int pmtudisc = -1;
int
main(int argc, char **argv)
{
struct hostent *hp;
int ch, hold, packlen;
int socket_errno;
u_char *packet;
char *target, hnamebuf[MAXHOSTNAMELEN];
char rspace[3 + 4 * NROUTES + 1]; /* record route space */
icmp_sock = socket(AF_INET, SOCK_RAW, IPPROTO_ICMP);
socket_errno = errno;
uid = getuid();
if (setuid(uid)) {
perror("ping: setuid");
exit(-1);
}
source.sin_family = AF_INET;
preload = 1;
while ((ch = getopt(argc, argv, COMMON_OPTSTR "bRT:")) != EOF) {
switch(ch) {
case 'b':
broadcast_pings = 1;
break;
case 'Q':
settos = parsetos(optarg);
if (settos &&
(setsockopt(icmp_sock, IPPROTO_IP, IP_TOS,
(char *)&settos, sizeof(int)) < 0)) {
perror("ping: error setting QOS sockopts");
exit(2);
}
break;
case 'R':
if (options & F_TIMESTAMP) {
fprintf(stderr, "Only one of -T or -R may be used\n");
exit(2);
}
options |= F_RROUTE;
break;
case 'T':
if (options & F_RROUTE) {
fprintf(stderr, "Only one of -T or -R may be used\n");
exit(2);
}
options |= F_TIMESTAMP;
if (strcmp(optarg, "tsonly") == 0)
ts_type = IPOPT_TS_TSONLY;
else if (strcmp(optarg, "tsandaddr") == 0)
ts_type = IPOPT_TS_TSANDADDR;
else if (strcmp(optarg, "tsprespec") == 0)
ts_type = IPOPT_TS_PRESPEC;
else {
fprintf(stderr, "Invalid timestamp type\n");
exit(2);
}
break;
case 'I':
{
#if 0
char dummy;
int i1, i2, i3, i4;
if (sscanf(optarg, "%u.%u.%u.%u%c",
&i1, &i2, &i3, &i4, &dummy) == 4) {
__u8 *ptr;
ptr = (__u8*)&source.sin_addr;
ptr[0] = i1;
ptr[1] = i2;
ptr[2] = i3;
ptr[3] = i4;
options |= F_STRICTSOURCE;
} else {
device = optarg;
}
#else
if (inet_pton(AF_INET, optarg, &source.sin_addr) > 0)
options |= F_STRICTSOURCE;
else
device = optarg;
#endif
break;
}
case 'M':
if (strcmp(optarg, "do") == 0)
pmtudisc = IP_PMTUDISC_DO;
else if (strcmp(optarg, "dont") == 0)
pmtudisc = IP_PMTUDISC_DONT;
else if (strcmp(optarg, "want") == 0)
pmtudisc = IP_PMTUDISC_WANT;
else {
fprintf(stderr, "ping: wrong value for -M: do, dont, want are valid ones.\n");
exit(2);
}
break;
case 'V':
printf("ping utility, iputils-ss%s\n", SNAPSHOT);
exit(0);
COMMON_OPTIONS
common_options(ch);
break;
default:
usage();
}
}
argc -= optind;
argv += optind;
if (argc == 0)
usage();
if (argc > 1) {
if (options & F_RROUTE)
usage();
else if (options & F_TIMESTAMP) {
if (ts_type != IPOPT_TS_PRESPEC)
usage();
if (argc > 5)
usage();
} else {
if (argc > 10)
usage();
options |= F_SOURCEROUTE;
}
}
while (argc > 0) {
target = *argv;
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);
if (!hp) {
fprintf(stderr, "ping: unknown host %s\n", target);
exit(2);
}
memcpy(&whereto.sin_addr, hp->h_addr, 4);
strncpy(hnamebuf, hp->h_name, sizeof(hnamebuf) - 1);
hnamebuf[sizeof(hnamebuf) - 1] = 0;
hostname = hnamebuf;
}
if (argc > 1)
route[nroute++] = whereto.sin_addr.s_addr;
argc--;
argv++;
}
if (source.sin_addr.s_addr == 0) {
socklen_t alen;
struct sockaddr_in dst = whereto;
int probe_fd = socket(AF_INET, SOCK_DGRAM, 0);
if (probe_fd < 0) {
perror("socket");
exit(2);
}
if (device) {
struct ifreq ifr;
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) {
if (IN_MULTICAST(ntohl(dst.sin_addr.s_addr))) {
struct ip_mreqn imr;
if (ioctl(probe_fd, SIOCGIFINDEX, &ifr) < 0) {
fprintf(stderr, "ping: unknown iface %s\n", device);
exit(2);
}
memset(&imr, 0, sizeof(imr));
imr.imr_ifindex = ifr.ifr_ifindex;
if (setsockopt(probe_fd, SOL_IP, IP_MULTICAST_IF, &imr, sizeof(imr)) == -1) {
perror("ping: IP_MULTICAST_IF");
exit(2);
}
}
}
}
if (settos &&
setsockopt(probe_fd, IPPROTO_IP, IP_TOS, (char *)&settos, sizeof(int)) < 0)
perror("Warning: erro