diff options
author | archie <archie@FreeBSD.org> | 1999-05-06 03:23:24 +0000 |
---|---|---|
committer | archie <archie@FreeBSD.org> | 1999-05-06 03:23:24 +0000 |
commit | 78c2183cb646fef06657bbcf5849e433600ce2e3 (patch) | |
tree | b73667c2ac1116ec517d50021e8d7012a2b19edf | |
parent | 254c5bc5a20a0ae065603b34619a9979d1029023 (diff) | |
download | FreeBSD-src-78c2183cb646fef06657bbcf5849e433600ce2e3.zip FreeBSD-src-78c2183cb646fef06657bbcf5849e433600ce2e3.tar.gz |
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.
-rw-r--r-- | contrib/traceroute/traceroute.8 | 34 | ||||
-rw-r--r-- | contrib/traceroute/traceroute.c | 247 |
2 files changed, 244 insertions, 37 deletions
diff --git a/contrib/traceroute/traceroute.8 b/contrib/traceroute/traceroute.8 index d4f1bc2..26e11b6 100644 --- a/contrib/traceroute/traceroute.8 +++ b/contrib/traceroute/traceroute.8 @@ -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. diff --git a/contrib/traceroute/traceroute.c b/contrib/traceroute/traceroute.c index 9f235a5..2a6dd51 100644 --- a/contrib/traceroute/traceroute.c +++ b/contrib/traceroute/traceroute.c @@ -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) |