#include <sys/cdefs.h>
#ifndef lint
#if 0
static const char rcsid[] =
"@(#)Id: traceroute.c,v 1.68 2000/12/14 08:04:33 leres Exp (LBL)";
#else
__COPYRIGHT("@(#) Copyright (c) 1988, 1989, 1991, 1994, 1995, 1996, 1997,\
1998, 1999, 2000\
The Regents of the University of California. All rights reserved.");
__RCSID("$NetBSD: traceroute.c,v 1.84 2018/01/19 14:30:09 maxv Exp $");
#endif
#endif
#include <sys/param.h>
#include <sys/file.h>
#include <sys/ioctl.h>
#include <sys/socket.h>
#include <sys/time.h>
#include <sys/sysctl.h>
#include <netinet/in_systm.h>
#include <netinet/in.h>
#include <netinet/ip.h>
#include <netinet/ip_var.h>
#include <netinet/ip_icmp.h>
#include <netinet/udp.h>
#include <netinet/udp_var.h>
#include <arpa/inet.h>
#include <ctype.h>
#include <err.h>
#include <errno.h>
#ifdef HAVE_MALLOC_H
#include <malloc.h>
#endif
#include <memory.h>
#include <netdb.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <poll.h>
#ifdef IPSEC
#include <net/route.h>
#include <netipsec/ipsec.h>
#endif
#include "gnuc.h"
#ifdef HAVE_OS_PROTO_H
#include "os-proto.h"
#endif
#ifndef ICMP_UNREACH_FILTER_PROHIB
#define ICMP_UNREACH_FILTER_PROHIB 13
#endif
#ifndef ICMP_UNREACH_HOST_PRECEDENCE
#define ICMP_UNREACH_HOST_PRECEDENCE 14
#endif
#ifndef ICMP_UNREACH_PRECEDENCE_CUTOFF
#define ICMP_UNREACH_PRECEDENCE_CUTOFF 15
#endif
#include "ifaddrlist.h"
#include "as.h"
#include "prog_ops.h"
#define NGATEWAYS ((int)((MAX_IPOPTLEN - IPOPT_MINOFF - 1) / sizeof(u_int32_t)))
#ifndef MAXHOSTNAMELEN
#define MAXHOSTNAMELEN 64
#endif
#define Fprintf (void)fprintf
#define Printf (void)printf
struct hostinfo {
char *name;
int n;
u_int32_t *addrs;
};
struct outdata {
u_char seq;
u_char ttl;
struct tv32 {
int32_t tv32_sec;
int32_t tv32_usec;
} tv;
};
#ifdef ICMP_EXT_OFFSET
#undef ICMP_EXT_OFFSET
#endif
#define ICMP_EXT_OFFSET 8 + \
128
#define MPLS_STACK_ENTRY_CLASS 1
#define MPLS_STACK_ENTRY_C_TYPE 1
struct mpls_header {
#if BYTE_ORDER == BIG_ENDIAN
uint32_t label:20;
unsigned char exp:3;
unsigned char s:1;
unsigned char ttl:8;
#else
unsigned char ttl:8;
unsigned char s:1;
unsigned char exp:3;
uint32_t label:20;
#endif
};
#ifndef HAVE_ICMP_NEXTMTU
struct my_pmtu {
u_short ipm_void;
u_short ipm_nextmtu;
};
#endif
static u_char packet[512];
static struct ip *outip;
static struct udphdr *outudp;
static void *outmark;
static struct outdata outsetup;
static struct icmp *outicmp;
static u_int32_t gwlist[NGATEWAYS + 1];
static int s;
static int sndsock;
static struct sockaddr whereto;
static struct sockaddr wherefrom;
static int packlen;
static int minpacket;
static int maxpacket = 32 * 1024;
static int printed_ttl = 0;
static int pmtu;
static u_int pausemsecs;
static const char *prog;
static char *source;
static char *hostname;
static char *device;
#ifdef notdef
static const char devnull[] = "/dev/null";
#endif
static int nprobes = 3;
static int max_ttl = 30;
static int first_ttl = 1;
static u_int16_t ident;
static in_port_t port = 32768 + 666;
static int options;
static int verbose;
static int waittime = 5;
static int nflag;
static int dump;
static int Mflag;
static int as_path;
static char *as_server = NULL;
static void *asn;
static int useicmp = 0;
#ifdef CANT_HACK_CKSUM
static int doipcksum = 0;
#else
static int doipcksum = 1;
#endif
static int optlen;
static int mtus[] = {
17914,
8166,
4464,
4352,
2048,
2002,
1536,
1500,
1492,
1480,
1280,
1006,
576,
552,
544,
512,
508,
296,
68,
0
};
static int *mtuptr = &mtus[0];
static int mtudisc = 0;
static int nextmtu;
static double deltaT(struct timeval *, struct timeval *);
static void freehostinfo(struct hostinfo *);
static void getaddr(u_int32_t *, char *);
static struct hostinfo *gethostinfo(char *);
static u_int16_t in_cksum(u_int16_t *, int);
static u_int16_t in_cksum2(u_int16_t, u_int16_t *, int);
static char *inetname(struct in_addr);
static int packet_ok(u_char *, ssize_t, struct sockaddr_in *, int);
static const char *pr_type(u_char);
static void print(u_char *, int, struct sockaddr_in *);
static void resize_packet(void);
static void dump_packet(void);
static void send_probe(int, int, struct timeval *);
static void setsin(struct sockaddr_in *, u_int32_t);
static int str2val(const char *, const char *, int, int);
static void tvsub(struct timeval *, struct timeval *);
static void usage(void) __attribute__((__noreturn__));
static ssize_t wait_for_reply(int, struct sockaddr_in *, const struct timeval *);
static void decode_extensions(unsigned char *buf, int ip_len);
static void frag_err(void);
static int find_local_ip(struct sockaddr_in *, struct sockaddr_in *);
#ifdef IPSEC
#ifdef IPSEC_POLICY_IPSEC
static int setpolicy(int, const char *);
#endif
#endif
int
main(int argc, char **argv)
{
int op, code, n;
u_char *outp;
u_int32_t *ap;
struct sockaddr_in *from = (struct sockaddr_in *)&wherefrom;
struct sockaddr_in *to = (struct sockaddr_in *)&whereto;
struct hostinfo *hi;
int on = 1;
int ttl, probe, i;
int seq = 0;
int tos = 0, settos = 0, ttl_flag = 0;
int lsrr = 0;
u_int16_t off = 0;
struct ifaddrlist *al, *al2;
char errbuf[132];
int mib[4] = { CTL_NET, PF_INET, IPPROTO_IP, IPCTL_DEFTTL };
size_t size = sizeof(max_ttl);
setprogname(argv[0]);
prog = getprogname();
if (prog_init && prog_init() == -1)
err(1, "init failed");
#ifdef notdef
if (open(devnull, O_RDONLY) < 0 ||
open(devnull, O_RDONLY) < 0 ||
open(devnull, O_RDONLY) < 0)
err(1, "Cannot open `%s'", devnull);
#endif
if ((s = prog_socket(AF_INET, SOCK_RAW, IPPROTO_ICMP)) < 0)
err(1, "icmp socket");
#ifndef __hpux
sndsock = prog_socket(AF_INET, SOCK_RAW, IPPROTO_RAW);
#else
sndsock = socket(AF_INET, SOCK_RAW, IPPROTO_RAW
useicmp ? IPPROTO_ICMP : IPPROTO_UDP);
#endif
if (sndsock < 0)
err(1, "raw socket");
(void) prog_sysctl(mib, sizeof(mib)/sizeof(mib[0]), &max_ttl, &size,
NULL, 0);
opterr = 0;
while ((op = getopt(argc, argv, "aA:dDFPIMnlrvxf:g:i:m:p:q:s:t:w:z:")) != -1)
switch (op) {
case 'a':
as_path = 1;
break;
case 'A':
as_path = 1;
as_server = optarg;
break;
case 'd':
options |= SO_DEBUG;
break;
case 'D':
dump = 1;
break;
case 'f':
first_ttl = str2val(optarg, "first ttl", 1, 255);
break;
case 'F':
off = IP_DF;
break;
case 'g':
if (lsrr >= NGATEWAYS)
errx(1, "more than %d gateways", NGATEWAYS);
getaddr(gwlist + lsrr, optarg);
++lsrr;
break;
case 'i':
device = optarg;
break;
case 'I':
++useicmp;
break;
case 'l':
++ttl_flag;
break;
case 'm':
max_ttl = str2val(optarg, "max ttl", 1, 255);
break;
case 'M':
Mflag = 1;
break;
case 'n':
++nflag;
break;
case 'p':
port = (u_short)str2val(optarg, "port",
1, (1 << 16) - 1);
break;
case 'q':
nprobes = str2val(optarg, "nprobes", 1, -1);
break;
case 'r':
options |= SO_DONTROUTE;
break;
case 's':
source = optarg;
break;
case 't':
tos = str2val(optarg, "tos", 0, 255);
++settos;
break;
case 'v':
++verbose;
break;
case 'x':
doipcksum = (doipcksum == 0);
break;
case 'w':
waittime = str2val(optarg, "wait time",
2, 24 * 60 * 60);
break;
case 'z':
pausemsecs = str2val(optarg, "pause msecs",
0, 60 * 60 * 1000);
break;
case 'P':
off = IP_DF;
mtudisc = 1;
break;
default:
usage();
}
if (first_ttl > max_ttl)
errx(1, "first ttl (%d) may not be greater than max ttl (%d)",
first_ttl, max_ttl);
if (!doipcksum)
warnx("ip checksums disabled");
if (lsrr > 0)
optlen = (lsrr + 1) * sizeof(gwlist[0]);
minpacket = sizeof(*outip) + sizeof(struct outdata) + optlen;
if (useicmp)
minpacket += 8;
else
minpacket += sizeof(*outudp);
packlen = minpacket;
if (mtudisc)
packlen = *mtuptr++;
switch (argc - optind) {
case 2:
packlen = str2val(argv[optind + 1],
"packet length", minpacket, maxpacket);
case 1:
hostname = argv[optind];
hi = gethostinfo(hostname);
setsin(to, hi->addrs[0]);
if (hi->n > 1)
warnx("%s has multiple addresses; using %s",
hostname, inet_ntoa(to->sin_addr));
hostname = hi->name;
hi->name = NULL;
freehostinfo(hi);
break;
default:
usage();
}
#ifdef HAVE_SETLINEBUF
setlinebuf (stdout);
#else
setvbuf(stdout, NULL, _IOLBF, 0);
#endif
outip = malloc((unsigned)packlen);
if (outip == NULL)
err(1, "malloc");
memset(outip, 0, packlen);
outip->ip_v = IPVERSION;
if (settos)
outip->ip_tos = tos;
#ifdef BYTESWAP_IP_HDR
outip->ip_len = htons(packlen);
outip->ip_off = htons(off);
#else
outip->ip_len = packlen;
outip->ip_off = off;
#endif
outp = (u_char *)(outip + 1);
#ifdef HAVE_RAW_OPTIONS
if (lsrr > 0) {
u_char *optlist;
optlist = outp;
outp += optlen;
gwlist[lsrr] = to->sin_addr.s_addr;
outip->ip_dst.s_addr = gwlist[0];
optlist[0] = IPOPT_NOP;
optlist[1] = IPOPT_LSRR;
i = lsrr * sizeof(gwlist[0]);
optlist[2] = i + 3;
optlist[3] = IPOPT_MINOFF;
memcpy(optlist + 4, gwlist + 1, i);
} else
#endif
outip->ip_dst = to->sin_addr;
outip->ip_hl = (outp - (u_char *)outip) >> 2;
ident = htons(arc4random() & 0xffff) | 0x8000;
if (useicmp) {
outip->ip_p = IPPROTO_ICMP;
outicmp = (struct icmp *)outp;
outicmp->icmp_type = ICMP_ECHO;
outicmp->icmp_id = htons(ident);
outmark = outp + 8;
} else {
outip->ip_p = IPPROTO_UDP;
outudp = (struct udphdr *)outp;
outudp->uh_sport = htons(ident);
outudp->uh_ulen =
htons((u_int16_t)(packlen - (sizeof(*outip) + optlen)));
outmark = outudp + 1;
}
if (options & SO_DEBUG)
(void)prog_setsockopt(s, SOL_SOCKET, SO_DEBUG, (char *)&on,
sizeof(on));
#ifdef IPSEC
#ifdef IPSEC_POLICY_IPSEC
if (setpolicy(s, "in bypass") < 0)
exit(1);
if (setpolicy(s, "out bypass") < 0)
exit(1);
#else
{
int level = IPSEC_LEVEL_AVAIL;
(void)prog_setsockopt(s, IPPROTO_IP, IP_ESP_TRANS_LEVEL, &level,
sizeof(level));
(void)prog_setsockopt(s, IPPROTO_IP, IP_ESP_NETWORK_LEVEL, &level,
sizeof(level));
#ifdef IP_AUTH_TRANS_LEVEL
(void)prog_setsockopt(s, IPPROTO_IP, IP_AUTH_TRANS_LEVEL, &level,
sizeof(level));
#else
(void)prog_setsockopt(s, IPPROTO_IP, IP_AUTH_LEVEL, &level,
sizeof(level));
#endif
#ifdef IP_AUTH_NETWORK_LEVEL
(void)prog_setsockopt(s, IPPROTO_IP, IP_AUTH_NETWORK_LEVEL, &level,
sizeof(level));
#endif
}
#endif
#endif
#ifdef IPSEC
#ifdef IPSEC_POLICY_IPSEC
if (setpolicy(sndsock, "in bypass") < 0)
exit(1);
if (setpolicy(sndsock, "out bypass") < 0)
exit(1);
#else
{
int level = IPSEC_LEVEL_BYPASS;
(void)prog_setsockopt(sndsock, IPPROTO_IP, IP_ESP_TRANS_LEVEL, &level,
sizeof(level));
(void)prog_setsockopt(sndsock, IPPROTO_IP, IP_ESP_NETWORK_LEVEL, &level,
sizeof(level));
#ifdef IP_AUTH_TRANS_LEVEL
(void)prog_setsockopt(sndsock, IPPROTO_IP, IP_AUTH_TRANS_LEVEL, &level,
sizeof(level));
#else
(void)prog_setsockopt(sndsock, IPPROTO_IP, IP_AUTH_LEVEL, &level,
sizeof(level));
#endif
#ifdef IP_AUTH_NETWORK_LEVEL
(void)prog_setsockopt(sndsock, IPPROTO_IP, IP_AUTH_NETWORK_LEVEL, &level,
sizeof(level));
#endif
}
#endif
#endif
#if defined(IP_OPTIONS) && !defined(HAVE_RAW_OPTIONS)
if (lsrr > 0) {
u_char optlist[MAX_IPOPTLEN];
gwlist[lsrr] = to->sin_addr.s_addr;
++lsrr;
optlist[0] = IPOPT_NOP;
optlist[1] = IPOPT_LSRR;
i = lsrr * sizeof(gwlist[0]);
optlist[2] = i + 3;
optlist[3] = IPOPT_MINOFF;
memcpy(optlist + 4, gwlist, i);
if ((prog_setsockopt(sndsock, IPPROTO_IP, IP_OPTIONS, optlist,
i + sizeof(gwlist[0]))) < 0)
err(1, "IP_OPTIONS");
}
#endif
#ifdef SO_SNDBUF
if (prog_setsockopt(sndsock, SOL_SOCKET, SO_SNDBUF, (char *)&packlen,
sizeof(packlen)) < 0)
err(1, "SO_SNDBUF");
#endif
#ifdef IP_HDRINCL
if (prog_setsockopt(sndsock, IPPROTO_IP, IP_HDRINCL, (char *)&on,
sizeof(on)) < 0)
err(1, "IP_HDRINCL");
#else
#ifdef IP_TOS
if (settos && prog_setsockopt(sndsock, IPPROTO_IP, IP_TOS,
&tos, sizeof(tos)) < 0)
err(1, "setsockopt tos %d", tos);
#endif
#endif
if (options & SO_DEBUG)
if (prog_setsockopt(sndsock, SOL_SOCKET, SO_DEBUG, &on,
sizeof(on)) < 0)
err(1, "setsockopt debug %d", tos);
if (options & SO_DONTROUTE)
if (prog_setsockopt(sndsock, SOL_SOCKET, SO_DONTROUTE, &on,
sizeof(on)) < 0)
err(1, "setsockopt dontroute %d", tos);
n = ifaddrlist(&al, errbuf, sizeof errbuf);
al2 = al;
if (n < 0)
errx(1, "ifaddrlist (%s)", errbuf);
if (n == 0)
errx(1, "Can't find any network interfaces");
if (device != NULL) {
for (i = n; i > 0; --i, ++al2)
if (strcmp(device, al2->device) == 0)
break;
if (i <= 0)
errx(1, "Can't find interface %.32s", device);
}
if (source == NULL) {
setsin(from, al2->addr);
if (n > 1 && device == NULL && !find_local_ip(from, to)) {
warnx("Multiple interfaces found; using %s @ %s",
inet_ntoa(from->sin_addr), al2->device);
}
} else {
hi = gethostinfo(source);
source = hi->name;
hi->name = NULL;
if (device == NULL) {
setsin(from, hi->addrs[0]);
if (hi->n > 1)
warnx("%s has multiple addresses; using %s",
source, inet_ntoa(from->sin_addr));
} else {
for (i = hi->n, ap = hi->addrs; i > 0; --i, ++ap)
if (*ap == al2->addr)
break;
if (i <= 0)
errx(1, "%s is not on interface %s",
source, device);
setsin(from, *ap);
}
freehostinfo(hi);
}
setgid(getgid());
setuid(getuid());
if (getuid()) {
al2 = al;
for (i = n; i > 0; --i, ++al2)
if (from->sin_addr.s_addr == al2->addr)
break;
if (i <= 0)
errx(1, "%s is not a valid local address "
"and you are not superuser.",
inet_ntoa(from->sin_addr));
}
outip->ip_src = from->sin_addr;
#ifndef IP_HDRINCL
if (bind(sndsock, (struct sockaddr *)from, sizeof(*from)) < 0)
err(1, "bind");
#endif
if (as_path) {
asn = as_setup(as_server);
if (asn == NULL) {
warnx("as_setup failed, AS# lookups disabled");
(void)fflush(stderr);
as_path = 0;
}
}
setuid(getuid());
Fprintf(stderr, "%s to %s (%s)",
prog, hostname, inet_ntoa(to->sin_addr));
if (source)
Fprintf(stderr, " from %s", source);
Fprintf(stderr, ", %d hops max, %d byte packets\n", max_ttl, packlen);
(void)fflush(stderr);
for (ttl = first_ttl; ttl <= max_ttl; ++ttl) {
u_int32_t lastaddr = 0;
int gotlastaddr = 0;
int got_there = 0;
int unreachable = 0;
int sentfirst = 0;
again:
printed_ttl = 0;
for (probe = 0; probe < nprobes; ++probe) {
int cc;
struct timeval t1, t2;
struct ip *ip;
if (sentfirst && pausemsecs > 0)
usleep(pausemsecs * 1000);
(void)gettimeofday(&t1, NULL);
if (!useicmp && htons(port + seq + 1) == 0)
seq++;
send_probe(++seq, ttl, &t1);
++sentfirst;
while ((cc = wait_for_reply(s, from, &t1)) != 0) {
(void)gettimeofday(&t2, NULL);
if (t2.tv_sec - t1.tv_sec > waittime) {
cc = 0;
break;
}
i = packet_ok(packet, cc, from, seq);
if (i == 0)
continue;
if (!gotlastaddr ||
from->sin_addr.s_addr != lastaddr) {
if (gotlastaddr) printf("\n ");
print(packet, cc, from);
lastaddr = from->sin_addr.s_addr;
++gotlastaddr;
}
ip = (struct ip *)packet;
Printf(" %.3f ms", deltaT(&t1, &t2));
if (ttl_flag)
Printf(" (ttl = %d)", ip->ip_ttl);
if (i == -2) {
#ifndef ARCHAIC
if (ip->ip_ttl <= 1)
Printf(" !");
#endif
++got_there;
break;
}
if (i == -1)
break;
code = i - 1;
switch (code) {
case ICMP_UNREACH_PORT:
#ifndef ARCHAIC
if (ip->ip_ttl <= 1)
Printf(" !");
#endif
++got_there;
break;
case ICMP_UNREACH_NET:
++unreachable;
Printf(" !N");
break;
case ICMP_UNREACH_HOST:
++unreachable;
Printf(" !H");
break;
case ICMP_UNREACH_PROTOCOL:
++got_there;
Printf(" !P");
break;
case ICMP_UNREACH_NEEDFRAG:
if (mtudisc) {
frag_err();
goto again;
} else {
++unreachable;
Printf(" !F-%d", pmtu);
}
break;
case ICMP_UNREACH_SRCFAIL:
++unreachable;
Printf(" !S");
break;
case ICMP_UNREACH_FILTER_PROHIB:
++unreachable;
Printf(" !X");
break;
case ICMP_UNREACH_HOST_PRECEDENCE:
++unreachable;
Printf(" !V");
break;
case ICMP_UNREACH_PRECEDENCE_CUTOFF:
++unreachable;
Printf(" !C");
break;
default:
++unreachable;
Printf(" !<%d>", code);
break;
}
break;
}
if (cc == 0)
Printf(" *");
else if (cc && probe == nprobes - 1 && Mflag)
decode_extensions(packet, cc);
(void)fflush(stdout);
}
putchar('\n');
if (got_there ||
(unreachable > 0 && unreachable >= ((nprobes + 1) / 2)))
break;
}
if (as_path)
as_shutdown(asn);
exit(0);
}
static ssize_t
wait_for_reply(int sock, struct sockaddr_in *fromp, const struct timeval *tp)
{
struct pollfd set[1];
struct timeval now, wait;
ssize_t cc = 0;
socklen_t fromlen = sizeof(*fromp);
int retval;
set[0].fd = sock;
set[0].events = POLLIN;
wait.tv_sec = tp->tv_sec + waittime;
wait.tv_usec = tp->tv_usec;
(void)gettimeofday(&now, NULL);
tvsub(&wait, &now);
if (wait.tv_sec < 0) {
wait.tv_sec = 0;
wait.tv_usec = 0;
}
retval = prog_poll(set, 1, wait.tv_sec * 1000 + wait.tv_usec / 1000);
if (retval < 0)
err(1, "poll");
if (retval > 0) {
cc = prog_recvfrom(sock, (char *)packet, sizeof(packet), 0,
(struct sockaddr *)fromp, &fromlen);
}
return cc;
}
static void
decode_extensions(unsigned char *buf, int ip_len)
{
struct icmp_ext_hdr *cmn_hdr;
struct icmp_ext_obj_hdr *obj_hdr;
union {
struct mpls_header mpls;
uint32_t mpls_h;
} mpls;
size_t datalen, obj_len;
struct ip *ip;
ip = (struct ip *)buf;
if (ip_len < (int)((ip->ip_hl << 2) + ICMP_EXT_OFFSET +
sizeof(struct icmp_ext_hdr))) {
return;
}
buf += (ip->ip_hl << 2) + ICMP_EXT_OFFSET;
cmn_hdr = (struct icmp_ext_hdr *)buf;
if (cmn_hdr->version != ICMP_EXT_VERSION) {
return;
}
datalen = ip_len - ((u_char *)cmn_hdr - (u_char *)ip);
if (ntohs(cmn_hdr->checksum) &&
in_cksum((u_short *)cmn_hdr, datalen)) {
return;
}
buf += sizeof(*cmn_hdr);
datalen -= sizeof(*cmn_hdr);
while (datalen >= sizeof(struct icmp_ext_obj_hdr)) {
obj_hdr = (struct icmp_ext_obj_hdr *)buf;
obj_len = ntohs(obj_hdr->length);
if (obj_len > datalen)
return;
datalen -= obj_len;
buf += sizeof(struct icmp_ext_obj_hdr);
obj_len -= sizeof(struct icmp_ext_obj_hdr);
switch (obj_hdr->class_num) {
case MPLS_STACK_ENTRY_CLASS:
switch (obj_hdr->c_type) {
case MPLS_STACK_ENTRY_C_TYPE:
while (obj_len >= sizeof(uint32_t)) {
mpls.mpls_h = ntohl(*(uint32_t *)buf);
buf += sizeof(uint32_t);
obj_len -= sizeof(uint32_t);
printf(" [MPLS: Label %d Exp %d]",
mpls.mpls.label, mpls.mpls.exp);
}
if (obj_len > 0) {
return;
}
break;
default:
buf += ntohs(obj_hdr->length) -
sizeof(struct icmp_ext_obj_hdr);
break;
}
break;
default:
buf += ntohs(obj_hdr->length) -
sizeof(struct icmp_ext_obj_hdr);
break;
}
}
}
static void
dump_packet(void)
{
u_char *p;
int i;
Fprintf(stderr, "packet data:");
#ifdef __hpux
for (p = useicmp ? (u_char *)outicmp : (u_char *)outudp, i = 0;
i < packlen - (sizeof(*outip) + optlen); i++)
#else
for (p = (u_char *)outip, i = 0; i < packlen; i++)
#endif
{
if ((i % 24) == 0)
Fprintf(stderr, "\n ");
Fprintf(stderr, " %02x", *p++);
}
Fprintf(stderr, "\n");
}
void
send_probe(int seq, int ttl, struct timeval *tp)
{
int cc;
struct udpiphdr * ui, *oui;
int oldmtu = packlen;
struct ip tip;
again:
#ifdef BYTESWAP_IP_LEN
outip->ip_len = htons(packlen);
#else
outip->ip_len = packlen;
#endif
outip->ip_ttl = ttl;
#ifndef __hpux
outip->ip_id = htons(ident + seq);
#endif
if (doipcksum) {
outip->ip_sum =
in_cksum((u_int16_t *)outip, sizeof(*outip) + optlen);
if (outip->ip_sum == 0)
outip->ip_sum = 0xffff;
}
outsetup.seq = seq;
outsetup.ttl = ttl;
outsetup.tv.tv32_sec = htonl(tp->tv_sec);
outsetup.tv.tv32_usec = htonl(tp->tv_usec);
memcpy(outmark,&outsetup,sizeof(outsetup));
if (useicmp)
outicmp->icmp_seq = htons(seq);
else
outudp->uh_dport = htons(port + seq);
if (useicmp) {
outicmp->icmp_cksum = 0;
outicmp->icmp_cksum = in_cksum((u_short *)outicmp,
packlen - (sizeof(*outip) + optlen));
if (outicmp->icmp_cksum == 0)
outicmp->icmp_cksum = 0xffff;
} else if (doipcksum) {
tip = *outip;
ui = (struct udpiphdr *)outip;
oui = (struct udpiphdr *)&tip;
memset(ui, 0, sizeof(ui->ui_i));
ui->ui_src = oui->ui_src;
ui->ui_dst = oui->ui_dst;
ui->ui_pr = oui->ui_pr;
ui->ui_len = outudp->uh_ulen;
outudp->uh_sum = 0;
outudp->uh_sum = in_cksum((u_short *)ui, packlen);
if (outudp->uh_sum == 0)
outudp->uh_sum = 0xffff;
*outip = tip;
}
if (verbose > 1) {
const u_int16_t *sp;
int nshorts, i;
sp = (u_int16_t *)outip;
nshorts = (u_int)packlen / sizeof(u_int16_t);
i = 0;
Printf("[ %d bytes", packlen);
while (--nshorts >= 0) {
if ((i++ % 8) == 0)
Printf("\n\t");
Printf(" %04x", ntohs(*sp++));
}
if (packlen & 1) {
if ((i % 8) == 0)
Printf("\n\t");
Printf(" %02x", *(const u_char *)sp);
}
Printf("]\n");
}
#if !defined(IP_HDRINCL) && defined(IP_TTL)
if (prog_setsockopt(sndsock, IPPROTO_IP, IP_TTL,
(char *)&ttl, sizeof(ttl)) < 0)
err(1, "setsockopt ttl %d", ttl);
#endif
if (dump)
dump_packet();
#ifdef __hpux
cc = sendto(sndsock, useicmp ? (char *)outicmp : (char *)outudp,
packlen - (sizeof(*outip) + optlen), 0, &whereto, sizeof(whereto));
if (cc > 0)
cc += sizeof(*outip) + optlen;
#else
cc = prog_sendto(sndsock, (char *)outip,
packlen, 0, &whereto, sizeof(whereto));
#endif
if (cc < 0 || cc != packlen) {
if (cc < 0) {
if (errno == EMSGSIZE) {
packlen = *mtuptr++;
resize_packet();
goto again;
} else
warn("sendto");
}
Printf("%s: wrote %s %d chars, ret=%d\n",
prog, hostname, packlen, cc);
(void)fflush(stdout);
}
if (oldmtu != packlen) {
Printf("message too big, "
"trying new MTU = %d\n", packlen);
printed_ttl = 0;
}
if (!printed_ttl) {
Printf("%2d ", ttl);
printed_ttl = 1;
}
}
static double
deltaT(struct timeval *t1p, struct timeval *t2p)
{
double dt;
dt = (double)(t2p->tv_sec - t1p->tv_sec) * 1000.0 +
(double)(t2p->tv_usec - t1p->tv_usec) / 1000.0;
return dt;
}
static const char *
pr_type(u_char t)
{
static const char *ttab[] = {
"Echo Reply", "ICMP 1", "ICMP 2", "Dest Unreachable",
"Source Quench", "Redirect", "ICMP 6", "ICMP 7",
"Echo", "ICMP 9", "ICMP 10", "Time Exceeded",
"Param Problem", "Timestamp", "Timestamp Reply", "Info Request",
"Info Reply"
};
if (t > 16)
return "OUT-OF-RANGE";
return ttab[t];
}
static int
packet_ok(u_char *buf, ssize_t cc, struct sockaddr_in *from, int seq)
{
struct icmp *icp;
u_char type, code;
int hlen;
#ifndef ARCHAIC
struct ip *ip;
ip = (struct ip *) buf;
hlen = ip->ip_hl << 2;
if (cc < hlen + ICMP_MINLEN) {
if (verbose)
Printf("packet too short (%zd bytes) from %s\n", cc,
inet_ntoa(from->sin_addr));
return 0;
}
cc -= hlen;
icp = (struct icmp *)(buf + hlen);
#else
icp = (struct icmp *)buf;
#endif
type = icp->icmp_type;
code = icp->icmp_code;
if (code != ICMP_UNREACH_NEEDFRAG)
pmtu = 0;
else {
#ifdef HAVE_ICMP_NEXTMTU
pmtu = ntohs(icp->icmp_nextmtu);
#else
pmtu = ntohs(((struct my_pmtu *)&icp->icmp_void)->ipm_nextmtu);
#endif
}
if ((type == ICMP_TIMXCEED && code == ICMP_TIMXCEED_INTRANS) ||
type == ICMP_UNREACH || type == ICMP_ECHOREPLY) {
struct ip *hip;
struct udphdr *up;
struct icmp *hicmp;
hip = &icp->icmp_ip;
hlen = hip->ip_hl << 2;
nextmtu = ntohs(icp->icmp_nextmtu);
if (useicmp) {
if (type == ICMP_ECHOREPLY &&
icp->icmp_id == htons(ident) &&
icp->icmp_seq == htons(seq))
return -2;
hicmp = (struct icmp *)((u_char *)hip + hlen);
if (hlen + 8 <= cc &&
hip->ip_p == IPPROTO_ICMP &&
hicmp->icmp_id == htons(ident) &&
hicmp->icmp_seq == htons(seq))
return type == ICMP_TIMXCEED ? -1 : code + 1;
} else {
up = (struct udphdr *)((u_char *)hip + hlen);
if (hlen + 12 <= cc &&
hip->ip_p == IPPROTO_UDP &&
up->uh_sport == htons(ident) &&
up->uh_dport == htons(port + seq))
return type == ICMP_TIMXCEED ? -1 : code + 1;
}
}
#ifndef ARCHAIC
if (verbose) {
int i;
u_int32_t *lp = (u_int32_t *)&icp->icmp_ip;
Printf("\n%zd bytes from %s to ", cc, inet_ntoa(from->sin_addr));
Printf("%s: icmp type %d (%s) code %d\n",
inet_ntoa(ip->ip_dst), type, pr_type(type), icp->icmp_code);
for (i = 4; i < cc ; i += sizeof(*lp))
Printf("%2d: x%8.8x\n", i, *lp++);
}
#endif
return(0);
}
static void
resize_packet(void)
{
if (useicmp) {
outicmp->icmp_cksum = 0;
outicmp->icmp_cksum = in_cksum((u_int16_t *)outicmp,
packlen - (sizeof(*outip) + optlen));
if (outicmp->icmp_cksum == 0)
outicmp->icmp_cksum = 0xffff;
} else {
outudp->uh_ulen =
htons((u_int16_t)(packlen - (sizeof(*outip) + optlen)));
}
}
static void
print(u_char *buf, int cc, struct sockaddr_in *from)
{
struct ip *ip;
int hlen;
char addr[INET_ADDRSTRLEN];
ip = (struct ip *) buf;
hlen = ip->ip_hl << 2;
cc -= hlen;
strlcpy(addr, inet_ntoa(from->sin_addr), sizeof(addr));
if (as_path)
Printf(" [AS%u]", as_lookup(asn, addr, AF_INET));
if (nflag)
Printf(" %s", addr);
else
Printf(" %s (%s)", inetname(from->sin_addr), addr);
if (verbose)
Printf(" %d bytes to %s", cc, inet_ntoa (ip->ip_dst));
}
static u_int16_t
in_cksum(u_int16_t *addr, int len)
{
return ~in_cksum2(0, addr, len);
}
static u_int16_t
in_cksum2(u_int16_t seed, u_int16_t *addr, int len)
{
int nleft = len;
u_int16_t *w = addr;
union {
u_int16_t w;
u_int8_t b[2];
} answer;
int32_t sum = seed;
while (nleft > 1) {
sum += *w++;
nleft -= 2;
}
if (nleft == 1) {
answer.b[0] = *(u_char *)w;
answer.b[1] = 0;
sum += answer.w;
}
sum = (sum >> 16) + (sum & 0xffff);
sum += (sum >> 16);
answer.w = sum;
return answer.w;
}
static void
tvsub(struct timeval *out, struct timeval *in)
{
if ((out->tv_usec -= in->tv_usec) < 0) {
--out->tv_sec;
out->tv_usec += 1000000;
}
out->tv_sec -= in->tv_sec;
}
static char *
inetname(struct in_addr in)
{
char *cp;
struct hostent *hp;
static int first = 1;
static char domain[MAXHOSTNAMELEN + 1], line[MAXHOSTNAMELEN + 1];
if (first && !nflag) {
first = 0;
if (gethostname(domain, sizeof(domain) - 1) < 0)
domain[0] = '\0';
else {
cp = strchr(domain, '.');
if (cp == NULL) {
hp = gethostbyname(domain);
if (hp != NULL)
cp = strchr(hp->h_name, '.');
}
if (cp == NULL)
domain[0] = '\0';
else {
++cp;
(void)strlcpy(domain, cp, sizeof(domain));
}
}
}
if (!nflag && in.s_addr != INADDR_ANY) {
hp = gethostbyaddr((char *)&in, sizeof(in), AF_INET);
if (hp != NULL) {
if ((cp = strchr(hp->h_name, '.')) != NULL &&
strcmp(cp + 1, domain) == 0)
*cp = '\0';
(void)strlcpy(line, hp->h_name, sizeof(line));
return line;
}
}
return inet_ntoa(in);
}
static struct hostinfo *
gethostinfo(char *hname)
{
int n;
struct hostent *hp;
struct hostinfo *hi;
char **p;
u_int32_t *ap;
struct in_addr addr;
hi = calloc(1, sizeof(*hi));
if (hi == NULL)
err(1, "calloc");
if (inet_aton(hname, &addr) != 0) {
hi->name = strdup(hname);
if (!hi->name)
err(1, "strdup");
hi->n = 1;
hi->addrs = calloc(1, sizeof(hi->addrs[0]));
if (hi->addrs == NULL)
err(1, "calloc");
hi->addrs[0] = addr.s_addr;
return hi;
}
hp = gethostbyname(hname);
if (hp == NULL)
errx(1, "unknown host %s", hname);
if (hp->h_addrtype != AF_INET || hp->h_length != 4)
errx(1, "bad host %s", hname);
hi->name = strdup(hp->h_name);
if (!hi->name)
err(1, "strdup");
for (n = 0, p = hp->h_addr_list; *p != NULL; ++n, ++p)
continue;
hi->n = n;
hi->addrs = calloc(n, sizeof(hi->addrs[0]));
if (hi->addrs == NULL)
err(1, "calloc");
for (ap = hi->addrs, p = hp->h_addr_list; *p != NULL; ++ap, ++p)
memcpy(ap, *p, sizeof(*ap));
return hi;
}
static void
freehostinfo(struct hostinfo *hi)
{
if (hi->name != NULL) {
free(hi->name);
hi->name = NULL;
}
free(hi->addrs);
free(hi);
}
static void
getaddr(u_int32_t *ap, char *hname)
{
struct hostinfo *hi;
hi = gethostinfo(hname);
*ap = hi->addrs[0];
freehostinfo(hi);
}
static void
setsin(struct sockaddr_in *sin, u_int32_t addr)
{
memset(sin, 0, sizeof(*sin));
#ifdef HAVE_SOCKADDR_SA_LEN
sin->sin_len = sizeof(*sin);
#endif
sin->sin_family = AF_INET;
sin->sin_addr.s_addr = addr;
}
static int
str2val(const char *str, const char *what, int mi, int ma)
{
const char *cp;
long val;
char *ep;
errno = 0;
ep = NULL;
if (str[0] == '0' && (str[1] == 'x' || str[1] == 'X')) {
cp = str + 2;
val = strtol(cp, &ep, 16);
} else
val = strtol(str, &ep, 10);
if (errno || str[0] == '\0' || *ep != '\0')
errx(1, "\"%s\" bad value for %s", str, what);
if (val < mi && mi >= 0) {
if (mi == 0)
errx(1, "%s must be >= %d", what, mi);
else
errx(1, "%s must be > %d", what, mi - 1);
}
if (val > ma && ma >= 0)
errx(1, "%s must be <= %d", what, ma);
return (int)val;
}
__dead void
usage(void)
{
extern char version[];
Fprintf(stderr, "Version %s\n", version);
Fprintf(stderr, "Usage: %s [-adDFPIlMnrvx] [-g gateway] [-i iface] \
[-f first_ttl]\n\t[-m max_ttl] [-p port] [-q nqueries] [-s src_addr] [-t tos]\n\t\
[-w waittime] [-z pausemsecs] [-A as_server] host [packetlen]\n",
getprogname());
exit(1);
}
void
frag_err()
{
int i;
if (nextmtu > 0 && nextmtu < packlen) {
Printf("\nfragmentation required and DF set, "
"next hop MTU = %d\n",
nextmtu);
packlen = nextmtu;
for (i = 0; mtus[i] > 0; i++) {
if (mtus[i] < nextmtu) {
mtuptr = &mtus[i];
break;
}
}
} else {
Printf("\nfragmentation required and DF set. ");
if (nextmtu)
Printf("\nBogus next hop MTU = %d > last MTU = %d. ",
nextmtu, packlen);
packlen = *mtuptr++;
Printf("Trying new MTU = %d\n", packlen);
}
resize_packet();
}
int
find_local_ip(struct sockaddr_in *from, struct sockaddr_in *to)
{
int sock;
struct sockaddr_in help;
socklen_t help_len;
sock = prog_socket(AF_INET, SOCK_DGRAM, 0);
if (sock < 0) return 0;
help.sin_family = AF_INET;
help.sin_port = 42;
help.sin_addr.s_addr = to->sin_addr.s_addr;
if (prog_connect(sock, (struct sockaddr *)&help, sizeof(help)) < 0) {
(void)prog_close(sock);
return 0;
}
help_len = sizeof(help);
if (prog_getsockname(sock, (struct sockaddr *)&help, &help_len) < 0 ||
help_len != sizeof(help) ||
help.sin_addr.s_addr == INADDR_ANY) {
(void)prog_close(sock);
return 0;
}
(void)prog_close(sock);
setsin(from, help.sin_addr.s_addr);
return 1;
}
#ifdef IPSEC
#ifdef IPSEC_POLICY_IPSEC
static int
setpolicy(int so, const char *policy)
{
char *buf;
buf = ipsec_set_policy(policy, strlen(policy));
if (buf == NULL) {
warnx("%s", ipsec_strerror());
return -1;
}
(void)prog_setsockopt(so, IPPROTO_IP, IP_IPSEC_POLICY,
buf, ipsec_get_policylen(buf));
free(buf);
return 0;
}
#endif
#endif