From d7b63faf0c556747301e07a416dea9751c92f500 Mon Sep 17 00:00:00 2001 From: David Malone Date: Sun, 10 Feb 2008 21:06:38 +0000 Subject: [PATCH] Give traceroute6 the ability to traceroute with packets with no upper layer header (IP PROTO = 59). Useful for testing firewalls. MFC after: 2 months --- usr.sbin/traceroute6/traceroute6.8 | 8 +- usr.sbin/traceroute6/traceroute6.c | 151 ++++++++++++++++++++--------- 2 files changed, 111 insertions(+), 48 deletions(-) diff --git a/usr.sbin/traceroute6/traceroute6.8 b/usr.sbin/traceroute6/traceroute6.8 index 9d39e2b86d6f..6e178c68adaa 100644 --- a/usr.sbin/traceroute6/traceroute6.8 +++ b/usr.sbin/traceroute6/traceroute6.8 @@ -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 diff --git a/usr.sbin/traceroute6/traceroute6.c b/usr.sbin/traceroute6/traceroute6.c index 617e5a5da771..d06502f076f6 100644 --- a/usr.sbin/traceroute6/traceroute6.c +++ b/usr.sbin/traceroute6/traceroute6.c @@ -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); }