Give traceroute6 the ability to traceroute with packets with no

upper layer header (IP PROTO = 59). Useful for testing firewalls.

MFC after:	2 months
This commit is contained in:
David Malone 2008-02-10 21:06:38 +00:00
parent 31d48c5406
commit d7b63faf0c
Notes: svn2git 2020-12-20 02:59:44 +00:00
svn path=/head/; revision=176154
2 changed files with 111 additions and 48 deletions

View File

@ -40,7 +40,7 @@
.Sh SYNOPSIS
.Nm
.Bk -words
.Op Fl dIlnrv
.Op Fl dIlnNrvU
.Ek
.Bk -words
.Op Fl f Ar firsthop
@ -108,6 +108,9 @@ Specify maximum hoplimit, up to 255.
The default is 30 hops.
.It Fl n
Do not resolve numeric address to hostname.
.It Fl N
Use a packet with no upper layer header for the probes,
instead of UDP datagrams.
.It Fl p Ar port
Set UDP port number to
.Ar port .
@ -128,6 +131,9 @@ that has no route through it
.It Fl s Ar src
.Ar Src
specifies the source IPv6 address to be used.
.It Fl U
Use UDP datagrams for the probes.
This is the default.
.It Fl v
Be verbose.
.It Fl w Ar waittime

View File

