Add the capability for traceroute(8) to send packets of any IP protocol

instead of just UDP; an alternate protocol is specified by '-P proto'.
This is useful for finding routers that are blocking packets based on
IP protocol.  New handlers can be added fairly easily to do protocol-
specific things.
This commit is contained in:
Archie Cobbs 1999-05-06 03:23:24 +00:00
parent 6e19760bfa
commit f5004fb09b
Notes: svn2git 2020-12-20 02:59:44 +00:00
svn path=/head/; revision=46542
2 changed files with 244 additions and 37 deletions

View File

@ -13,7 +13,7 @@
.\" IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED
.\" WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE.
.\"
.\" $Header: /home/ncvs/src/contrib/traceroute/traceroute.8,v 1.2 1996/10/08 02:44:23 sef Exp $
.\" $Header: /home/ncvs/src/contrib/traceroute/traceroute.8,v 1.3 1998/01/09 18:46:53 steve Exp $
.\"
.TH TRACEROUTE 8 "27 September 1996"
.UC 6
@ -35,24 +35,29 @@ max_ttl
.br
.ti +8
[
.B \-P
.I proto
] [
.B \-p
.I port
] [
.B \-q
.I nqueries
] [
.B \-s
.I src_addr
]
.br
.ti +8
[
.B \-s
.I src_addr
] [
.B \-t
.I tos
] [
.B \-w
.I waittime
]
.br
.ti +8
.I host
[
.I packetlen
@ -91,8 +96,18 @@ Print hop addresses numerically rather than symbolically and numerically
(saves a nameserver address-to-name lookup for each gateway found on the
path).
.TP
.B \-P
Send packets of specified IP protocol. The currently supported protocols
are: UDP, TCP and GRE. Other protocols may also be specified (either by
name or by number), though
.I traceroute
does not implement any special knowledge of their packet formats. This
option is useful for determining which router along a path may be
blocking packets based on IP protocol number. But see BUGS below.
.TP
.B \-p
Set the base UDP port number used in probes (default is 33434).
Protocol specific. For UDP and TCP, sets
the base port number used in probes (default is 33434).
Traceroute hopes that nothing is listening on UDP ports
.I base
to
@ -298,4 +313,13 @@ The current version is available via anonymous ftp:
.I ftp://ftp.ee.lbl.gov/traceroute.tar.Z
.RE
.SH BUGS
When using protocols other than UDP, functionality is reduced.
In particular, the last packet will often appear to be lost, because
even though it reaches the destination host, there's no way to know
that because no ICMP message is sent back.
In the TCP case,
.I traceroute
should listen for a RST from the destination host (or an intermediate
router that's filtering packets), but this is not implemented yet.
.PP
Please send bug reports to traceroute@ee.lbl.gov.

View File

@ -24,7 +24,7 @@ static const char copyright[] =
"@(#) Copyright (c) 1988, 1989, 1991, 1994, 1995, 1996\n\
The Regents of the University of California. All rights reserved.\n";
static const char rcsid[] =
"@(#)$Header: /home/ncvs/src/contrib/traceroute/traceroute.c,v 1.7 1999/02/15 08:11:44 des Exp $ (LBL)";
"@(#)$Header: /home/ncvs/src/contrib/traceroute/traceroute.c,v 1.8 1999/02/16 14:19:50 des Exp $ (LBL)";
#endif
/*
@ -213,6 +213,7 @@ static const char rcsid[] =
#include <netinet/ip_var.h>
#include <netinet/ip_icmp.h>
#include <netinet/udp.h>
#include <netinet/tcp.h>
#include <arpa/inet.h>
@ -243,6 +244,20 @@ static const char rcsid[] =
#define Fprintf (void)fprintf
#define Printf (void)printf
/* What a GRE packet header looks like */
struct grehdr {
u_int16_t flags;
u_int16_t proto;
u_int16_t length; /* PPTP version of these fields */
u_int16_t callId;
};
#ifndef IPPROTO_GRE
#define IPPROTO_GRE 47
#endif
/* For GRE, we prepare what looks like a PPTP packet */
#define GRE_PPTP_PROTO 0x880b
/* Data section of the probe packet */
struct outdata {
u_char seq; /* sequence number of this packet */
@ -250,11 +265,22 @@ struct outdata {
struct timeval tv; /* time packet left */
};
/* Descriptor structure for each outgoing protocol we support */
struct outproto {
char *name; /* name of protocol */
u_char num; /* IP protocol number */
u_short hdrlen; /* max size of protocol header */
u_short port; /* default base protocol-specific "port" */
void (*prepare)(struct outdata *);
/* finish preparing an outgoing packet */
int (*check)(const u_char *, int);
/* check an incoming packet */
};
u_char packet[512]; /* last inbound (icmp) packet */
struct ip *outip; /* last output (udp) packet */
struct udphdr *outudp; /* last output (udp) packet */
struct outdata *outdata; /* last output (udp) packet */
struct ip *outip; /* last output ip packet */
u_char *outprot; /* last output inner protocol packet */
/* loose source route gateway list (including room for final destination) */
u_int32_t gwlist[NGATEWAYS + 1];
@ -264,6 +290,7 @@ int sndsock; /* send (udp) socket file descriptor */
struct sockaddr whereto; /* Who to try to reach */
int packlen; /* total length of packet */
int protlen; /* length of protocol part of packet */
int maxpacket = 32 * 1024; /* max ip packet size */
char *prog;
@ -273,7 +300,7 @@ char *hostname;
int nprobes = 3;
int max_ttl = 30;
u_short ident;
u_short port = 32768 + 666; /* start udp dest port # for probe packets */
u_short port; /* protocol specific base "port" */
int options; /* socket options */
int verbose;
@ -294,11 +321,58 @@ void print(u_char *, int, struct sockaddr_in *);
char *getaddr(u_int32_t *, char *);
char *getsin(struct sockaddr_in *, char *);
char *savestr(const char *);
void send_probe(int, int, struct timeval *);
void send_probe(int, int);
void tvsub(struct timeval *, struct timeval *);
__dead void usage(void);
int wait_for_reply(int, struct sockaddr_in *, struct timeval *);
void udp_prep(struct outdata *);
int udp_check(const u_char *, int);
void tcp_prep(struct outdata *);
int tcp_check(const u_char *, int);
void gre_prep(struct outdata *);
int gre_check(const u_char *, int);
void gen_prep(struct outdata *);
int gen_check(const u_char *, int);
/* List of supported protocols. The first one is the default. The last
one is the handler for generic protocols not explicitly listed. */
struct outproto protos[] = {
{
"udp",
IPPROTO_UDP,
sizeof(struct udphdr),
32768 + 666,
udp_prep,
udp_check
},
{
"tcp",
IPPROTO_TCP,
sizeof(struct tcphdr),
32768 + 666,
tcp_prep,
tcp_check
},
{
"gre",
IPPROTO_GRE,
sizeof(struct grehdr),
GRE_PPTP_PROTO,
gre_prep,
gre_check
},
{
NULL,
0,
2 * sizeof(u_short),
0,
gen_prep,
gen_check
},
};
struct outproto *proto = &protos[0];
int
main(int argc, char **argv)
{
@ -338,7 +412,7 @@ main(int argc, char **argv)
prog = argv[0];
opterr = 0;
while ((op = getopt(argc, argv, "Sdnrvg:m:p:q:s:t:w:")) != EOF)
while ((op = getopt(argc, argv, "Sdnrvg:m:P:p:q:s:t:w:")) != EOF)
switch (op) {
case 'S':
@ -372,6 +446,36 @@ main(int argc, char **argv)
++nflag;
break;
case 'P':
for (i = 0; protos[i].name != NULL; i++) {
if (strcasecmp(protos[i].name, optarg) == 0) {
proto = &protos[i];
break;
}
}
if (protos[i].name == NULL) { /* generic handler */
struct protoent *pe;
u_long pnum;
char *eptr;
/* Determine the IP protocol number */
if ((pe = getprotobyname(optarg)) != NULL)
pnum = pe->p_proto;
else {
pnum = strtoul(optarg, &eptr, 10);
if (pnum > 0xff
|| *optarg == '\0'
|| *eptr != '\0') {
Fprintf(stderr, "%s: unknown "
"protocol \"%s\"\n",
prog, optarg);
exit(1);
}
}
proto->num = pnum;
}
break;
case 'p':
port = atoi(optarg);
if (port <= 0) {
@ -451,7 +555,7 @@ main(int argc, char **argv)
if (lsrr > 0)
optlen = (lsrr + 1) * sizeof(gwlist[0]);
i = sizeof(*outip) + sizeof(*outudp) + sizeof(*outdata) + optlen;
i = sizeof(*outip) + proto->hdrlen + sizeof(struct outdata) + optlen;
if (packlen == 0)
packlen = i; /* minimum sized packet */
else if (i > packlen || packlen > maxpacket) {
@ -459,6 +563,7 @@ main(int argc, char **argv)
prog, i, maxpacket);
exit(1);
}
protlen = packlen - sizeof(*outip) - optlen;
outip = (struct ip *)malloc((unsigned)packlen);
if (outip == NULL) {
@ -474,14 +579,14 @@ main(int argc, char **argv)
#else
outip->ip_len = packlen;
#endif
outip->ip_p = IPPROTO_UDP;
outudp = (struct udphdr *)(outip + 1);
outip->ip_p = proto->num;
outprot = (u_char *)(outip + 1);
#ifdef HAVE_RAW_OPTIONS
if (lsrr > 0) {
register u_char *optlist;
optlist = (u_char *)outudp;
(u_char *)outudp += optlen;
optlist = (u_char *)outprot;
(u_char *)outprot += optlen;
/* final hop */
gwlist[lsrr] = to->sin_addr.s_addr;
@ -501,13 +606,9 @@ main(int argc, char **argv)
#endif
outip->ip_dst = to->sin_addr;
outip->ip_hl = ((u_char *)outudp - (u_char *)outip) >> 2;
outip->ip_hl = ((u_char *)outprot - (u_char *)outip) >> 2;
ident = (getpid() & 0xffff) | 0x8000;
outudp->uh_sport = htons(ident);
outudp->uh_ulen = htons((u_short)(packlen - (sizeof(*outip) + optlen)));
outdata = (struct outdata *)(outudp + 1);
if (pe == NULL) {
Fprintf(stderr, "%s: unknown protocol %s\n", prog, cp);
@ -616,9 +717,21 @@ main(int argc, char **argv)
struct timeval t1, t2;
struct timezone tz;
register struct ip *ip;
struct outdata outdata;
/* Prepare outgoing data */
outdata.seq = ++seq;
outdata.ttl = ttl;
/* Avoid alignment problems by copying bytewise: */
(void)gettimeofday(&t1, &tz);
send_probe(++seq, ttl, &t1);
memcpy(&outdata.tv, &t1, sizeof(outdata.tv));
/* Finalize and send packet */
(*proto->prepare)(&outdata);
send_probe(seq, ttl);
/* Wait for a reply */
while ((cc = wait_for_reply(s, &from, &t1)) != 0) {
double T;
int precis;
@ -753,21 +866,13 @@ wait_for_reply(register int sock, register struct sockaddr_in *fromp,
}
void
send_probe(register int seq, register int ttl, register struct timeval *tp)
send_probe(int seq, int ttl)
{
register int i;
outip->ip_ttl = ttl;
outip->ip_id = htons(ident + seq);
outudp->uh_dport = htons(port + seq);
outdata->seq = seq;
outdata->ttl = ttl;
/* Avoid alignment problems by copying bytewise: */
memcpy(&outdata->tv, tp, sizeof(outdata->tv));
i = sendto(sndsock, (char *)outip, packlen, 0, &whereto,
sizeof(whereto));
if (i < 0 || i != packlen) {
@ -838,14 +943,14 @@ packet_ok(register u_char *buf, int cc, register struct sockaddr_in *from,
if ((type == ICMP_TIMXCEED && code == ICMP_TIMXCEED_INTRANS) ||
type == ICMP_UNREACH) {
struct ip *hip;
struct udphdr *up;
u_char *inner;
hip = &icp->icmp_ip;
hlen = hip->ip_hl << 2;
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))
inner = (u_char *)((u_char *)hip + hlen);
if (hlen + 12 <= cc
&& hip->ip_p == proto->num
&& (*proto->check)(inner, seq))
return (type == ICMP_TIMXCEED ? -1 : code + 1);
}
#ifndef ARCHAIC
@ -863,6 +968,84 @@ packet_ok(register u_char *buf, int cc, register struct sockaddr_in *from,
return(0);
}
void
udp_prep(struct outdata *outdata)
{
struct udphdr *const udp = (struct udphdr *) outprot;
udp->uh_sport = htons(ident);
udp->uh_dport = htons(port + outdata->seq);
udp->uh_ulen = htons((u_short)protlen);
}
int
udp_check(const u_char *data, int seq)
{
struct udphdr *const udp = (struct udphdr *) data;
return (ntohs(udp->uh_sport) == ident
&& ntohs(udp->uh_dport) == port + seq);
}
void
tcp_prep(struct outdata *outdata)
{
struct tcphdr *const tcp = (struct tcphdr *) outprot;
tcp->th_sport = htons(ident);
tcp->th_dport = htons(port + outdata->seq);
tcp->th_seq = (tcp->th_sport << 16) | tcp->th_dport;
tcp->th_ack = 0;
tcp->th_off = 5;
tcp->th_flags = TH_SYN;
}
int
tcp_check(const u_char *data, int seq)
{
struct tcphdr *const tcp = (struct tcphdr *) data;
return (ntohs(tcp->th_sport) == ident
&& ntohs(tcp->th_dport) == port + seq);
}
void
gre_prep(struct outdata *outdata)
{
struct grehdr *const gre = (struct grehdr *) outprot;
gre->flags = htons(0x2001);
gre->proto = htons(port);
gre->length = 0;
gre->callId = htons(ident + outdata->seq);
}
int
gre_check(const u_char *data, int seq)
{
struct grehdr *const gre = (struct grehdr *) data;
return(ntohs(gre->proto) == port
&& ntohs(gre->callId) == ident + seq);
}
void
gen_prep(struct outdata *outdata)
{
u_int16_t *const ptr;
ptr[0] = htons(ident);
ptr[1] = htons(port + outdata->seq);
}
int
gen_check(const u_char *data, int seq)
{
u_int16_t *const ptr = (u_int16_t *) data;
return(ntohs(ptr[0]) == ident
&& ntohs(ptr[1]) == port + seq);
}
void
print(register u_char *buf, register int cc, register struct sockaddr_in *from)