8ff4fc03e6
On the receive socket, recvmsg() and poll()/select() is called. Therefore, CAP_EVENT is needed in addition to CAP_RECV.. While there, check the socket for readbility before calling recvmsg(). Reviewed by: markj@ MFC after: 3 days Sponsored by: Netflix, Inc. Differential Revision: https://reviews.freebsd.org/D36878
1815 lines
51 KiB
C
1815 lines
51 KiB
C
/* $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.
|
|
*/
|
|
|
|
#ifndef lint
|
|
static const char copyright[] =
|
|
"@(#) Copyright (c) 1990, 1993\n\
|
|
The Regents of the University of California. All rights reserved.\n";
|
|
#endif /* not lint */
|
|
|
|
#ifndef lint
|
|
#if 0
|
|
static char sccsid[] = "@(#)traceroute.c 8.1 (Berkeley) 6/6/93";
|
|
#endif
|
|
static const char rcsid[] =
|
|
"$FreeBSD$";
|
|
#endif /* not lint */
|
|
|
|
/*
|
|
* 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 30 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), 30 hops max, 56 byte packet
|
|
* 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), 30 hops max
|
|
* 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@helios.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/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 *);
|
|
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 lflag; /* print both numerical address & hostname */
|
|
static int as_path; /* print as numbers for each hop */
|
|
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;
|
|
#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:df: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 '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':
|
|
lflag++;
|
|
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);
|
|
}
|
|
|
|
/* 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)) {
|
|
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 (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;
|
|
}
|
|
} 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->th_flags = 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)
|
|
{
|
|
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);
|
|
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 if (lflag)
|
|
printf(" %s (%s)", inetname((struct sockaddr *)from), hbuf);
|
|
else
|
|
printf(" %s", inetname((struct sockaddr *)from));
|
|
|
|
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 [-adIlnNrSTUv] [-A as_server] [-f firsthop] [-g gateway]\n"
|
|
" [-m hoplimit] [-p port] [-q probes] [-s src] [-w waittime] target\n"
|
|
" [datalen]\n");
|
|
exit(1);
|
|
}
|