@ -317,7 +317,7 @@ int setpolicy(int so, char *policy);
#endif
#endif
void send_probe(int, u_long);
struct udphdr *get_udphdr(struct ip6_hdr *, u_char *);
void *get_uphdr(struct ip6_hdr *, u_char *);
int get_hoplim(struct msghdr *);
double deltaT(struct timeval *, struct timeval *);
char *pr_type(int);
@ -357,7 +357,7 @@ int options; /* socket options */
int verbose;
int waittime = 5; /* time to wait for response (in seconds) */
int nflag; /* print addresses numerically */
int useicmp;
int useproto = IPPROTO_UDP; /* protocol to use to send packet */
int lflag; /* print both numerical address & hostname */
int
@ -383,13 +383,6 @@ main(argc, argv)
exit(5);
}
/* revoke privs */
uid = getuid();
if (setresuid(uid, uid, uid) == -1) {
perror("setresuid");
exit(1);
}
size = sizeof(i);
(void) sysctl(mib, sizeof(mib)/sizeof(mib[0]), &i, &size, NULL, 0);
max_hops = i;
@ -418,7 +411,7 @@ main(argc, argv)
seq = 0;
while ((ch = getopt(argc, argv, "df:g:Ilm:np:q:rs:w:v")) != -1)
while ((ch = getopt(argc, argv, "df:g:Ilm:nNp:q:rs:Uvw:")) != -1)
switch (ch) {
case 'd':
options |= SO_DEBUG;
@ -470,7 +463,7 @@ main(argc, argv)
freehostent(hp);
break;
case 'I':
useicmp++;
useproto = IPPROTO_ICMPV6;
ident = htons(getpid() & 0xffff); /* same as ping6 */
break;
case 'l':
@ -489,6 +482,9 @@ main(argc, argv)
case 'n':
nflag++;
break;
case 'N':
useproto = IPPROTO_NONE;
break;
case 'p':
ep = NULL;
errno = 0;
@ -532,6 +528,9 @@ main(argc, argv)
case 'v':
verbose++;
break;
case 'U':
useproto = IPPROTO_UDP;
break;
case 'w':
ep = NULL;
errno = 0;
@ -553,12 +552,44 @@ main(argc, argv)
argc -= optind;
argv += optind;
/*
* Open socket to send probe packets.
*/
switch (useproto) {
case IPPROTO_ICMPV6:
sndsock = rcvsock;
break;
case IPPROTO_UDP:
if ((sndsock = socket(AF_INET6, SOCK_DGRAM, 0)) < 0) {
perror("socket(SOCK_DGRAM)");
exit(5);
}
break;
case IPPROTO_NONE:
if ((sndsock = socket(AF_INET6, SOCK_RAW, IPPROTO_NONE)) < 0) {
perror("socket(SOCK_RAW)");
exit(5);
}
break;
default:
fprintf(stderr, "traceroute6: unknown probe protocol %d",
useproto);
exit(5);
}
if (max_hops < first_hop) {
fprintf(stderr,
"traceroute6: max hoplimit must be larger than first hoplimit.\n");
exit(1);
}
/* revoke privs */
uid = getuid();
if (setresuid(uid, uid, uid) == -1) {
perror("setresuid");
exit(1);
}
if (argc < 1 || argc > 2)
usage();
@ -608,10 +639,22 @@ main(argc, argv)
exit(1);
}
}
if (useicmp)
switch (useproto) {
case IPPROTO_ICMPV6:
minlen = ICMP6ECHOLEN + sizeof(struct tv32);
else
break;
case IPPROTO_UDP:
minlen = sizeof(struct opacket);
break;
case IPPROTO_NONE:
minlen = 0;
datalen = 0;
break;
default:
fprintf(stderr, "traceroute6: unknown probe protocol %d.\n",
useproto);
exit(1);
}
if (datalen < minlen)
datalen = minlen;
else if (datalen >= MAXPACKET) {
@ -682,21 +725,10 @@ main(argc, argv)
#endif /*IPSEC_POLICY_IPSEC*/
#endif /*IPSEC*/
/*
* Send UDP or ICMP
*/
if (useicmp) {
sndsock = rcvsock;
} else {
if ((sndsock = socket(AF_INET6, SOCK_DGRAM, 0)) < 0) {
perror("socket(SOCK_DGRAM)");
exit(5);
}
}
#ifdef SO_SNDBUF
i = datalen;
if (setsockopt(sndsock, SOL_SOCKET, SO_SNDBUF, (char *)&i,
sizeof(i)) < 0) {
sizeof(i)) < 0 && useproto != IPPROTO_NONE) {
perror("setsockopt(SO_SNDBUF)");
exit(6);
}
@ -986,6 +1018,8 @@ send_probe(seq, hops)
int seq;
u_long hops;
{
struct icmp6_hdr *icp;
struct opacket *op;
struct timeval tv;
struct tv32 tv32;
int i;
@ -1001,8 +1035,9 @@ send_probe(seq, hops)
tv32.tv32_sec = htonl(tv.tv_sec);
tv32.tv32_usec = htonl(tv.tv_usec);
if (useicmp) {
struct icmp6_hdr *icp = (struct icmp6_hdr *)outpacket;
switch (useproto) {
case IPPROTO_ICMPV6:
icp = (struct icmp6_hdr *)outpacket;
icp->icmp6_type = ICMP6_ECHO_REQUEST;
icp->icmp6_code = 0;
@ -1011,12 +1046,20 @@ send_probe(seq, hops)
icp->icmp6_seq = htons(seq);
bcopy(&tv32, ((u_int8_t *)outpacket + ICMP6ECHOLEN),
sizeof(tv32));
} else {
struct opacket *op = outpacket;
break;
case IPPROTO_UDP:
op = outpacket;
op->seq = seq;
op->hops = hops;
bcopy(&tv32, &op->tv, sizeof tv32);
break;
case IPPROTO_NONE:
/* No space for anything. No harm as seq/tv32 are decorative. */
break;
default:
fprintf(stderr, "Unknown probe protocol %d.\n", useproto);
exit(1);
}
i = sendto(sndsock, (char *)outpacket, datalen, 0,
@ -1196,23 +1239,34 @@ packet_ok(mhdr, cc, seq)
if ((type == ICMP6_TIME_EXCEEDED && code == ICMP6_TIME_EXCEED_TRANSIT)
|| type == ICMP6_DST_UNREACH) {
struct ip6_hdr *hip;
struct udphdr *up;
void *up;
hip = (struct ip6_hdr *)(icp + 1);
if ((up = get_udphdr(hip, (u_char *)(buf + cc))) == NULL) {
if ((up = get_uphdr(hip, (u_char *)(buf + cc))) == NULL) {
if (verbose)
warnx("failed to get upper layer header");
return(0);
}
if (useicmp &&
((struct icmp6_hdr *)up)->icmp6_id == ident &&
((struct icmp6_hdr *)up)->icmp6_seq == htons(seq))
return (type == ICMP6_TIME_EXCEEDED ? -1 : code + 1);
else if (!useicmp &&
up->uh_sport == htons(srcport) &&
up->uh_dport == htons(port + seq))
return (type == ICMP6_TIME_EXCEEDED ? -1 : code + 1);
} else if (useicmp && type == ICMP6_ECHO_REPLY) {
switch (useproto) {
case IPPROTO_ICMPV6:
if (((struct icmp6_hdr *)up)->icmp6_id == ident &&
((struct icmp6_hdr *)up)->icmp6_seq == htons(seq))
return (type == ICMP6_TIME_EXCEEDED ?
-1 : code + 1);
break;
case IPPROTO_UDP:
if (((struct udphdr *)up)->uh_sport == htons(srcport) &&
((struct udphdr *)up)->uh_dport == htons(port + seq))
return (type == ICMP6_TIME_EXCEEDED ?
-1 : code + 1);
break;
case IPPROTO_NONE:
return (type == ICMP6_TIME_EXCEEDED ? -1 : code + 1);
default:
fprintf(stderr, "Unknown probe proto %d.\n", useproto);
break;
}
} else if (useproto == IPPROTO_ICMPV6 && type == ICMP6_ECHO_REPLY) {
if (icp->icmp6_id == ident &&
icp->icmp6_seq == htons(seq))
return (ICMP6_DST_UNREACH_NOPORT + 1);
@ -1250,29 +1304,32 @@ packet_ok(mhdr, cc, seq)
/*
* Increment pointer until find the UDP or ICMP header.
*/
struct udphdr *
get_udphdr(ip6, lim)
void *
get_uphdr(ip6, lim)
struct ip6_hdr *ip6;
u_char *lim;
{
u_char *cp = (u_char *)ip6, nh;
int hlen;
static u_char none_hdr[1]; /* Fake pointer for IPPROTO_NONE. */
if (cp + sizeof(*ip6) >= lim)
if (cp + sizeof(*ip6) > lim)
return(NULL);
nh = ip6->ip6_nxt;
cp += sizeof(struct ip6_hdr);
while (lim - cp >= 8) {
while (lim - cp >= (nh == IPPROTO_NONE ? 0 : 8)) {
switch (nh) {
case IPPROTO_ESP:
case IPPROTO_TCP:
return(NULL);
case IPPROTO_ICMPV6:
return(useicmp ? (struct udphdr *)cp : NULL);
return(useproto == nh ? cp : NULL);
case IPPROTO_UDP:
return(useicmp ? NULL : (struct udphdr *)cp);
return(useproto == nh ? cp : NULL);
case IPPROTO_NONE:
return(useproto == nh ? none_hdr : NULL);
case IPPROTO_FRAGMENT:
hlen = sizeof(struct ip6_frag);
nh = ((struct ip6_frag *)cp)->ip6f_nxt;
@ -1369,7 +1426,7 @@ usage()
{
fprintf(stderr,
"usage: traceroute6 [-dIlnrv] [-f firsthop] [-g gateway] [-m hoplimit]\n"
"usage: traceroute6 [-dIlnNrUv] [-f firsthop] [-g gateway] [-m hoplimit]\n"
" [-p port] [-q probes] [-s src] [-w waittime] target [datalen]\n");
exit(1);
}