diff options
Diffstat (limited to 'usr.sbin/traceroute6')
| -rw-r--r-- | usr.sbin/traceroute6/Makefile | 40 | ||||
| -rw-r--r-- | usr.sbin/traceroute6/Makefile.depend | 20 | ||||
| -rw-r--r-- | usr.sbin/traceroute6/traceroute6.8 | 217 | ||||
| -rw-r--r-- | usr.sbin/traceroute6/traceroute6.c | 1830 |
4 files changed, 2107 insertions, 0 deletions
diff --git a/usr.sbin/traceroute6/Makefile b/usr.sbin/traceroute6/Makefile new file mode 100644 index 000000000000..d2b84296fe97 --- /dev/null +++ b/usr.sbin/traceroute6/Makefile @@ -0,0 +1,40 @@ +# Copyright (c) 1996 WIDE Project. All rights reserved. +# +# Redistribution and use in source and binary forms, with or without +# modifications, are permitted provided that the above copyright notice +# and this paragraph are duplicated in all such forms and that any +# documentation, advertising materials, and other materials related to +# such distribution and use acknowledge that the software was developed +# by the WIDE Project, Japan. The name of the Project may not be used to +# endorse or promote products derived from this software without +# specific prior written permission. THIS SOFTWARE IS PROVIDED ``AS IS'' +# AND WITHOUT ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, WITHOUT +# LIMITATION, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +# A PARTICULAR PURPOSE. + +.include <src.opts.mk> + +TRACEROUTE_DISTDIR?= ${SRCTOP}/usr.sbin/traceroute +.PATH: ${TRACEROUTE_DISTDIR} + +PACKAGE= runtime +PROG= traceroute6 +MAN= traceroute6.8 +SRCS= as.c traceroute6.c +BINOWN= root +BINMODE= 4555 + +CFLAGS+= -DIPSEC -DHAVE_POLL +CFLAGS+= -I${.CURDIR} -I${TRACEROUTE_DISTDIR} -I. + +.if ${MK_CASPER} != "no" +LIBADD+= casper +LIBADD+= cap_dns +CFLAGS+= -DWITH_CASPER +.endif + +LIBADD+= ipsec + +.include <bsd.prog.mk> + +CWARNFLAGS+= -Wno-cast-align diff --git a/usr.sbin/traceroute6/Makefile.depend b/usr.sbin/traceroute6/Makefile.depend new file mode 100644 index 000000000000..fe5482dd4b09 --- /dev/null +++ b/usr.sbin/traceroute6/Makefile.depend @@ -0,0 +1,20 @@ +# Autogenerated - do NOT edit! + +DIRDEPS = \ + include \ + include/arpa \ + include/xlocale \ + lib/${CSU_DIR} \ + lib/libc \ + lib/libcapsicum \ + lib/libcasper/libcasper \ + lib/libcasper/services/cap_dns \ + lib/libcompiler_rt \ + lib/libipsec \ + + +.include <dirdeps.mk> + +.if ${DEP_RELDIR} == ${_DEP_RELDIR} +# local dependencies - needed for -jN in clean tree +.endif diff --git a/usr.sbin/traceroute6/traceroute6.8 b/usr.sbin/traceroute6/traceroute6.8 new file mode 100644 index 000000000000..406a96a04424 --- /dev/null +++ b/usr.sbin/traceroute6/traceroute6.8 @@ -0,0 +1,217 @@ +.\" $KAME: traceroute6.8,v 1.10 2004/06/06 12:35:15 suz Exp $ +.\" +.\" Copyright (C) 1995, 1996, 1997, and 1998 WIDE Project. +.\" All rights reserved. +.\" +.\" Redistribution and use in source and binary forms, with or without +.\" modification, are permitted provided that the following conditions +.\" are met: +.\" 1. Redistributions of source code must retain the above copyright +.\" notice, this list of conditions and the following disclaimer. +.\" 2. Redistributions in binary form must reproduce the above copyright +.\" notice, this list of conditions and the following disclaimer in the +.\" documentation and/or other materials provided with the distribution. +.\" 3. Neither the name of the project nor the names of its contributors +.\" may be used to endorse or promote products derived from this software +.\" without specific prior written permission. +.\" +.\" THIS SOFTWARE IS PROVIDED BY THE PROJECT AND CONTRIBUTORS ``AS IS'' AND +.\" ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +.\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +.\" ARE DISCLAIMED. IN NO EVENT SHALL THE PROJECT OR CONTRIBUTORS BE LIABLE +.\" FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +.\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS +.\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) +.\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +.\" LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY +.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF +.\" SUCH DAMAGE. +.\" +.Dd November 12, 2024 +.Dt TRACEROUTE6 8 +.Os +.\" +.Sh NAME +.Nm traceroute6 +.Nd "print the route IPv6 packets will take to a network node" +.\" +.Sh SYNOPSIS +.Nm +.Bk -words +.Op Fl adEIlnNrSTUv +.Ek +.Bk -words +.Op Fl A Ar as_server +.Ek +.Bk -words +.Op Fl f Ar firsthop +.Ek +.Bk -words +.Op Fl g Ar gateway +.Ek +.Bk -words +.Op Fl m Ar hoplimit +.Ek +.Bk -words +.Op Fl p Ar port +.Ek +.Bk -words +.Op Fl q Ar probes +.Ek +.Bk -words +.Op Fl s Ar src +.Ek +.Bk -words +.Op Fl t Ar tclass +.Ek +.Bk -words +.Op Fl w Ar waittime +.Ek +.Bk -words +.Ar target +.Op Ar datalen +.Ek +.\" +.Sh DESCRIPTION +The +.Nm +utility uses the IPv6 protocol hop limit field to elicit an ICMP6 +TIME_EXCEEDED response from each gateway along the path to some host. +.Pp +The only mandatory parameter is the destination host name or IPv6 address. +The default probe datagram carries 20 bytes of payload, in addition to the IPv6 +header. +The size of the payload can be specified by giving a length (in bytes) after +the destination host name. +.Pp +Other options are: +.Bl -tag -width Ds +.It Fl a +Turn on AS# lookups for each hop encountered. +.It Fl A Ar as_server +Turn on AS# lookups and use the given server instead of the default. +.It Fl d +Debug mode. +.It Fl E +Detect ECN bleaching. +Set the +.Em IPTOS_ECN_ECT1 +Explicit Congestion Notification (ECN) bits +.Pq Dv 01 , +and report if the hop has bleached +.Pq Dv 00 +or mangled +.Pq Dv 10 +them, or if it is experiencing congestion +.Pq Dv 11 . +Otherwise, report that it passed the bits appropriately. +If +.Fl t +is also specified, the corresponding ECN bits will be replaced. +.It Fl f Ar firsthop +Specify how many hops to skip in trace. +.It Fl g Ar gateway +Specify intermediate gateway. +Please note that +.Nm +tries to use routing headers. +.It Fl I +Use ICMP6 ECHO instead of UDP datagrams. +.It Fl l +Ignored for backward compatibility. +.It Fl m Ar hoplimit +Specify maximum hoplimit, up to 255. +The default is the value of the +.Va net.inet6.ip6.hlim +.Xr sysctl 8 +(the same default used for TCP connections). +.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 SCTP/TCP/UDP port number to +.Ar port . +.It Fl q Ar probes +Set the number of probe per hop count to +.Ar probes . +.It Fl r +Bypass the normal routing tables and send directly to a host on an attached +network. +If the host is not on a directly-connected network, +an error is returned. +This option corresponds to the +.Dv SO_DONTROUTE +socket option; it can be used to ping a local host through an interface that +has no route through it (e.g., after the interface was dropped by a routing +daemon). +.It Fl s Ar src +.Ar Src +specifies the source IPv6 address to be used. +.It Fl S +Use SCTP packets for the probes. +The size of probe packets must be a multiple of 4. +If +.Ar datalen +is up to 28, probe packets consist of a SHUTDOWN-ACK chunk possibly bundled +with a PAD chunk. +For larger probe packets, an INIT chunk is used. +.It Fl t Ar tclass +.Ar tclass +specifies the +.Em traffic class +used when sending probe packets. +The value must be a decimal integer in the range 0 to 255. +The default is 0. +.It Fl T +Use TCP segments for the probes. +.It Fl U +Use UDP datagrams for the probes. +This is the default. +.It Fl v +Be verbose. +.It Fl w Ar waittime +Specify the delay time between probes. +.El +.Pp +This program prints the route to the given destination and the round-trip time +to each gateway, in the same manner as traceroute. +.Pp +Here is a list of possible annotations after the round-trip time for each +gateway: +.Bl -hang -offset indent +.It !N +Destination Unreachable - No Route to Host. +.It !P +Destination Unreachable - Administratively Prohibited. +.It !S +Destination Unreachable - Not a Neighbour. +.It !A +Destination Unreachable - Address Unreachable. +.It !H +Parameter Problem - Unrecognized Next Header Type. +.It !<num> +ICMP6 unreachable code <num>. +.It !\& +This is printed if the hop limit is <= 1 on a port unreachable message. +This means that the packet got to the destination, but that the reply had a hop +limit that was just large enough to allow it to get back to the source of the +traceroute6. +This was more interesting in the IPv4 case, where some IP stack bugs could be +identified by this behaviour. +.El +.\" +.Sh EXIT STATUS +The +.Nm +utility will exit with 0 on success, and non-zero on errors. +.\" +.Sh SEE ALSO +.Xr ping 8 , +.Xr traceroute 8 +.\" +.Sh HISTORY +The +.Nm +utility first appeared in WIDE hydrangea IPv6 protocol stack kit. diff --git a/usr.sbin/traceroute6/traceroute6.c b/usr.sbin/traceroute6/traceroute6.c new file mode 100644 index 000000000000..173e97c13bb3 --- /dev/null +++ b/usr.sbin/traceroute6/traceroute6.c @@ -0,0 +1,1830 @@ +/* $KAME: traceroute6.c,v 1.68 2004/01/25 11:16:12 suz Exp $ */ + +/*- + * SPDX-License-Identifier: BSD-3-Clause + * + * Copyright (C) 1995, 1996, 1997, and 1998 WIDE Project. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the project nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE PROJECT AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE PROJECT OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +/*- + * Copyright (c) 1990, 1993 + * The Regents of the University of California. All rights reserved. + * + * This code is derived from software contributed to Berkeley by + * Van Jacobson. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +/* + * traceroute host - trace the route ip packets follow going to "host". + * + * Attempt to trace the route an ip packet would follow to some + * internet host. We find out intermediate hops by launching probe + * packets with a small ttl (time to live) then listening for an + * icmp "time exceeded" reply from a gateway. We start our probes + * with a ttl of one and increase by one until we get an icmp "port + * unreachable" (which means we got to "host") or hit a max (which + * defaults to net.inet.ip.ttl hops & can be changed with the -m flag). + * Three probes (change with -q flag) are sent at each ttl setting and + * a line is printed showing the ttl, address of the gateway and + * round trip time of each probe. If the probe answers come from + * different gateways, the address of each responding system will + * be printed. If there is no response within a 5 sec. timeout + * interval (changed with the -w flag), a "*" is printed for that + * probe. + * + * Probe packets are UDP format. We don't want the destination + * host to process them so the destination port is set to an + * unlikely value (if some clod on the destination is using that + * value, it can be changed with the -p flag). + * + * A sample use might be: + * + * [yak 71]% traceroute nis.nsf.net. + * traceroute to nis.nsf.net (35.1.1.48), 64 hops max, 40 byte packets + * 1 helios.ee.lbl.gov (128.3.112.1) 19 ms 19 ms 0 ms + * 2 lilac-dmc.Berkeley.EDU (128.32.216.1) 39 ms 39 ms 19 ms + * 3 lilac-dmc.Berkeley.EDU (128.32.216.1) 39 ms 39 ms 19 ms + * 4 ccngw-ner-cc.Berkeley.EDU (128.32.136.23) 39 ms 40 ms 39 ms + * 5 ccn-nerif22.Berkeley.EDU (128.32.168.22) 39 ms 39 ms 39 ms + * 6 128.32.197.4 (128.32.197.4) 40 ms 59 ms 59 ms + * 7 131.119.2.5 (131.119.2.5) 59 ms 59 ms 59 ms + * 8 129.140.70.13 (129.140.70.13) 99 ms 99 ms 80 ms + * 9 129.140.71.6 (129.140.71.6) 139 ms 239 ms 319 ms + * 10 129.140.81.7 (129.140.81.7) 220 ms 199 ms 199 ms + * 11 nic.merit.edu (35.1.1.48) 239 ms 239 ms 239 ms + * + * Note that lines 2 & 3 are the same. This is due to a buggy + * kernel on the 2nd hop system -- lbl-csam.arpa -- that forwards + * packets with a zero ttl. + * + * A more interesting example is: + * + * [yak 72]% traceroute allspice.lcs.mit.edu. + * traceroute to allspice.lcs.mit.edu (18.26.0.115), 64 hops max, 40 byte packets + * 1 helios.ee.lbl.gov (128.3.112.1) 0 ms 0 ms 0 ms + * 2 lilac-dmc.Berkeley.EDU (128.32.216.1) 19 ms 19 ms 19 ms + * 3 lilac-dmc.Berkeley.EDU (128.32.216.1) 39 ms 19 ms 19 ms + * 4 ccngw-ner-cc.Berkeley.EDU (128.32.136.23) 19 ms 39 ms 39 ms + * 5 ccn-nerif22.Berkeley.EDU (128.32.168.22) 20 ms 39 ms 39 ms + * 6 128.32.197.4 (128.32.197.4) 59 ms 119 ms 39 ms + * 7 131.119.2.5 (131.119.2.5) 59 ms 59 ms 39 ms + * 8 129.140.70.13 (129.140.70.13) 80 ms 79 ms 99 ms + * 9 129.140.71.6 (129.140.71.6) 139 ms 139 ms 159 ms + * 10 129.140.81.7 (129.140.81.7) 199 ms 180 ms 300 ms + * 11 129.140.72.17 (129.140.72.17) 300 ms 239 ms 239 ms + * 12 * * * + * 13 128.121.54.72 (128.121.54.72) 259 ms 499 ms 279 ms + * 14 * * * + * 15 * * * + * 16 * * * + * 17 * * * + * 18 ALLSPICE.LCS.MIT.EDU (18.26.0.115) 339 ms 279 ms 279 ms + * + * (I start to see why I'm having so much trouble with mail to + * MIT.) Note that the gateways 12, 14, 15, 16 & 17 hops away + * either don't send ICMP "time exceeded" messages or send them + * with a ttl too small to reach us. 14 - 17 are running the + * MIT C Gateway code that doesn't send "time exceeded"s. God + * only knows what's going on with 12. + * + * The silent gateway 12 in the above may be the result of a bug in + * the 4.[23]BSD network code (and its derivatives): 4.x (x <= 3) + * sends an unreachable message using whatever ttl remains in the + * original datagram. Since, for gateways, the remaining ttl is + * zero, the icmp "time exceeded" is guaranteed to not make it back + * to us. The behavior of this bug is slightly more interesting + * when it appears on the destination system: + * + * 1 helios.ee.lbl.gov (128.3.112.1) 0 ms 0 ms 0 ms + * 2 lilac-dmc.Berkeley.EDU (128.32.216.1) 39 ms 19 ms 39 ms + * 3 lilac-dmc.Berkeley.EDU (128.32.216.1) 19 ms 39 ms 19 ms + * 4 ccngw-ner-cc.Berkeley.EDU (128.32.136.23) 39 ms 40 ms 19 ms + * 5 ccn-nerif35.Berkeley.EDU (128.32.168.35) 39 ms 39 ms 39 ms + * 6 csgw.Berkeley.EDU (128.32.133.254) 39 ms 59 ms 39 ms + * 7 * * * + * 8 * * * + * 9 * * * + * 10 * * * + * 11 * * * + * 12 * * * + * 13 rip.Berkeley.EDU (128.32.131.22) 59 ms ! 39 ms ! 39 ms ! + * + * Notice that there are 12 "gateways" (13 is the final + * destination) and exactly the last half of them are "missing". + * What's really happening is that rip (a Sun-3 running Sun OS3.5) + * is using the ttl from our arriving datagram as the ttl in its + * icmp reply. So, the reply will time out on the return path + * (with no notice sent to anyone since icmp's aren't sent for + * icmp's) until we probe with a ttl that's at least twice the path + * length. I.e., rip is really only 7 hops away. A reply that + * returns with a ttl of 1 is a clue this problem exists. + * Traceroute prints a "!" after the time if the ttl is <= 1. + * Since vendors ship a lot of obsolete (DEC's Ultrix, Sun 3.x) or + * non-standard (HPUX) software, expect to see this problem + * frequently and/or take care picking the target host of your + * probes. + * + * Other possible annotations after the time are !H, !N, !P (got a host, + * network or protocol unreachable, respectively), !S or !F (source + * route failed or fragmentation needed -- neither of these should + * ever occur and the associated gateway is busted if you see one). If + * almost all the probes result in some kind of unreachable, traceroute + * will give up and exit. + * + * Notes + * ----- + * This program must be run by root or be setuid. (I suggest that + * you *don't* make it setuid -- casual use could result in a lot + * of unnecessary traffic on our poor, congested nets.) + * + * This program requires a kernel mod that does not appear in any + * system available from Berkeley: A raw ip socket using proto + * IPPROTO_RAW must interpret the data sent as an ip datagram (as + * opposed to data to be wrapped in an ip datagram). See the README + * file that came with the source to this program for a description + * of the mods I made to /sys/netinet/raw_ip.c. Your mileage may + * vary. But, again, ANY 4.x (x < 4) BSD KERNEL WILL HAVE TO BE + * MODIFIED TO RUN THIS PROGRAM. + * + * The udp port usage may appear bizarre (well, ok, it is bizarre). + * The problem is that an icmp message only contains 8 bytes of + * data from the original datagram. 8 bytes is the size of a udp + * header so, if we want to associate replies with the original + * datagram, the necessary information must be encoded into the + * udp header (the ip id could be used but there's no way to + * interlock with the kernel's assignment of ip id's and, anyway, + * it would have taken a lot more kernel hacking to allow this + * code to set the ip id). So, to allow two or more users to + * use traceroute simultaneously, we use this task's pid as the + * source port (the high bit is set to move the port number out + * of the "likely" range). To keep track of which probe is being + * replied to (so times and/or hop counts don't get confused by a + * reply that was delayed in transit), we increment the destination + * port number before each probe. + * + * Don't use this as a coding example. I was trying to find a + * routing problem and this code sort-of popped out after 48 hours + * without sleep. I was amazed it ever compiled, much less ran. + * + * I stole the idea for this program from Steve Deering. Since + * the first release, I've learned that had I attended the right + * IETF working group meetings, I also could have stolen it from Guy + * Almes or Matt Mathis. I don't know (or care) who came up with + * the idea first. I envy the originators' perspicacity and I'm + * glad they didn't keep the idea a secret. + * + * Tim Seaver, Ken Adelman and C. Philip Wood provided bug fixes and/or + * enhancements to the original distribution. + * + * I've hacked up a round-trip-route version of this that works by + * sending a loose-source-routed udp datagram through the destination + * back to yourself. Unfortunately, SO many gateways botch source + * routing, the thing is almost worthless. Maybe one day... + * + * -- Van Jacobson (van@ee.lbl.gov) + * Tue Dec 20 03:50:13 PST 1988 + */ + +#include <sys/param.h> +#include <sys/capsicum.h> +#include <sys/time.h> +#include <sys/socket.h> +#include <sys/uio.h> +#include <sys/file.h> +#include <sys/ioctl.h> +#include <sys/sysctl.h> + +#include <netinet/in.h> + +#include <arpa/inet.h> + +#include <libcasper.h> +#include <casper/cap_dns.h> +#include <capsicum_helpers.h> + +#include <netdb.h> +#include <stdio.h> +#include <err.h> +#ifdef HAVE_POLL +#include <poll.h> +#endif +#include <errno.h> +#include <stdlib.h> +#include <string.h> +#include <unistd.h> + +#include <netinet/ip.h> +#include <netinet/ip6.h> +#include <netinet/icmp6.h> +#include <netinet/sctp.h> +#include <netinet/sctp_header.h> +#include <netinet/tcp.h> +#include <netinet/udp.h> + +#ifdef IPSEC +#include <net/route.h> +#include <netipsec/ipsec.h> +#endif + +#include "as.h" + +#define DUMMY_PORT 10010 + +#define MAXPACKET 65535 /* max ip packet size */ + +static u_char packet[512]; /* last inbound (icmp) packet */ +static char *outpacket; /* last output packet */ + +int main(int, char *[]); +int wait_for_reply(int, struct msghdr *); +#if defined(IPSEC) && defined(IPSEC_POLICY_IPSEC) +int setpolicy(int so, char *policy); +#endif +void send_probe(int, u_long); +void *get_uphdr(struct ip6_hdr *, u_char *); +void capdns_open(void); +int get_hoplim(struct msghdr *); +double deltaT(struct timeval *, struct timeval *); +const char *pr_type(int); +int packet_ok(struct msghdr *, int, int, u_char *, u_char *, u_char *); +void print(struct msghdr *, int); +const char *inetname(struct sockaddr *); +u_int32_t sctp_crc32c(void *, u_int32_t); +u_int16_t in_cksum(u_int16_t *addr, int); +u_int16_t udp_cksum(struct sockaddr_in6 *, struct sockaddr_in6 *, + void *, u_int32_t); +u_int16_t tcp_chksum(struct sockaddr_in6 *, struct sockaddr_in6 *, + void *, u_int32_t); +void usage(void); + +static int rcvsock; /* receive (icmp) socket file descriptor */ +static int sndsock; /* send (raw/udp) socket file descriptor */ + +static struct msghdr rcvmhdr; +static struct iovec rcviov[2]; +static int rcvhlim; +static struct in6_pktinfo *rcvpktinfo; + +static struct sockaddr_in6 Src, Dst, Rcv; +static u_long datalen = 20; /* How much data */ +#define ICMP6ECHOLEN 8 +/* XXX: 2064 = 127(max hops in type 0 rthdr) * sizeof(ip6_hdr) + 16(margin) */ +static char rtbuf[2064]; +static struct ip6_rthdr *rth; +static struct cmsghdr *cmsg; + +static char *source = NULL; +static char *hostname; + +static cap_channel_t *capdns; + +static u_long nprobes = 3; +static u_long first_hop = 1; +static u_long max_hops = 30; +static u_int16_t srcport; +static u_int16_t port = 32768 + 666; /* start udp dest port # for probe packets */ +static u_int16_t ident; +static int tclass = -1; +static int options; /* socket options */ +static int verbose; +static int waittime = 5; /* time to wait for response (in seconds) */ +static int nflag; /* print addresses numerically */ +static int useproto = IPPROTO_UDP; /* protocol to use to send packet */ +static int as_path; /* print as numbers for each hop */ +static int ecnflag; /* ECN bleaching detection flag */ +static char *as_server = NULL; +static void *asn; + +int +main(int argc, char *argv[]) +{ + int mib[4] = { CTL_NET, PF_INET6, IPPROTO_IPV6, IPV6CTL_DEFHLIM }; + char hbuf[NI_MAXHOST], src0[NI_MAXHOST], *ep; + int ch, i, on = 1, seq, rcvcmsglen, error; + struct addrinfo hints, *res; + static u_char *rcvcmsgbuf; + u_long probe, hops, lport, ltclass; + struct hostent *hp; + size_t size, minlen; + uid_t uid; + u_char type, code, ecn; +#if defined(IPSEC) && defined(IPSEC_POLICY_IPSEC) + char ipsec_inpolicy[] = "in bypass"; + char ipsec_outpolicy[] = "out bypass"; +#endif + cap_rights_t rights; + + capdns_open(); + + /* + * Receive ICMP + */ + if ((rcvsock = socket(AF_INET6, SOCK_RAW, IPPROTO_ICMPV6)) < 0) { + perror("socket(ICMPv6)"); + exit(5); + } + + size = sizeof(i); + (void) sysctl(mib, sizeof(mib) / sizeof(mib[0]), &i, &size, NULL, 0); + max_hops = i; + + /* specify to tell receiving interface */ +#ifdef IPV6_RECVPKTINFO + if (setsockopt(rcvsock, IPPROTO_IPV6, IPV6_RECVPKTINFO, &on, + sizeof(on)) < 0) + err(1, "setsockopt(IPV6_RECVPKTINFO)"); +#else /* old adv. API */ + if (setsockopt(rcvsock, IPPROTO_IPV6, IPV6_PKTINFO, &on, + sizeof(on)) < 0) + err(1, "setsockopt(IPV6_PKTINFO)"); +#endif + + /* specify to tell value of hoplimit field of received IP6 hdr */ +#ifdef IPV6_RECVHOPLIMIT + if (setsockopt(rcvsock, IPPROTO_IPV6, IPV6_RECVHOPLIMIT, &on, + sizeof(on)) < 0) + err(1, "setsockopt(IPV6_RECVHOPLIMIT)"); +#else /* old adv. API */ + if (setsockopt(rcvsock, IPPROTO_IPV6, IPV6_HOPLIMIT, &on, + sizeof(on)) < 0) + err(1, "setsockopt(IPV6_HOPLIMIT)"); +#endif + + seq = 0; + ident = htons(getpid() & 0xffff); /* same as ping6 */ + + while ((ch = getopt(argc, argv, "aA:dEf:g:Ilm:nNp:q:rs:St:TUvw:")) != -1) + switch (ch) { + case 'a': + as_path = 1; + break; + case 'A': + as_path = 1; + as_server = optarg; + break; + case 'd': + options |= SO_DEBUG; + break; + case 'E': + ecnflag = 1; + break; + case 'f': + ep = NULL; + errno = 0; + first_hop = strtoul(optarg, &ep, 0); + if (errno || !*optarg || *ep || first_hop > 255) { + fprintf(stderr, + "traceroute6: invalid min hoplimit.\n"); + exit(1); + } + break; + case 'g': + /* XXX use after capability mode is entered */ + hp = getipnodebyname(optarg, AF_INET6, 0, &h_errno); + if (hp == NULL) { + fprintf(stderr, + "traceroute6: unknown host %s\n", optarg); + exit(1); + } + if (rth == NULL) { + /* + * XXX: We can't detect the number of + * intermediate nodes yet. + */ + if ((rth = inet6_rth_init((void *)rtbuf, + sizeof(rtbuf), IPV6_RTHDR_TYPE_0, + 0)) == NULL) { + fprintf(stderr, + "inet6_rth_init failed.\n"); + exit(1); + } + } + if (inet6_rth_add((void *)rth, + (struct in6_addr *)hp->h_addr)) { + fprintf(stderr, + "inet6_rth_add failed for %s\n", + optarg); + exit(1); + } + freehostent(hp); + break; + case 'I': + useproto = IPPROTO_ICMPV6; + break; + case 'l': + break; + case 'm': + ep = NULL; + errno = 0; + max_hops = strtoul(optarg, &ep, 0); + if (errno || !*optarg || *ep || max_hops > 255) { + fprintf(stderr, + "traceroute6: invalid max hoplimit.\n"); + exit(1); + } + break; + case 'n': + nflag++; + break; + case 'N': + useproto = IPPROTO_NONE; + break; + case 'p': + ep = NULL; + errno = 0; + lport = strtoul(optarg, &ep, 0); + if (errno || !*optarg || *ep) { + fprintf(stderr, "traceroute6: invalid port.\n"); + exit(1); + } + if (lport == 0 || lport != (lport & 0xffff)) { + fprintf(stderr, + "traceroute6: port out of range.\n"); + exit(1); + } + port = lport & 0xffff; + break; + case 'q': + ep = NULL; + errno = 0; + nprobes = strtoul(optarg, &ep, 0); + if (errno || !*optarg || *ep) { + fprintf(stderr, + "traceroute6: invalid nprobes.\n"); + exit(1); + } + if (nprobes < 1) { + fprintf(stderr, + "traceroute6: nprobes must be >0.\n"); + exit(1); + } + break; + case 'r': + options |= SO_DONTROUTE; + break; + case 's': + /* + * set the ip source address of the outbound + * probe (e.g., on a multi-homed host). + */ + source = optarg; + break; + case 'S': + useproto = IPPROTO_SCTP; + break; + case 't': + ep = NULL; + errno = 0; + ltclass = strtoul(optarg, &ep, 0); + if (errno || !*optarg || *ep || ltclass > 255) { + fprintf(stderr, + "traceroute6: invalid traffic class.\n"); + exit(1); + } + tclass = (int)ltclass; + break; + case 'T': + useproto = IPPROTO_TCP; + break; + case 'U': + useproto = IPPROTO_UDP; + break; + case 'v': + verbose++; + break; + case 'w': + ep = NULL; + errno = 0; + waittime = strtoul(optarg, &ep, 0); + if (errno || !*optarg || *ep) { + fprintf(stderr, + "traceroute6: invalid wait time.\n"); + exit(1); + } + if (waittime < 1) { + fprintf(stderr, + "traceroute6: wait must be >= 1 sec.\n"); + exit(1); + } + break; + default: + usage(); + } + argc -= optind; + argv += optind; + + /* + * Open socket to send probe packets. + */ + switch (useproto) { + case IPPROTO_ICMPV6: + case IPPROTO_NONE: + case IPPROTO_SCTP: + case IPPROTO_TCP: + case IPPROTO_UDP: + if ((sndsock = socket(AF_INET6, SOCK_RAW, useproto)) < 0) { + perror("socket(SOCK_RAW)"); + exit(5); + } + break; + default: + fprintf(stderr, "traceroute6: unknown probe protocol %d\n", + useproto); + exit(5); + } + if (max_hops < first_hop) { + fprintf(stderr, + "traceroute6: max hoplimit must be larger than first hoplimit.\n"); + exit(1); + } + + if (ecnflag) { + if (tclass != -1) { + tclass &= ~IPTOS_ECN_MASK; + } else { + tclass = 0; + } + tclass |= IPTOS_ECN_ECT1; + } + + /* revoke privs */ + uid = getuid(); + if (setresuid(uid, uid, uid) == -1) { + perror("setresuid"); + exit(1); + } + + if (tclass != -1) { + if (setsockopt(sndsock, IPPROTO_IPV6, IPV6_TCLASS, &tclass, + sizeof(int)) == -1) { + perror("setsockopt(IPV6_TCLASS)"); + exit(7); + } + } + + if (argc < 1 || argc > 2) + usage(); + +#if 1 + setvbuf(stdout, NULL, _IOLBF, BUFSIZ); +#else + setlinebuf(stdout); +#endif + + memset(&hints, 0, sizeof(hints)); + hints.ai_family = PF_INET6; + hints.ai_socktype = SOCK_RAW; + hints.ai_protocol = IPPROTO_ICMPV6; + hints.ai_flags = AI_CANONNAME; + + error = cap_getaddrinfo(capdns, *argv, NULL, &hints, &res); + + if (error) { + fprintf(stderr, + "traceroute6: %s\n", gai_strerror(error)); + exit(1); + } + if (res->ai_addrlen != sizeof(Dst)) { + fprintf(stderr, + "traceroute6: size of sockaddr mismatch\n"); + exit(1); + } + memcpy(&Dst, res->ai_addr, res->ai_addrlen); + hostname = res->ai_canonname ? strdup(res->ai_canonname) : *argv; + if (!hostname) { + fprintf(stderr, "traceroute6: not enough core\n"); + exit(1); + } + if (res->ai_next) { + if (cap_getnameinfo(capdns, res->ai_addr, res->ai_addrlen, hbuf, + sizeof(hbuf), NULL, 0, NI_NUMERICHOST) != 0) + strlcpy(hbuf, "?", sizeof(hbuf)); + fprintf(stderr, "traceroute6: Warning: %s has multiple " + "addresses; using %s\n", hostname, hbuf); + } + freeaddrinfo(res); + if (*++argv) { + ep = NULL; + errno = 0; + datalen = strtoul(*argv, &ep, 0); + if (errno || *ep) { + fprintf(stderr, + "traceroute6: invalid packet length.\n"); + exit(1); + } + } + switch (useproto) { + case IPPROTO_ICMPV6: + minlen = ICMP6ECHOLEN; + break; + case IPPROTO_UDP: + minlen = sizeof(struct udphdr); + break; + case IPPROTO_NONE: + minlen = 0; + datalen = 0; + break; + case IPPROTO_SCTP: + minlen = sizeof(struct sctphdr); + break; + case IPPROTO_TCP: + minlen = sizeof(struct tcphdr); + break; + default: + fprintf(stderr, "traceroute6: unknown probe protocol %d.\n", + useproto); + exit(1); + } + if (datalen < minlen) + datalen = minlen; + else if (datalen >= MAXPACKET) { + fprintf(stderr, + "traceroute6: packet size must be %zu <= s < %d.\n", + minlen, MAXPACKET); + exit(1); + } + if ((useproto == IPPROTO_SCTP) && (datalen & 3)) { + fprintf(stderr, + "traceroute6: packet size must be a multiple of 4.\n"); + exit(1); + } + outpacket = malloc(datalen); + if (!outpacket) { + perror("malloc"); + exit(1); + } + (void) bzero((char *)outpacket, datalen); + + /* initialize msghdr for receiving packets */ + rcviov[0].iov_base = (caddr_t)packet; + rcviov[0].iov_len = sizeof(packet); + rcvmhdr.msg_name = (caddr_t)&Rcv; + rcvmhdr.msg_namelen = sizeof(Rcv); + rcvmhdr.msg_iov = rcviov; + rcvmhdr.msg_iovlen = 1; + rcvcmsglen = CMSG_SPACE(sizeof(struct in6_pktinfo)) + + CMSG_SPACE(sizeof(int)); + if ((rcvcmsgbuf = malloc(rcvcmsglen)) == NULL) { + fprintf(stderr, "traceroute6: malloc failed\n"); + exit(1); + } + rcvmhdr.msg_control = (caddr_t) rcvcmsgbuf; + rcvmhdr.msg_controllen = rcvcmsglen; + + if (options & SO_DEBUG) + (void) setsockopt(rcvsock, SOL_SOCKET, SO_DEBUG, + (char *)&on, sizeof(on)); + if (options & SO_DONTROUTE) + (void) setsockopt(rcvsock, SOL_SOCKET, SO_DONTROUTE, + (char *)&on, sizeof(on)); +#if defined(IPSEC) && defined(IPSEC_POLICY_IPSEC) + /* + * do not raise error even if setsockopt fails, kernel may have ipsec + * turned off. + */ + if (setpolicy(rcvsock, ipsec_inpolicy) < 0) + errx(1, "%s", ipsec_strerror()); + if (setpolicy(rcvsock, ipsec_outpolicy) < 0) + errx(1, "%s", ipsec_strerror()); +#else + { + int level = IPSEC_LEVEL_NONE; + + (void)setsockopt(rcvsock, IPPROTO_IPV6, IPV6_ESP_TRANS_LEVEL, &level, + sizeof(level)); + (void)setsockopt(rcvsock, IPPROTO_IPV6, IPV6_ESP_NETWORK_LEVEL, &level, + sizeof(level)); +#ifdef IP_AUTH_TRANS_LEVEL + (void)setsockopt(rcvsock, IPPROTO_IPV6, IPV6_AUTH_TRANS_LEVEL, &level, + sizeof(level)); +#else + (void)setsockopt(rcvsock, IPPROTO_IPV6, IPV6_AUTH_LEVEL, &level, + sizeof(level)); +#endif +#ifdef IP_AUTH_NETWORK_LEVEL + (void)setsockopt(rcvsock, IPPROTO_IPV6, IPV6_AUTH_NETWORK_LEVEL, &level, + sizeof(level)); +#endif + } +#endif /* !(IPSEC && IPSEC_POLICY_IPSEC) */ + +#ifdef SO_SNDBUF + i = datalen; + if (i == 0) + i = 1; + if (setsockopt(sndsock, SOL_SOCKET, SO_SNDBUF, (char *)&i, + sizeof(i)) < 0) { + perror("setsockopt(SO_SNDBUF)"); + exit(6); + } +#endif /* SO_SNDBUF */ + if (options & SO_DEBUG) + (void) setsockopt(sndsock, SOL_SOCKET, SO_DEBUG, + (char *)&on, sizeof(on)); + if (options & SO_DONTROUTE) + (void) setsockopt(sndsock, SOL_SOCKET, SO_DONTROUTE, + (char *)&on, sizeof(on)); + if (rth) {/* XXX: there is no library to finalize the header... */ + rth->ip6r_len = rth->ip6r_segleft * 2; + if (setsockopt(sndsock, IPPROTO_IPV6, IPV6_RTHDR, + (void *)rth, (rth->ip6r_len + 1) << 3)) { + fprintf(stderr, "setsockopt(IPV6_RTHDR): %s\n", + strerror(errno)); + exit(1); + } + } +#if defined(IPSEC) && defined(IPSEC_POLICY_IPSEC) + /* + * do not raise error even if setsockopt fails, kernel may have ipsec + * turned off. + */ + if (setpolicy(sndsock, ipsec_inpolicy) < 0) + errx(1, "%s", ipsec_strerror()); + if (setpolicy(sndsock, ipsec_outpolicy) < 0) + errx(1, "%s", ipsec_strerror()); +#else + { + int level = IPSEC_LEVEL_BYPASS; + + (void)setsockopt(sndsock, IPPROTO_IPV6, IPV6_ESP_TRANS_LEVEL, &level, + sizeof(level)); + (void)setsockopt(sndsock, IPPROTO_IPV6, IPV6_ESP_NETWORK_LEVEL, &level, + sizeof(level)); +#ifdef IP_AUTH_TRANS_LEVEL + (void)setsockopt(sndsock, IPPROTO_IPV6, IPV6_AUTH_TRANS_LEVEL, &level, + sizeof(level)); +#else + (void)setsockopt(sndsock, IPPROTO_IPV6, IPV6_AUTH_LEVEL, &level, + sizeof(level)); +#endif +#ifdef IP_AUTH_NETWORK_LEVEL + (void)setsockopt(sndsock, IPPROTO_IPV6, IPV6_AUTH_NETWORK_LEVEL, &level, + sizeof(level)); +#endif + } +#endif /* !(IPSEC && IPSEC_POLICY_IPSEC) */ + + /* + * Source selection + */ + bzero(&Src, sizeof(Src)); + if (source) { + memset(&hints, 0, sizeof(hints)); + hints.ai_family = AF_INET6; + hints.ai_socktype = SOCK_DGRAM; /*dummy*/ + hints.ai_flags = AI_NUMERICHOST; + error = cap_getaddrinfo(capdns, source, "0", &hints, &res); + if (error) { + printf("traceroute6: %s: %s\n", source, + gai_strerror(error)); + exit(1); + } + if (res->ai_addrlen > sizeof(Src)) { + printf("traceroute6: %s: %s\n", source, + gai_strerror(error)); + exit(1); + } + memcpy(&Src, res->ai_addr, res->ai_addrlen); + freeaddrinfo(res); + } else { + struct sockaddr_in6 Nxt; + int dummy; + socklen_t len; + + Nxt = Dst; + Nxt.sin6_port = htons(DUMMY_PORT); + if (cmsg != NULL) + bcopy(inet6_rthdr_getaddr(cmsg, 1), &Nxt.sin6_addr, + sizeof(Nxt.sin6_addr)); + if ((dummy = socket(AF_INET6, SOCK_DGRAM, 0)) < 0) { + perror("socket"); + exit(1); + } + if (connect(dummy, (struct sockaddr *)&Nxt, Nxt.sin6_len) < 0) { + perror("connect"); + exit(1); + } + len = sizeof(Src); + if (getsockname(dummy, (struct sockaddr *)&Src, &len) < 0) { + perror("getsockname"); + exit(1); + } + if (cap_getnameinfo(capdns, (struct sockaddr *)&Src, Src.sin6_len, + src0, sizeof(src0), NULL, 0, NI_NUMERICHOST)) { + fprintf(stderr, "getnameinfo failed for source\n"); + exit(1); + } + source = src0; + close(dummy); + } + + Src.sin6_port = htons(0); + if (bind(sndsock, (struct sockaddr *)&Src, Src.sin6_len) < 0) { + perror("bind"); + exit(1); + } + + { + socklen_t len; + + len = sizeof(Src); + if (getsockname(sndsock, (struct sockaddr *)&Src, &len) < 0) { + perror("getsockname"); + exit(1); + } + srcport = ntohs(Src.sin6_port); + } + + if (as_path) { + asn = as_setup(as_server); + if (asn == NULL) { + fprintf(stderr, + "traceroute6: as_setup failed, AS# lookups" + " disabled\n"); + (void)fflush(stderr); + as_path = 0; + } + } + + /* + * Message to users + */ + if (cap_getnameinfo(capdns, (struct sockaddr *)&Dst, Dst.sin6_len, hbuf, + sizeof(hbuf), NULL, 0, NI_NUMERICHOST)) + strlcpy(hbuf, "(invalid)", sizeof(hbuf)); + fprintf(stderr, "traceroute6"); + fprintf(stderr, " to %s (%s)", hostname, hbuf); + if (source) + fprintf(stderr, " from %s", source); + fprintf(stderr, ", %lu hops max, %lu byte packets\n", + max_hops, + datalen + ((useproto == IPPROTO_UDP) ? sizeof(struct udphdr) : 0)); + (void) fflush(stderr); + + if (first_hop > 1) + printf("Skipping %lu intermediate hops\n", first_hop - 1); + + if (connect(sndsock, (struct sockaddr *)&Dst, + sizeof(Dst)) != 0) { + fprintf(stderr, "connect: %s\n", strerror(errno)); + exit(1); + } + + /* + * Here we enter capability mode. Further down access to global + * namespaces (e.g filesystem) is restricted (see capsicum(4)). + * We must connect(2) our socket before this point. + */ + if (caph_enter_casper() < 0) { + fprintf(stderr, "caph_enter_casper: %s\n", strerror(errno)); + exit(1); + } + + cap_rights_init(&rights, CAP_SEND, CAP_SETSOCKOPT); + if (caph_rights_limit(sndsock, &rights) < 0) { + fprintf(stderr, "caph_rights_limit sndsock: %s\n", + strerror(errno)); + exit(1); + } + cap_rights_init(&rights, CAP_RECV, CAP_EVENT); + if (caph_rights_limit(rcvsock, &rights) < 0) { + fprintf(stderr, "caph_rights_limit rcvsock: %s\n", + strerror(errno)); + exit(1); + } + + /* + * Main loop + */ + for (hops = first_hop; hops <= max_hops; ++hops) { + struct in6_addr lastaddr; + int got_there = 0; + unsigned unreachable = 0; + + printf("%2lu ", hops); + bzero(&lastaddr, sizeof(lastaddr)); + for (probe = 0; probe < nprobes; ++probe) { + int cc; + struct timeval t1, t2; + + (void) gettimeofday(&t1, NULL); + send_probe(++seq, hops); + while ((cc = wait_for_reply(rcvsock, &rcvmhdr))) { + (void) gettimeofday(&t2, NULL); + if (packet_ok(&rcvmhdr, cc, seq, &type, &code, &ecn)) { + if (!IN6_ARE_ADDR_EQUAL(&Rcv.sin6_addr, + &lastaddr)) { + if (probe > 0) + fputs("\n ", stdout); + print(&rcvmhdr, cc); + lastaddr = Rcv.sin6_addr; + } + printf(" %.3f ms", deltaT(&t1, &t2)); + if (ecnflag) { + switch (ecn) { + case IPTOS_ECN_ECT1: + printf(" (ecn=passed)"); + break; + case IPTOS_ECN_NOTECT: + printf(" (ecn=bleached)"); + break; + case IPTOS_ECN_CE: + printf(" (ecn=congested)"); + break; + default: + printf(" (ecn=mangled)"); + break; + } + } + if (type == ICMP6_DST_UNREACH) { + switch (code) { + case ICMP6_DST_UNREACH_NOROUTE: + ++unreachable; + printf(" !N"); + break; + case ICMP6_DST_UNREACH_ADMIN: + ++unreachable; + printf(" !P"); + break; + case ICMP6_DST_UNREACH_NOTNEIGHBOR: + ++unreachable; + printf(" !S"); + break; + case ICMP6_DST_UNREACH_ADDR: + ++unreachable; + printf(" !A"); + break; + case ICMP6_DST_UNREACH_NOPORT: + if (rcvhlim >= 0 && + rcvhlim <= 1) + printf(" !"); + ++got_there; + break; + default: + ++unreachable; + printf(" !<%d>", code & 0xff); + break; + } + } else if (type == ICMP6_PARAM_PROB && + code == ICMP6_PARAMPROB_NEXTHEADER) { + printf(" !H"); + ++got_there; + } else if (type == ICMP6_ECHO_REPLY) { + if (rcvhlim >= 0 && + rcvhlim <= 1) + printf(" !"); + ++got_there; + } + break; + } else if (deltaT(&t1, &t2) > waittime * 1000) { + cc = 0; + break; + } + } + if (cc == 0) + printf(" *"); + (void) fflush(stdout); + } + putchar('\n'); + if (got_there || + (unreachable > 0 && unreachable >= ((nprobes + 1) / 2))) { + exit(0); + } + } + if (as_path) + as_shutdown(asn); + + exit(0); +} + +int +wait_for_reply(int sock, struct msghdr *mhdr) +{ +#ifdef HAVE_POLL + struct pollfd pfd[1]; + int cc = 0; + + pfd[0].fd = sock; + pfd[0].events = POLLIN; + pfd[0].revents = 0; + + if (poll(pfd, 1, waittime * 1000) > 0 && + pfd[0].revents & POLLIN) + cc = recvmsg(rcvsock, mhdr, 0); + + return (cc); +#else + fd_set *fdsp; + struct timeval wait; + int cc = 0, fdsn; + + fdsn = howmany(sock + 1, NFDBITS) * sizeof(fd_mask); + if ((fdsp = (fd_set *)malloc(fdsn)) == NULL) + err(1, "malloc"); + memset(fdsp, 0, fdsn); + FD_SET(sock, fdsp); + wait.tv_sec = waittime; wait.tv_usec = 0; + + if (select(sock + 1, fdsp, (fd_set *)0, (fd_set *)0, &wait) > 0) + cc = recvmsg(rcvsock, mhdr, 0); + + free(fdsp); + return (cc); +#endif +} + +#if defined(IPSEC) && defined(IPSEC_POLICY_IPSEC) +int +setpolicy(int so, char *policy) +{ + char *buf; + + buf = ipsec_set_policy(policy, strlen(policy)); + if (buf == NULL) { + warnx("%s", ipsec_strerror()); + return (-1); + } + (void)setsockopt(so, IPPROTO_IPV6, IPV6_IPSEC_POLICY, + buf, ipsec_get_policylen(buf)); + + free(buf); + + return (0); +} +#endif + +void +send_probe(int seq, u_long hops) +{ + struct icmp6_hdr *icp; + struct sctphdr *sctp; + struct udphdr *outudp; + struct sctp_chunkhdr *chk; + struct sctp_init_chunk *init; + struct sctp_paramhdr *param; + struct tcphdr *tcp; + int i; + + i = hops; + if (setsockopt(sndsock, IPPROTO_IPV6, IPV6_UNICAST_HOPS, + (char *)&i, sizeof(i)) < 0) { + perror("setsockopt IPV6_UNICAST_HOPS"); + } + + Dst.sin6_port = htons(port + seq); + + switch (useproto) { + case IPPROTO_ICMPV6: + icp = (struct icmp6_hdr *)outpacket; + + icp->icmp6_type = ICMP6_ECHO_REQUEST; + icp->icmp6_code = 0; + icp->icmp6_cksum = 0; + icp->icmp6_id = ident; + icp->icmp6_seq = htons(seq); + break; + case IPPROTO_UDP: + outudp = (struct udphdr *) outpacket; + outudp->uh_sport = htons(ident); + outudp->uh_dport = htons(port + seq); + outudp->uh_ulen = htons(datalen); + outudp->uh_sum = 0; + outudp->uh_sum = udp_cksum(&Src, &Dst, outpacket, datalen); + break; + case IPPROTO_NONE: + /* No space for anything. No harm as seq/tv32 are decorative. */ + break; + case IPPROTO_SCTP: + sctp = (struct sctphdr *)outpacket; + + sctp->src_port = htons(ident); + sctp->dest_port = htons(port + seq); + if (datalen >= (u_long)(sizeof(struct sctphdr) + + sizeof(struct sctp_init_chunk))) { + sctp->v_tag = 0; + } else { + sctp->v_tag = (sctp->src_port << 16) | sctp->dest_port; + } + sctp->checksum = htonl(0); + if (datalen >= (u_long)(sizeof(struct sctphdr) + + sizeof(struct sctp_init_chunk))) { + /* + * Send a packet containing an INIT chunk. This works + * better in case of firewalls on the path, but + * results in a probe packet containing at least + * 32 bytes of payload. For shorter payloads, use + * SHUTDOWN-ACK chunks. + */ + init = (struct sctp_init_chunk *)(sctp + 1); + init->ch.chunk_type = SCTP_INITIATION; + init->ch.chunk_flags = 0; + init->ch.chunk_length = htons((u_int16_t)(datalen - + sizeof(struct sctphdr))); + init->init.initiate_tag = (sctp->src_port << 16) | + sctp->dest_port; + init->init.a_rwnd = htonl(1500); + init->init.num_outbound_streams = htons(1); + init->init.num_inbound_streams = htons(1); + init->init.initial_tsn = htonl(0); + if (datalen >= (u_long)(sizeof(struct sctphdr) + + sizeof(struct sctp_init_chunk) + + sizeof(struct sctp_paramhdr))) { + param = (struct sctp_paramhdr *)(init + 1); + param->param_type = htons(SCTP_PAD); + param->param_length = + htons((u_int16_t)(datalen - + sizeof(struct sctphdr) - + sizeof(struct sctp_init_chunk))); + } + } else { + /* + * Send a packet containing a SHUTDOWN-ACK chunk, + * possibly followed by a PAD chunk. + */ + if (datalen >= (u_long)(sizeof(struct sctphdr) + + sizeof(struct sctp_chunkhdr))) { + chk = (struct sctp_chunkhdr *)(sctp + 1); + chk->chunk_type = SCTP_SHUTDOWN_ACK; + chk->chunk_flags = 0; + chk->chunk_length = htons(4); + } + if (datalen >= (u_long)(sizeof(struct sctphdr) + + 2 * sizeof(struct sctp_chunkhdr))) { + chk = chk + 1; + chk->chunk_type = SCTP_PAD_CHUNK; + chk->chunk_flags = 0; + chk->chunk_length = htons((u_int16_t)(datalen - + sizeof(struct sctphdr) - + sizeof(struct sctp_chunkhdr))); + } + } + sctp->checksum = sctp_crc32c(outpacket, datalen); + break; + case IPPROTO_TCP: + tcp = (struct tcphdr *)outpacket; + + tcp->th_sport = htons(ident); + tcp->th_dport = htons(port + seq); + tcp->th_seq = (tcp->th_sport << 16) | tcp->th_dport; + tcp->th_ack = 0; + tcp->th_off = 5; + __tcp_set_flags(tcp, TH_SYN); + tcp->th_sum = 0; + tcp->th_sum = tcp_chksum(&Src, &Dst, outpacket, datalen); + break; + default: + fprintf(stderr, "Unknown probe protocol %d.\n", useproto); + exit(1); + } + + i = send(sndsock, (char *)outpacket, datalen, 0); + if (i < 0 || (u_long)i != datalen) { + if (i < 0) + perror("send"); + printf("traceroute6: wrote %s %lu chars, ret=%d\n", + hostname, datalen, i); + (void) fflush(stdout); + } +} + +int +get_hoplim(struct msghdr *mhdr) +{ + struct cmsghdr *cm; + + for (cm = (struct cmsghdr *)CMSG_FIRSTHDR(mhdr); cm; + cm = (struct cmsghdr *)CMSG_NXTHDR(mhdr, cm)) { + if (cm->cmsg_level == IPPROTO_IPV6 && + cm->cmsg_type == IPV6_HOPLIMIT && + cm->cmsg_len == CMSG_LEN(sizeof(int))) + return (*(int *)CMSG_DATA(cm)); + } + + return (-1); +} + +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); +} + +/* + * Convert an ICMP "type" field to a printable string. + */ +const char * +pr_type(int t0) +{ + u_char t = t0 & 0xff; + const char *cp; + + switch (t) { + case ICMP6_DST_UNREACH: + cp = "Destination Unreachable"; + break; + case ICMP6_PACKET_TOO_BIG: + cp = "Packet Too Big"; + break; + case ICMP6_TIME_EXCEEDED: + cp = "Time Exceeded"; + break; + case ICMP6_PARAM_PROB: + cp = "Parameter Problem"; + break; + case ICMP6_ECHO_REQUEST: + cp = "Echo Request"; + break; + case ICMP6_ECHO_REPLY: + cp = "Echo Reply"; + break; + case ICMP6_MEMBERSHIP_QUERY: + cp = "Group Membership Query"; + break; + case ICMP6_MEMBERSHIP_REPORT: + cp = "Group Membership Report"; + break; + case ICMP6_MEMBERSHIP_REDUCTION: + cp = "Group Membership Reduction"; + break; + case ND_ROUTER_SOLICIT: + cp = "Router Solicitation"; + break; + case ND_ROUTER_ADVERT: + cp = "Router Advertisement"; + break; + case ND_NEIGHBOR_SOLICIT: + cp = "Neighbor Solicitation"; + break; + case ND_NEIGHBOR_ADVERT: + cp = "Neighbor Advertisement"; + break; + case ND_REDIRECT: + cp = "Redirect"; + break; + default: + cp = "Unknown"; + break; + } + return (cp); +} + +int +packet_ok(struct msghdr *mhdr, int cc, int seq, u_char *type, u_char *code, + u_char *ecn) +{ + struct icmp6_hdr *icp; + struct sockaddr_in6 *from = (struct sockaddr_in6 *)mhdr->msg_name; + char *buf = (char *)mhdr->msg_iov[0].iov_base; + struct cmsghdr *cm; + int *hlimp; + char hbuf[NI_MAXHOST]; + +#ifdef OLDRAWSOCKET + int hlen; + struct ip6_hdr *ip; +#endif + +#ifdef OLDRAWSOCKET + ip = (struct ip6_hdr *) buf; + hlen = sizeof(struct ip6_hdr); + if (cc < hlen + sizeof(struct icmp6_hdr)) { + if (verbose) { + if (cap_getnameinfo(capdns, (struct sockaddr *)from, from->sin6_len, + hbuf, sizeof(hbuf), NULL, 0, NI_NUMERICHOST) != 0) + strlcpy(hbuf, "invalid", sizeof(hbuf)); + printf("packet too short (%d bytes) from %s\n", cc, + hbuf); + } + return (0); + } + cc -= hlen; + icp = (struct icmp6_hdr *)(buf + hlen); +#else + if (cc < (int)sizeof(struct icmp6_hdr)) { + if (verbose) { + if (cap_getnameinfo(capdns, (struct sockaddr *)from, from->sin6_len, + hbuf, sizeof(hbuf), NULL, 0, NI_NUMERICHOST) != 0) + strlcpy(hbuf, "invalid", sizeof(hbuf)); + printf("data too short (%d bytes) from %s\n", cc, hbuf); + } + return (0); + } + icp = (struct icmp6_hdr *)buf; +#endif + /* get optional information via advanced API */ + rcvpktinfo = NULL; + hlimp = NULL; + for (cm = (struct cmsghdr *)CMSG_FIRSTHDR(mhdr); cm; + cm = (struct cmsghdr *)CMSG_NXTHDR(mhdr, cm)) { + if (cm->cmsg_level == IPPROTO_IPV6 && + cm->cmsg_type == IPV6_PKTINFO && + cm->cmsg_len == + CMSG_LEN(sizeof(struct in6_pktinfo))) + rcvpktinfo = (struct in6_pktinfo *)(CMSG_DATA(cm)); + + if (cm->cmsg_level == IPPROTO_IPV6 && + cm->cmsg_type == IPV6_HOPLIMIT && + cm->cmsg_len == CMSG_LEN(sizeof(int))) + hlimp = (int *)CMSG_DATA(cm); + } + if (rcvpktinfo == NULL || hlimp == NULL) { + warnx("failed to get received hop limit or packet info"); +#if 0 + return (0); +#else + rcvhlim = 0; /*XXX*/ +#endif + } else + rcvhlim = *hlimp; + + *type = icp->icmp6_type; + *code = icp->icmp6_code; + if ((*type == ICMP6_TIME_EXCEEDED && + *code == ICMP6_TIME_EXCEED_TRANSIT) || + (*type == ICMP6_DST_UNREACH) || + (*type == ICMP6_PARAM_PROB && + *code == ICMP6_PARAMPROB_NEXTHEADER)) { + struct ip6_hdr *hip; + struct icmp6_hdr *icmp; + struct sctp_init_chunk *init; + struct sctphdr *sctp; + struct tcphdr *tcp; + struct udphdr *udp; + void *up; + + hip = (struct ip6_hdr *)(icp + 1); + *ecn = ntohl(hip->ip6_flow & IPV6_ECN_MASK) >> 20; + if ((up = get_uphdr(hip, (u_char *)(buf + cc))) == NULL) { + if (verbose) + warnx("failed to get upper layer header"); + return (0); + } + switch (useproto) { + case IPPROTO_ICMPV6: + icmp = (struct icmp6_hdr *)up; + if (icmp->icmp6_id == ident && + icmp->icmp6_seq == htons(seq)) + return (1); + break; + case IPPROTO_UDP: + udp = (struct udphdr *)up; + if (udp->uh_sport == htons(ident) && + udp->uh_dport == htons(port + seq)) + return (1); + break; + case IPPROTO_SCTP: + sctp = (struct sctphdr *)up; + if (sctp->src_port != htons(ident) || + sctp->dest_port != htons(port + seq)) { + break; + } + if (datalen >= (u_long)(sizeof(struct sctphdr) + + sizeof(struct sctp_init_chunk))) { + if (sctp->v_tag != 0) { + break; + } + init = (struct sctp_init_chunk *)(sctp + 1); + /* Check the initiate tag, if available. */ + if ((char *)&init->init.a_rwnd > buf + cc) { + return (1); + } + if (init->init.initiate_tag == (u_int32_t) + ((sctp->src_port << 16) | sctp->dest_port)) { + return (1); + } + } else { + if (sctp->v_tag == + (u_int32_t)((sctp->src_port << 16) | + sctp->dest_port)) { + return (1); + } + } + break; + case IPPROTO_TCP: + tcp = (struct tcphdr *)up; + if (tcp->th_sport == htons(ident) && + tcp->th_dport == htons(port + seq) && + tcp->th_seq == + (tcp_seq)((tcp->th_sport << 16) | tcp->th_dport)) + return (1); + break; + case IPPROTO_NONE: + return (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 (1); + } + if (verbose) { + char sbuf[NI_MAXHOST + 1], dbuf[INET6_ADDRSTRLEN]; + u_int8_t *p; + int i; + + if (cap_getnameinfo(capdns, (struct sockaddr *)from, from->sin6_len, + sbuf, sizeof(sbuf), NULL, 0, NI_NUMERICHOST) != 0) + strlcpy(sbuf, "invalid", sizeof(sbuf)); + printf("\n%d bytes from %s to %s", cc, sbuf, + rcvpktinfo ? inet_ntop(AF_INET6, &rcvpktinfo->ipi6_addr, + dbuf, sizeof(dbuf)) : "?"); + printf(": icmp type %d (%s) code %d\n", *type, pr_type(*type), + *code); + p = (u_int8_t *)(icp + 1); +#define WIDTH 16 + for (i = 0; i < cc; i++) { + if (i % WIDTH == 0) + printf("%04x:", i); + if (i % 4 == 0) + printf(" "); + printf("%02x", p[i]); + if (i % WIDTH == WIDTH - 1) + printf("\n"); + } + if (cc % WIDTH != 0) + printf("\n"); + } + return (0); +} + +/* + * Increment pointer until find the UDP or ICMP header. + */ +void * +get_uphdr(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) + return (NULL); + + nh = ip6->ip6_nxt; + cp += sizeof(struct ip6_hdr); + + while (lim - cp >= (nh == IPPROTO_NONE ? 0 : 8)) { + switch (nh) { + case IPPROTO_ESP: + return (NULL); + case IPPROTO_ICMPV6: + return (useproto == nh ? cp : NULL); + case IPPROTO_SCTP: + case IPPROTO_TCP: + case IPPROTO_UDP: + 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; + break; + case IPPROTO_AH: + hlen = (((struct ip6_ext *)cp)->ip6e_len + 2) << 2; + nh = ((struct ip6_ext *)cp)->ip6e_nxt; + break; + default: + hlen = (((struct ip6_ext *)cp)->ip6e_len + 1) << 3; + nh = ((struct ip6_ext *)cp)->ip6e_nxt; + break; + } + + cp += hlen; + } + + return (NULL); +} + +void +capdns_open(void) +{ +#ifdef WITH_CASPER + const char *types[] = { "NAME", "ADDR" }; + int families[1]; + cap_channel_t *casper; + + casper = cap_init(); + if (casper == NULL) + errx(1, "unable to create casper process"); + capdns = cap_service_open(casper, "system.dns"); + if (capdns == NULL) + errx(1, "unable to open system.dns service"); + if (cap_dns_type_limit(capdns, types, nitems(types)) < 0) + errx(1, "unable to limit access to system.dns service"); + families[0] = AF_INET6; + if (cap_dns_family_limit(capdns, families, nitems(families)) < 0) + errx(1, "unable to limit access to system.dns service"); + cap_close(casper); +#endif /* WITH_CASPER */ +} + +void +print(struct msghdr *mhdr, int cc) +{ + struct sockaddr_in6 *from = (struct sockaddr_in6 *)mhdr->msg_name; + char hbuf[NI_MAXHOST]; + + if (cap_getnameinfo(capdns, (struct sockaddr *)from, from->sin6_len, + hbuf, sizeof(hbuf), NULL, 0, NI_NUMERICHOST) != 0) + strlcpy(hbuf, "invalid", sizeof(hbuf)); + if (as_path) + printf(" [AS%u]", as_lookup(asn, hbuf, AF_INET6)); + if (nflag) + printf(" %s", hbuf); + else + printf(" %s (%s)", inetname((struct sockaddr *)from), hbuf); + + if (verbose) { +#ifdef OLDRAWSOCKET + printf(" %d bytes to %s", cc, + rcvpktinfo ? inet_ntop(AF_INET6, &rcvpktinfo->ipi6_addr, + hbuf, sizeof(hbuf)) : "?"); +#else + printf(" %d bytes of data to %s", cc, + rcvpktinfo ? inet_ntop(AF_INET6, &rcvpktinfo->ipi6_addr, + hbuf, sizeof(hbuf)) : "?"); +#endif + } +} + +/* + * Construct an Internet address representation. + * If the nflag has been supplied, give + * numeric value, otherwise try for symbolic name. + */ +const char * +inetname(struct sockaddr *sa) +{ + static char line[NI_MAXHOST], domain[MAXHOSTNAMELEN + 1]; + static int first = 1; + char *cp; + + if (first && !nflag) { + first = 0; + if (gethostname(domain, sizeof(domain)) == 0 && + (cp = strchr(domain, '.'))) + (void) strlcpy(domain, cp + 1, sizeof(domain)); + else + domain[0] = 0; + } + cp = NULL; + if (!nflag) { + if (cap_getnameinfo(capdns, sa, sa->sa_len, line, sizeof(line), NULL, 0, + NI_NAMEREQD) == 0) { + if ((cp = strchr(line, '.')) && + !strcmp(cp + 1, domain)) + *cp = 0; + cp = line; + } + } + if (cp) + return (cp); + + if (cap_getnameinfo(capdns, sa, sa->sa_len, line, sizeof(line), NULL, 0, + NI_NUMERICHOST) != 0) + strlcpy(line, "invalid", sizeof(line)); + return (line); +} + +/* + * CRC32C routine for the Stream Control Transmission Protocol + */ + +#define CRC32C(c, d) (c = (c >> 8) ^ crc_c[(c ^ (d)) & 0xFF]) + +static u_int32_t crc_c[256] = { + 0x00000000, 0xF26B8303, 0xE13B70F7, 0x1350F3F4, + 0xC79A971F, 0x35F1141C, 0x26A1E7E8, 0xD4CA64EB, + 0x8AD958CF, 0x78B2DBCC, 0x6BE22838, 0x9989AB3B, + 0x4D43CFD0, 0xBF284CD3, 0xAC78BF27, 0x5E133C24, + 0x105EC76F, 0xE235446C, 0xF165B798, 0x030E349B, + 0xD7C45070, 0x25AFD373, 0x36FF2087, 0xC494A384, + 0x9A879FA0, 0x68EC1CA3, 0x7BBCEF57, 0x89D76C54, + 0x5D1D08BF, 0xAF768BBC, 0xBC267848, 0x4E4DFB4B, + 0x20BD8EDE, 0xD2D60DDD, 0xC186FE29, 0x33ED7D2A, + 0xE72719C1, 0x154C9AC2, 0x061C6936, 0xF477EA35, + 0xAA64D611, 0x580F5512, 0x4B5FA6E6, 0xB93425E5, + 0x6DFE410E, 0x9F95C20D, 0x8CC531F9, 0x7EAEB2FA, + 0x30E349B1, 0xC288CAB2, 0xD1D83946, 0x23B3BA45, + 0xF779DEAE, 0x05125DAD, 0x1642AE59, 0xE4292D5A, + 0xBA3A117E, 0x4851927D, 0x5B016189, 0xA96AE28A, + 0x7DA08661, 0x8FCB0562, 0x9C9BF696, 0x6EF07595, + 0x417B1DBC, 0xB3109EBF, 0xA0406D4B, 0x522BEE48, + 0x86E18AA3, 0x748A09A0, 0x67DAFA54, 0x95B17957, + 0xCBA24573, 0x39C9C670, 0x2A993584, 0xD8F2B687, + 0x0C38D26C, 0xFE53516F, 0xED03A29B, 0x1F682198, + 0x5125DAD3, 0xA34E59D0, 0xB01EAA24, 0x42752927, + 0x96BF4DCC, 0x64D4CECF, 0x77843D3B, 0x85EFBE38, + 0xDBFC821C, 0x2997011F, 0x3AC7F2EB, 0xC8AC71E8, + 0x1C661503, 0xEE0D9600, 0xFD5D65F4, 0x0F36E6F7, + 0x61C69362, 0x93AD1061, 0x80FDE395, 0x72966096, + 0xA65C047D, 0x5437877E, 0x4767748A, 0xB50CF789, + 0xEB1FCBAD, 0x197448AE, 0x0A24BB5A, 0xF84F3859, + 0x2C855CB2, 0xDEEEDFB1, 0xCDBE2C45, 0x3FD5AF46, + 0x7198540D, 0x83F3D70E, 0x90A324FA, 0x62C8A7F9, + 0xB602C312, 0x44694011, 0x5739B3E5, 0xA55230E6, + 0xFB410CC2, 0x092A8FC1, 0x1A7A7C35, 0xE811FF36, + 0x3CDB9BDD, 0xCEB018DE, 0xDDE0EB2A, 0x2F8B6829, + 0x82F63B78, 0x709DB87B, 0x63CD4B8F, 0x91A6C88C, + 0x456CAC67, 0xB7072F64, 0xA457DC90, 0x563C5F93, + 0x082F63B7, 0xFA44E0B4, 0xE9141340, 0x1B7F9043, + 0xCFB5F4A8, 0x3DDE77AB, 0x2E8E845F, 0xDCE5075C, + 0x92A8FC17, 0x60C37F14, 0x73938CE0, 0x81F80FE3, + 0x55326B08, 0xA759E80B, 0xB4091BFF, 0x466298FC, + 0x1871A4D8, 0xEA1A27DB, 0xF94AD42F, 0x0B21572C, + 0xDFEB33C7, 0x2D80B0C4, 0x3ED04330, 0xCCBBC033, + 0xA24BB5A6, 0x502036A5, 0x4370C551, 0xB11B4652, + 0x65D122B9, 0x97BAA1BA, 0x84EA524E, 0x7681D14D, + 0x2892ED69, 0xDAF96E6A, 0xC9A99D9E, 0x3BC21E9D, + 0xEF087A76, 0x1D63F975, 0x0E330A81, 0xFC588982, + 0xB21572C9, 0x407EF1CA, 0x532E023E, 0xA145813D, + 0x758FE5D6, 0x87E466D5, 0x94B49521, 0x66DF1622, + 0x38CC2A06, 0xCAA7A905, 0xD9F75AF1, 0x2B9CD9F2, + 0xFF56BD19, 0x0D3D3E1A, 0x1E6DCDEE, 0xEC064EED, + 0xC38D26C4, 0x31E6A5C7, 0x22B65633, 0xD0DDD530, + 0x0417B1DB, 0xF67C32D8, 0xE52CC12C, 0x1747422F, + 0x49547E0B, 0xBB3FFD08, 0xA86F0EFC, 0x5A048DFF, + 0x8ECEE914, 0x7CA56A17, 0x6FF599E3, 0x9D9E1AE0, + 0xD3D3E1AB, 0x21B862A8, 0x32E8915C, 0xC083125F, + 0x144976B4, 0xE622F5B7, 0xF5720643, 0x07198540, + 0x590AB964, 0xAB613A67, 0xB831C993, 0x4A5A4A90, + 0x9E902E7B, 0x6CFBAD78, 0x7FAB5E8C, 0x8DC0DD8F, + 0xE330A81A, 0x115B2B19, 0x020BD8ED, 0xF0605BEE, + 0x24AA3F05, 0xD6C1BC06, 0xC5914FF2, 0x37FACCF1, + 0x69E9F0D5, 0x9B8273D6, 0x88D28022, 0x7AB90321, + 0xAE7367CA, 0x5C18E4C9, 0x4F48173D, 0xBD23943E, + 0xF36E6F75, 0x0105EC76, 0x12551F82, 0xE03E9C81, + 0x34F4F86A, 0xC69F7B69, 0xD5CF889D, 0x27A40B9E, + 0x79B737BA, 0x8BDCB4B9, 0x988C474D, 0x6AE7C44E, + 0xBE2DA0A5, 0x4C4623A6, 0x5F16D052, 0xAD7D5351 +}; + +u_int32_t +sctp_crc32c(void *pack, u_int32_t len) +{ + u_int32_t i, crc32c; + u_int8_t byte0, byte1, byte2, byte3; + u_int8_t *buf = (u_int8_t *)pack; + + crc32c = ~0; + for (i = 0; i < len; i++) + CRC32C(crc32c, buf[i]); + crc32c = ~crc32c; + byte0 = crc32c & 0xff; + byte1 = (crc32c >> 8) & 0xff; + byte2 = (crc32c >> 16) & 0xff; + byte3 = (crc32c >> 24) & 0xff; + crc32c = ((byte0 << 24) | (byte1 << 16) | (byte2 << 8) | byte3); + return (htonl(crc32c)); +} + +u_int16_t +in_cksum(u_int16_t *addr, int len) +{ + int nleft = len; + u_int16_t *w = addr; + u_int16_t answer; + int sum = 0; + + /* + * Our algorithm is simple, using a 32 bit accumulator (sum), + * we add sequential 16 bit words to it, and at the end, fold + * back all the carry bits from the top 16 bits into the lower + * 16 bits. + */ + while (nleft > 1) { + sum += *w++; + nleft -= 2; + } + + /* mop up an odd byte, if necessary */ + if (nleft == 1) + sum += *(u_char *)w; + + /* + * add back carry outs from top 16 bits to low 16 bits + */ + sum = (sum >> 16) + (sum & 0xffff); /* add hi 16 to low 16 */ + sum += (sum >> 16); /* add carry */ + answer = ~sum; /* truncate to 16 bits */ + return (answer); +} + +u_int16_t +udp_cksum(struct sockaddr_in6 *src, struct sockaddr_in6 *dst, + void *payload, u_int32_t len) +{ + struct { + struct in6_addr src; + struct in6_addr dst; + u_int32_t len; + u_int8_t zero[3]; + u_int8_t next; + } pseudo_hdr; + u_int16_t sum[2]; + + pseudo_hdr.src = src->sin6_addr; + pseudo_hdr.dst = dst->sin6_addr; + pseudo_hdr.len = htonl(len); + pseudo_hdr.zero[0] = 0; + pseudo_hdr.zero[1] = 0; + pseudo_hdr.zero[2] = 0; + pseudo_hdr.next = IPPROTO_UDP; + + sum[1] = in_cksum((u_int16_t *)&pseudo_hdr, sizeof(pseudo_hdr)); + sum[0] = in_cksum(payload, len); + + return (~in_cksum(sum, sizeof(sum))); +} + +u_int16_t +tcp_chksum(struct sockaddr_in6 *src, struct sockaddr_in6 *dst, + void *payload, u_int32_t len) +{ + struct { + struct in6_addr src; + struct in6_addr dst; + u_int32_t len; + u_int8_t zero[3]; + u_int8_t next; + } pseudo_hdr; + u_int16_t sum[2]; + + pseudo_hdr.src = src->sin6_addr; + pseudo_hdr.dst = dst->sin6_addr; + pseudo_hdr.len = htonl(len); + pseudo_hdr.zero[0] = 0; + pseudo_hdr.zero[1] = 0; + pseudo_hdr.zero[2] = 0; + pseudo_hdr.next = IPPROTO_TCP; + + sum[1] = in_cksum((u_int16_t *)&pseudo_hdr, sizeof(pseudo_hdr)); + sum[0] = in_cksum(payload, len); + + return (~in_cksum(sum, sizeof(sum))); +} + +void +usage(void) +{ + fprintf(stderr, +"Usage: traceroute6 [-adEIlnNrSTUv] [-A as_server] [-f firsthop] [-g gateway]\n" +"\t[-m hoplimit] [-p port] [-q probes] [-s src] [-t tclass]\n" +"\t[-w waittime] target [datalen]\n"); + exit(1); +} |
