1121472Sume/* $KAME: ping6.c,v 1.169 2003/07/25 06:01:47 itojun Exp $ */ 262627Skris 355163Sshin/* 455163Sshin * Copyright (C) 1995, 1996, 1997, and 1998 WIDE Project. 555163Sshin * All rights reserved. 655163Sshin * 755163Sshin * Redistribution and use in source and binary forms, with or without 855163Sshin * modification, are permitted provided that the following conditions 955163Sshin * are met: 1055163Sshin * 1. Redistributions of source code must retain the above copyright 1155163Sshin * notice, this list of conditions and the following disclaimer. 1255163Sshin * 2. Redistributions in binary form must reproduce the above copyright 1355163Sshin * notice, this list of conditions and the following disclaimer in the 1455163Sshin * documentation and/or other materials provided with the distribution. 1555163Sshin * 3. Neither the name of the project nor the names of its contributors 1655163Sshin * may be used to endorse or promote products derived from this software 1755163Sshin * without specific prior written permission. 1855163Sshin * 1955163Sshin * THIS SOFTWARE IS PROVIDED BY THE PROJECT AND CONTRIBUTORS ``AS IS'' AND 2055163Sshin * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 2155163Sshin * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 2255163Sshin * ARE DISCLAIMED. IN NO EVENT SHALL THE PROJECT OR CONTRIBUTORS BE LIABLE 2355163Sshin * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 2455163Sshin * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 2555163Sshin * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 2655163Sshin * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 2755163Sshin * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 2855163Sshin * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 2955163Sshin * SUCH DAMAGE. 3055163Sshin */ 3155163Sshin 3255163Sshin/* BSDI ping.c,v 2.3 1996/01/21 17:56:50 jch Exp */ 3355163Sshin 3455163Sshin/* 3555163Sshin * Copyright (c) 1989, 1993 3655163Sshin * The Regents of the University of California. All rights reserved. 3755163Sshin * 3855163Sshin * This code is derived from software contributed to Berkeley by 3955163Sshin * Mike Muuss. 4055163Sshin * 4155163Sshin * Redistribution and use in source and binary forms, with or without 4255163Sshin * modification, are permitted provided that the following conditions 4355163Sshin * are met: 4455163Sshin * 1. Redistributions of source code must retain the above copyright 4555163Sshin * notice, this list of conditions and the following disclaimer. 4655163Sshin * 2. Redistributions in binary form must reproduce the above copyright 4755163Sshin * notice, this list of conditions and the following disclaimer in the 4855163Sshin * documentation and/or other materials provided with the distribution. 4955163Sshin * 4. Neither the name of the University nor the names of its contributors 5055163Sshin * may be used to endorse or promote products derived from this software 5155163Sshin * without specific prior written permission. 5255163Sshin * 5355163Sshin * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND 5455163Sshin * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 5555163Sshin * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 5655163Sshin * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE 5755163Sshin * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 5855163Sshin * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 5955163Sshin * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 6055163Sshin * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 6155163Sshin * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 6255163Sshin * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 6355163Sshin * SUCH DAMAGE. 6455163Sshin */ 6555163Sshin 6655163Sshin#ifndef lint 67139920Strhodesstatic const char copyright[] = 6855163Sshin"@(#) Copyright (c) 1989, 1993\n\ 6955163Sshin The Regents of the University of California. All rights reserved.\n"; 7055163Sshin#endif /* not lint */ 7155163Sshin 7255163Sshin#ifndef lint 7355163Sshin#if 0 7455163Sshinstatic char sccsid[] = "@(#)ping.c 8.1 (Berkeley) 6/5/93"; 7555163Sshin#endif 7655163Sshin#endif /* not lint */ 7755163Sshin 78216561Scharnier#include <sys/cdefs.h> 79216561Scharnier__FBSDID("$FreeBSD$"); 80216561Scharnier 8155163Sshin/* 8255163Sshin * Using the InterNet Control Message Protocol (ICMP) "ECHO" facility, 8355163Sshin * measure round-trip-delays and packet loss across network paths. 8455163Sshin * 8555163Sshin * Author - 8655163Sshin * Mike Muuss 8755163Sshin * U. S. Army Ballistic Research Laboratory 8855163Sshin * December, 1983 8955163Sshin * 9055163Sshin * Status - 9155163Sshin * Public Domain. Distribution Unlimited. 9255163Sshin * Bugs - 9355163Sshin * More statistics could always be gathered. 9455163Sshin * This program has to run SUID to ROOT to access the ICMP socket. 9555163Sshin */ 9655163Sshin/* 9755163Sshin * NOTE: 9855163Sshin * USE_SIN6_SCOPE_ID assumes that sin6_scope_id has the same semantics 9978064Sume * as IPV6_PKTINFO. Some people object it (sin6_scope_id specifies *link* 10078064Sume * while IPV6_PKTINFO specifies *interface*. Link is defined as collection of 10155163Sshin * network attached to 1 or more interfaces) 10255163Sshin */ 10355163Sshin 10455163Sshin#include <sys/param.h> 10555163Sshin#include <sys/uio.h> 10655163Sshin#include <sys/socket.h> 10755163Sshin#include <sys/time.h> 10855163Sshin 10955163Sshin#include <net/if.h> 11055163Sshin#include <net/route.h> 11155163Sshin 11255163Sshin#include <netinet/in.h> 11355163Sshin#include <netinet/ip6.h> 11455163Sshin#include <netinet/icmp6.h> 11555163Sshin#include <arpa/inet.h> 11678064Sume#include <arpa/nameser.h> 11755163Sshin#include <netdb.h> 11855163Sshin 11955163Sshin#include <ctype.h> 12055163Sshin#include <err.h> 12155163Sshin#include <errno.h> 12255163Sshin#include <fcntl.h> 12378064Sume#include <math.h> 12455163Sshin#include <signal.h> 12555163Sshin#include <stdio.h> 12655163Sshin#include <stdlib.h> 12755163Sshin#include <string.h> 12855163Sshin#include <unistd.h> 129121472Sume#ifdef HAVE_POLL_H 130121472Sume#include <poll.h> 131121472Sume#endif 13255163Sshin 13355163Sshin#ifdef IPSEC 134171135Sgnn#include <netipsec/ah.h> 135171135Sgnn#include <netipsec/ipsec.h> 13655163Sshin#endif 13755163Sshin 13862627Skris#include <md5.h> 13962627Skris 140121472Sumestruct tv32 { 141121472Sume u_int32_t tv32_sec; 142121472Sume u_int32_t tv32_usec; 143121472Sume}; 144121472Sume 14562627Skris#define MAXPACKETLEN 131072 14655163Sshin#define IP6LEN 40 147121472Sume#define ICMP6ECHOLEN 8 /* icmp echo header len excluding time */ 148121472Sume#define ICMP6ECHOTMLEN sizeof(struct tv32) 14962627Skris#define ICMP6_NIQLEN (ICMP6ECHOLEN + 8) 150168866Smtm# define CONTROLLEN 10240 /* ancillary data buffer size RFC3542 20.1 */ 15178064Sume/* FQDN case, 64 bits of nonce + 32 bits ttl */ 15278064Sume#define ICMP6_NIRLEN (ICMP6ECHOLEN + 12) 15355163Sshin#define EXTRA 256 /* for AH and various other headers. weird. */ 15455163Sshin#define DEFDATALEN ICMP6ECHOTMLEN 15562627Skris#define MAXDATALEN MAXPACKETLEN - IP6LEN - ICMP6ECHOLEN 15655163Sshin#define NROUTES 9 /* number of record route slots */ 15755163Sshin 15855163Sshin#define A(bit) rcvd_tbl[(bit)>>3] /* identify byte in array */ 15955163Sshin#define B(bit) (1 << ((bit) & 0x07)) /* identify bit in byte */ 16055163Sshin#define SET(bit) (A(bit) |= B(bit)) 16155163Sshin#define CLR(bit) (A(bit) &= (~B(bit))) 16255163Sshin#define TST(bit) (A(bit) & B(bit)) 16355163Sshin 16455163Sshin#define F_FLOOD 0x0001 16555163Sshin#define F_INTERVAL 0x0002 16655163Sshin#define F_PINGFILLED 0x0008 16755163Sshin#define F_QUIET 0x0010 16855163Sshin#define F_RROUTE 0x0020 16955163Sshin#define F_SO_DEBUG 0x0040 17055163Sshin#define F_VERBOSE 0x0100 17155163Sshin#ifdef IPSEC 17255163Sshin#ifdef IPSEC_POLICY_IPSEC 17362627Skris#define F_POLICY 0x0400 17455163Sshin#else 17562627Skris#define F_AUTHHDR 0x0200 17662627Skris#define F_ENCRYPT 0x0400 17755163Sshin#endif /*IPSEC_POLICY_IPSEC*/ 17855163Sshin#endif /*IPSEC*/ 17962627Skris#define F_NODEADDR 0x0800 18062627Skris#define F_FQDN 0x1000 18162627Skris#define F_INTERFACE 0x2000 18262627Skris#define F_SRCADDR 0x4000 18362627Skris#define F_HOSTNAME 0x10000 18462627Skris#define F_FQDNOLD 0x20000 18562627Skris#define F_NIGROUP 0x40000 18678064Sume#define F_SUPTYPES 0x80000 18778064Sume#define F_NOMINMTU 0x100000 188173765Sdd#define F_ONCE 0x200000 189182195Smatteo#define F_AUDIBLE 0x400000 190182276Smatteo#define F_MISSED 0x800000 191206889Smaxim#define F_DONTFRAG 0x1000000 19278064Sume#define F_NOUSERDATA (F_NODEADDR | F_FQDN | F_FQDNOLD | F_SUPTYPES) 19355163Sshinu_int options; 19455163Sshin 19562627Skris#define IN6LEN sizeof(struct in6_addr) 19662627Skris#define SA6LEN sizeof(struct sockaddr_in6) 19778064Sume#define DUMMY_PORT 10101 19855163Sshin 19978064Sume#define SIN6(s) ((struct sockaddr_in6 *)(s)) 20055163Sshin 20155163Sshin/* 20255163Sshin * MAX_DUP_CHK is the number of bits in received table, i.e. the maximum 20355163Sshin * number of received sequence numbers we can keep track of. Change 128 20455163Sshin * to 8192 for complete accuracy... 20555163Sshin */ 20655163Sshin#define MAX_DUP_CHK (8 * 8192) 20755163Sshinint mx_dup_ck = MAX_DUP_CHK; 20855163Sshinchar rcvd_tbl[MAX_DUP_CHK / 8]; 20955163Sshin 210209236Sbrucecstruct addrinfo *res = NULL; 21178064Sumestruct sockaddr_in6 dst; /* who to ping6 */ 21278064Sumestruct sockaddr_in6 src; /* src addr of this packet */ 213121472Sumesocklen_t srclen; 21455163Sshinint datalen = DEFDATALEN; 21555163Sshinint s; /* socket file descriptor */ 21655163Sshinu_char outpack[MAXPACKETLEN]; 21755163Sshinchar BSPACE = '\b'; /* characters written for flood */ 218182195Smatteochar BBELL = '\a'; /* characters written for AUDIBLE */ 21955163Sshinchar DOT = '.'; 22055163Sshinchar *hostname; 22155163Sshinint ident; /* process id to identify our packets */ 22278064Sumeu_int8_t nonce[8]; /* nonce field for node information */ 22378064Sumeint hoplimit = -1; /* hoplimit */ 22478064Sumeint pathmtu = 0; /* path MTU for the destination. 0 = unspec. */ 225209236Sbrucecu_char *packet = NULL; 226209236Sbrucec#ifdef HAVE_POLL_H 227209236Sbrucecstruct pollfd fdmaskp[1]; 228209236Sbrucec#else 229209236Sbrucecfd_set *fdmaskp = NULL; 230209236Sbrucecint fdmasks; 231209236Sbrucec#endif 23255163Sshin 23355163Sshin/* counters */ 234182276Smatteolong nmissedmax; /* max value of ntransmitted - nreceived - 1 */ 23555163Sshinlong npackets; /* max packets to transmit */ 23655163Sshinlong nreceived; /* # of packets we got back */ 23755163Sshinlong nrepeats; /* number of duplicates */ 23855163Sshinlong ntransmitted; /* sequence # for outbound packets = #sent */ 23978064Sumestruct timeval interval = {1, 0}; /* interval between packets */ 24055163Sshin 24155163Sshin/* timing */ 24255163Sshinint timing; /* flag to do timing */ 24355163Sshindouble tmin = 999999999.0; /* minimum round trip time */ 24455163Sshindouble tmax = 0.0; /* maximum round trip time */ 24555163Sshindouble tsum = 0.0; /* sum of all times, for doing average */ 24678064Sumedouble tsumsq = 0.0; /* sum of all times squared, for std. dev. */ 24755163Sshin 24855163Sshin/* for node addresses */ 24955163Sshinu_short naflags; 25055163Sshin 25155163Sshin/* for ancillary data(advanced API) */ 25255163Sshinstruct msghdr smsghdr; 25355163Sshinstruct iovec smsgiov; 25455163Sshinchar *scmsg = 0; 25555163Sshin 25678064Sumevolatile sig_atomic_t seenalrm; 25778064Sumevolatile sig_atomic_t seenint; 25878064Sume#ifdef SIGINFO 25978064Sumevolatile sig_atomic_t seeninfo; 26078064Sume#endif 26178064Sume 262121472Sumeint main(int, char *[]); 26392883Simpvoid fill(char *, char *); 26492883Simpint get_hoplim(struct msghdr *); 26592883Simpint get_pathmtu(struct msghdr *); 26692883Simpstruct in6_pktinfo *get_rcvpktinfo(struct msghdr *); 26792883Simpvoid onsignal(int); 26892883Simpvoid retransmit(void); 26992883Simpvoid onint(int); 27092883Simpsize_t pingerlen(void); 27192883Simpint pinger(void); 27292883Simpconst char *pr_addr(struct sockaddr *, int); 27392883Simpvoid pr_icmph(struct icmp6_hdr *, u_char *); 27492883Simpvoid pr_iph(struct ip6_hdr *); 27592883Simpvoid pr_suptypes(struct icmp6_nodeinfo *, size_t); 27692883Simpvoid pr_nodeaddr(struct icmp6_nodeinfo *, int); 27792883Simpint myechoreply(const struct icmp6_hdr *); 27892883Simpint mynireply(const struct icmp6_nodeinfo *); 27992883Simpchar *dnsdecode(const u_char **, const u_char *, const u_char *, 280121472Sume char *, size_t); 28192883Simpvoid pr_pack(u_char *, int, struct msghdr *); 28292883Simpvoid pr_exthdrs(struct msghdr *); 283168866Smtmvoid pr_ip6opt(void *, size_t); 284168866Smtmvoid pr_rthdr(void *, size_t); 28592883Simpint pr_bitrange(u_int32_t, int, int); 28692883Simpvoid pr_retip(struct ip6_hdr *, u_char *); 28792883Simpvoid summary(void); 28892883Simpvoid tvsub(struct timeval *, struct timeval *); 28992883Simpint setpolicy(int, char *); 290250251Shrschar *nigroup(char *, int); 29192883Simpvoid usage(void); 29255163Sshin 29355163Sshinint 294216561Scharniermain(int argc, char *argv[]) 29555163Sshin{ 29655163Sshin struct itimerval itimer; 29755163Sshin struct sockaddr_in6 from; 298121472Sume#ifndef HAVE_ARC4RANDOM 299121472Sume struct timeval seed; 300121472Sume#endif 301121472Sume#ifdef HAVE_POLL_H 302121472Sume int timeout; 303121472Sume#else 30478064Sume struct timeval timeout, *tv; 305121472Sume#endif 30655163Sshin struct addrinfo hints; 30792806Sobrien int cc, i; 308121472Sume int ch, hold, packlen, preload, optval, ret_ga; 309250251Shrs int nig_oldmcprefix = -1; 310209236Sbrucec u_char *datap; 311121472Sume char *e, *target, *ifname = NULL, *gateway = NULL; 31255163Sshin int ip6optlen = 0; 31355163Sshin struct cmsghdr *scmsgp = NULL; 314209236Sbrucec /* For control (ancillary) data received from recvmsg() */ 315209236Sbrucec struct cmsghdr cm[CONTROLLEN]; 316121472Sume#if defined(SO_SNDBUF) && defined(SO_RCVBUF) 317121472Sume u_long lsockbufsize; 31855163Sshin int sockbufsize = 0; 319121472Sume#endif 32062627Skris int usepktinfo = 0; 32162627Skris struct in6_pktinfo *pktinfo = NULL; 32262627Skris#ifdef USE_RFC2292BIS 32362627Skris struct ip6_rthdr *rthdr = NULL; 32462627Skris#endif 32555163Sshin#ifdef IPSEC_POLICY_IPSEC 32655163Sshin char *policy_in = NULL; 32755163Sshin char *policy_out = NULL; 32855163Sshin#endif 32978064Sume double intval; 33078064Sume size_t rthlen; 331121472Sume#ifdef IPV6_USE_MIN_MTU 332121472Sume int mflag = 0; 333121472Sume#endif 33455163Sshin 33555163Sshin /* just to be sure */ 336107652Ssuz memset(&smsghdr, 0, sizeof(smsghdr)); 337107652Ssuz memset(&smsgiov, 0, sizeof(smsgiov)); 33855163Sshin 33955163Sshin preload = 0; 34055163Sshin datap = &outpack[ICMP6ECHOLEN + ICMP6ECHOTMLEN]; 34155163Sshin#ifndef IPSEC 34278064Sume#define ADDOPTS 34355163Sshin#else 34455163Sshin#ifdef IPSEC_POLICY_IPSEC 34578064Sume#define ADDOPTS "P:" 34655163Sshin#else 34778064Sume#define ADDOPTS "AE" 34855163Sshin#endif /*IPSEC_POLICY_IPSEC*/ 34955163Sshin#endif 35078064Sume while ((ch = getopt(argc, argv, 351206889Smaxim "a:b:c:DdfHg:h:I:i:l:mnNop:qrRS:s:tvwW" ADDOPTS)) != -1) { 35278064Sume#undef ADDOPTS 35378064Sume switch (ch) { 35478064Sume case 'a': 35578064Sume { 35678064Sume char *cp; 35755163Sshin 35878064Sume options &= ~F_NOUSERDATA; 35978064Sume options |= F_NODEADDR; 36078064Sume for (cp = optarg; *cp != '\0'; cp++) { 36178064Sume switch (*cp) { 36278064Sume case 'a': 36378064Sume naflags |= NI_NODEADDR_FLAG_ALL; 36478064Sume break; 36578064Sume case 'c': 36678064Sume case 'C': 36778064Sume naflags |= NI_NODEADDR_FLAG_COMPAT; 36878064Sume break; 36978064Sume case 'l': 37078064Sume case 'L': 37178064Sume naflags |= NI_NODEADDR_FLAG_LINKLOCAL; 37278064Sume break; 37378064Sume case 's': 37478064Sume case 'S': 37578064Sume naflags |= NI_NODEADDR_FLAG_SITELOCAL; 37678064Sume break; 37778064Sume case 'g': 37878064Sume case 'G': 37978064Sume naflags |= NI_NODEADDR_FLAG_GLOBAL; 38078064Sume break; 38178064Sume case 'A': /* experimental. not in the spec */ 38278064Sume#ifdef NI_NODEADDR_FLAG_ANYCAST 38378064Sume naflags |= NI_NODEADDR_FLAG_ANYCAST; 38478064Sume break; 38578064Sume#else 38678064Sume errx(1, 38778064Sume"-a A is not supported on the platform"); 38878064Sume /*NOTREACHED*/ 38978064Sume#endif 39078064Sume default: 39178064Sume usage(); 39278064Sume /*NOTREACHED*/ 39378064Sume } 39478064Sume } 39578064Sume break; 39678064Sume } 39778064Sume case 'b': 39855163Sshin#if defined(SO_SNDBUF) && defined(SO_RCVBUF) 399121472Sume errno = 0; 400121472Sume e = NULL; 401121472Sume lsockbufsize = strtoul(optarg, &e, 10); 402121472Sume sockbufsize = lsockbufsize; 403121472Sume if (errno || !*optarg || *e || 404121472Sume sockbufsize != lsockbufsize) 405121472Sume errx(1, "invalid socket buffer size"); 40655163Sshin#else 40787668Scharnier errx(1, 40855163Sshin"-b option ignored: SO_SNDBUF/SO_RCVBUF socket options not supported"); 40955163Sshin#endif 41055163Sshin break; 41155163Sshin case 'c': 41255163Sshin npackets = strtol(optarg, &e, 10); 41355163Sshin if (npackets <= 0 || *optarg == '\0' || *e != '\0') 41455163Sshin errx(1, 41555163Sshin "illegal number of packets -- %s", optarg); 41655163Sshin break; 417206889Smaxim case 'D': 418206889Smaxim options |= F_DONTFRAG; 419206889Smaxim break; 42055163Sshin case 'd': 42155163Sshin options |= F_SO_DEBUG; 42255163Sshin break; 42355163Sshin case 'f': 424121472Sume if (getuid()) { 425121472Sume errno = EPERM; 426121472Sume errx(1, "Must be superuser to flood ping"); 427121472Sume } 42855163Sshin options |= F_FLOOD; 42955163Sshin setbuf(stdout, (char *)NULL); 43055163Sshin break; 431121472Sume case 'g': 432121472Sume gateway = optarg; 433121472Sume break; 43462627Skris case 'H': 43562627Skris options |= F_HOSTNAME; 43662627Skris break; 43755163Sshin case 'h': /* hoplimit */ 43855163Sshin hoplimit = strtol(optarg, &e, 10); 439121472Sume if (*optarg == '\0' || *e != '\0') 440121472Sume errx(1, "illegal hoplimit %s", optarg); 44155163Sshin if (255 < hoplimit || hoplimit < -1) 44255163Sshin errx(1, 44355163Sshin "illegal hoplimit -- %s", optarg); 44455163Sshin break; 44555163Sshin case 'I': 44655163Sshin ifname = optarg; 44755163Sshin options |= F_INTERFACE; 44862627Skris#ifndef USE_SIN6_SCOPE_ID 44962627Skris usepktinfo++; 45062627Skris#endif 45155163Sshin break; 45255163Sshin case 'i': /* wait between sending packets */ 45378064Sume intval = strtod(optarg, &e); 45478064Sume if (*optarg == '\0' || *e != '\0') 45578064Sume errx(1, "illegal timing interval %s", optarg); 45678064Sume if (intval < 1 && getuid()) { 45778064Sume errx(1, "%s: only root may use interval < 1s", 45878064Sume strerror(EPERM)); 45978064Sume } 46078064Sume interval.tv_sec = (long)intval; 46178064Sume interval.tv_usec = 46278064Sume (long)((intval - interval.tv_sec) * 1000000); 46378064Sume if (interval.tv_sec < 0) 46478064Sume errx(1, "illegal timing interval %s", optarg); 46578064Sume /* less than 1/hz does not make sense */ 466176549Ssilby if (interval.tv_sec == 0 && interval.tv_usec < 1) { 467176549Ssilby warnx("too small interval, raised to .000001"); 468176549Ssilby interval.tv_usec = 1; 46978064Sume } 47055163Sshin options |= F_INTERVAL; 47155163Sshin break; 47255163Sshin case 'l': 473121472Sume if (getuid()) { 474121472Sume errno = EPERM; 475121472Sume errx(1, "Must be superuser to preload"); 476121472Sume } 47755163Sshin preload = strtol(optarg, &e, 10); 47855163Sshin if (preload < 0 || *optarg == '\0' || *e != '\0') 47955163Sshin errx(1, "illegal preload value -- %s", optarg); 48055163Sshin break; 48178064Sume case 'm': 48278064Sume#ifdef IPV6_USE_MIN_MTU 483121472Sume mflag++; 48478064Sume break; 48578064Sume#else 48678064Sume errx(1, "-%c is not supported on this platform", ch); 48778064Sume /*NOTREACHED*/ 48878064Sume#endif 48955163Sshin case 'n': 49078064Sume options &= ~F_HOSTNAME; 49155163Sshin break; 49262627Skris case 'N': 49362627Skris options |= F_NIGROUP; 494250251Shrs nig_oldmcprefix++; 49562627Skris break; 496173765Sdd case 'o': 497173765Sdd options |= F_ONCE; 498173765Sdd break; 49955163Sshin case 'p': /* fill buffer with user pattern */ 50055163Sshin options |= F_PINGFILLED; 50155163Sshin fill((char *)datap, optarg); 50255163Sshin break; 50355163Sshin case 'q': 50455163Sshin options |= F_QUIET; 50555163Sshin break; 506182276Smatteo case 'r': 507182276Smatteo options |= F_AUDIBLE; 508182276Smatteo break; 509182276Smatteo case 'R': 510182276Smatteo options |= F_MISSED; 511182276Smatteo break; 51262627Skris case 'S': 513121472Sume memset(&hints, 0, sizeof(struct addrinfo)); 514121472Sume hints.ai_flags = AI_NUMERICHOST; /* allow hostname? */ 515121472Sume hints.ai_family = AF_INET6; 516121472Sume hints.ai_socktype = SOCK_RAW; 517121472Sume hints.ai_protocol = IPPROTO_ICMPV6; 518121472Sume 519121472Sume ret_ga = getaddrinfo(optarg, NULL, &hints, &res); 520121472Sume if (ret_ga) { 521121472Sume errx(1, "invalid source address: %s", 522121472Sume gai_strerror(ret_ga)); 523121472Sume } 524121472Sume /* 525121472Sume * res->ai_family must be AF_INET6 and res->ai_addrlen 526121472Sume * must be sizeof(src). 527121472Sume */ 528121472Sume memcpy(&src, res->ai_addr, res->ai_addrlen); 529121472Sume srclen = res->ai_addrlen; 530121472Sume freeaddrinfo(res); 531209236Sbrucec res = NULL; 53262627Skris options |= F_SRCADDR; 53362627Skris break; 53455163Sshin case 's': /* size of packet to send */ 53555163Sshin datalen = strtol(optarg, &e, 10); 53655163Sshin if (datalen <= 0 || *optarg == '\0' || *e != '\0') 53755163Sshin errx(1, "illegal datalen value -- %s", optarg); 53878064Sume if (datalen > MAXDATALEN) { 53955163Sshin errx(1, 54055163Sshin "datalen value too large, maximum is %d", 54155163Sshin MAXDATALEN); 54278064Sume } 54355163Sshin break; 54478064Sume case 't': 54578064Sume options &= ~F_NOUSERDATA; 54678064Sume options |= F_SUPTYPES; 54778064Sume break; 54855163Sshin case 'v': 54955163Sshin options |= F_VERBOSE; 55055163Sshin break; 55155163Sshin case 'w': 55278064Sume options &= ~F_NOUSERDATA; 55355163Sshin options |= F_FQDN; 55455163Sshin break; 55562627Skris case 'W': 55678064Sume options &= ~F_NOUSERDATA; 55762627Skris options |= F_FQDNOLD; 55862627Skris break; 55955163Sshin#ifdef IPSEC 56055163Sshin#ifdef IPSEC_POLICY_IPSEC 56155163Sshin case 'P': 56255163Sshin options |= F_POLICY; 56369571Sume if (!strncmp("in", optarg, 2)) { 56469571Sume if ((policy_in = strdup(optarg)) == NULL) 56569571Sume errx(1, "strdup"); 56669571Sume } else if (!strncmp("out", optarg, 3)) { 56769571Sume if ((policy_out = strdup(optarg)) == NULL) 56869571Sume errx(1, "strdup"); 56969571Sume } else 57055163Sshin errx(1, "invalid security policy"); 57155163Sshin break; 57255163Sshin#else 57355163Sshin case 'A': 57455163Sshin options |= F_AUTHHDR; 57555163Sshin break; 57655163Sshin case 'E': 57755163Sshin options |= F_ENCRYPT; 57855163Sshin break; 57955163Sshin#endif /*IPSEC_POLICY_IPSEC*/ 58055163Sshin#endif /*IPSEC*/ 58155163Sshin default: 58255163Sshin usage(); 58362627Skris /*NOTREACHED*/ 58455163Sshin } 58562627Skris } 586121472Sume 58755163Sshin argc -= optind; 58855163Sshin argv += optind; 58955163Sshin 59062627Skris if (argc < 1) { 59155163Sshin usage(); 59262627Skris /*NOTREACHED*/ 59362627Skris } 59455163Sshin 59562627Skris if (argc > 1) { 59678064Sume#ifdef IPV6_RECVRTHDR /* 2292bis */ 59778064Sume rthlen = CMSG_SPACE(inet6_rth_space(IPV6_RTHDR_TYPE_0, 59878064Sume argc - 1)); 59978064Sume#else /* RFC2292 */ 60078064Sume rthlen = inet6_rthdr_space(IPV6_RTHDR_TYPE_0, argc - 1); 60162627Skris#endif 60278064Sume if (rthlen == 0) { 60378064Sume errx(1, "too many intermediate hops"); 60478064Sume /*NOTREACHED*/ 60578064Sume } 60678064Sume ip6optlen += rthlen; 60762627Skris } 60855163Sshin 60962627Skris if (options & F_NIGROUP) { 610250251Shrs target = nigroup(argv[argc - 1], nig_oldmcprefix); 61162627Skris if (target == NULL) { 61262627Skris usage(); 61362627Skris /*NOTREACHED*/ 61462627Skris } 61562627Skris } else 61662627Skris target = argv[argc - 1]; 61755163Sshin 61855163Sshin /* getaddrinfo */ 619121472Sume memset(&hints, 0, sizeof(struct addrinfo)); 62078064Sume hints.ai_flags = AI_CANONNAME; 62155163Sshin hints.ai_family = AF_INET6; 62255163Sshin hints.ai_socktype = SOCK_RAW; 62355163Sshin hints.ai_protocol = IPPROTO_ICMPV6; 62455163Sshin 62555163Sshin ret_ga = getaddrinfo(target, NULL, &hints, &res); 62687668Scharnier if (ret_ga) 62787668Scharnier errx(1, "%s", gai_strerror(ret_ga)); 62855163Sshin if (res->ai_canonname) 62955163Sshin hostname = res->ai_canonname; 63055163Sshin else 63155163Sshin hostname = target; 63278064Sume 63355163Sshin if (!res->ai_addr) 63455163Sshin errx(1, "getaddrinfo failed"); 63555163Sshin 63655163Sshin (void)memcpy(&dst, res->ai_addr, res->ai_addrlen); 63755163Sshin 63878064Sume if ((s = socket(res->ai_family, res->ai_socktype, 63978064Sume res->ai_protocol)) < 0) 64078064Sume err(1, "socket"); 64178064Sume 642121472Sume /* set the source address if specified. */ 643121472Sume if ((options & F_SRCADDR) && 644121472Sume bind(s, (struct sockaddr *)&src, srclen) != 0) { 645121472Sume err(1, "bind"); 646121472Sume } 647121472Sume 648121472Sume /* set the gateway (next hop) if specified */ 649121472Sume if (gateway) { 650121472Sume struct addrinfo ghints, *gres; 651121472Sume int error; 652121472Sume 653121472Sume memset(&ghints, 0, sizeof(ghints)); 654121472Sume ghints.ai_family = AF_INET6; 655121472Sume ghints.ai_socktype = SOCK_RAW; 656121472Sume ghints.ai_protocol = IPPROTO_ICMPV6; 657121472Sume 658121472Sume error = getaddrinfo(gateway, NULL, &hints, &gres); 659121472Sume if (error) { 660121472Sume errx(1, "getaddrinfo for the gateway %s: %s", 661121472Sume gateway, gai_strerror(error)); 662121472Sume } 663121472Sume if (gres->ai_next && (options & F_VERBOSE)) 664121472Sume warnx("gateway resolves to multiple addresses"); 665121472Sume 666121472Sume if (setsockopt(s, IPPROTO_IPV6, IPV6_NEXTHOP, 667121472Sume gres->ai_addr, gres->ai_addrlen)) { 668121472Sume err(1, "setsockopt(IPV6_NEXTHOP)"); 669121472Sume } 670121472Sume 671121472Sume freeaddrinfo(gres); 672121472Sume } 673121472Sume 67478064Sume /* 67578064Sume * let the kerel pass extension headers of incoming packets, 67678064Sume * for privileged socket options 67778064Sume */ 67878064Sume if ((options & F_VERBOSE) != 0) { 67978064Sume int opton = 1; 68078064Sume 68178064Sume#ifdef IPV6_RECVHOPOPTS 68278064Sume if (setsockopt(s, IPPROTO_IPV6, IPV6_RECVHOPOPTS, &opton, 68378064Sume sizeof(opton))) 68478064Sume err(1, "setsockopt(IPV6_RECVHOPOPTS)"); 68578064Sume#else /* old adv. API */ 68678064Sume if (setsockopt(s, IPPROTO_IPV6, IPV6_HOPOPTS, &opton, 68778064Sume sizeof(opton))) 68878064Sume err(1, "setsockopt(IPV6_HOPOPTS)"); 68978064Sume#endif 69078064Sume#ifdef IPV6_RECVDSTOPTS 69178064Sume if (setsockopt(s, IPPROTO_IPV6, IPV6_RECVDSTOPTS, &opton, 69278064Sume sizeof(opton))) 69378064Sume err(1, "setsockopt(IPV6_RECVDSTOPTS)"); 69478064Sume#else /* old adv. API */ 69578064Sume if (setsockopt(s, IPPROTO_IPV6, IPV6_DSTOPTS, &opton, 69678064Sume sizeof(opton))) 69778064Sume err(1, "setsockopt(IPV6_DSTOPTS)"); 69878064Sume#endif 69978064Sume#ifdef IPV6_RECVRTHDRDSTOPTS 70078064Sume if (setsockopt(s, IPPROTO_IPV6, IPV6_RECVRTHDRDSTOPTS, &opton, 70178064Sume sizeof(opton))) 70278064Sume err(1, "setsockopt(IPV6_RECVRTHDRDSTOPTS)"); 70378064Sume#endif 70478064Sume } 70578064Sume 70678064Sume /* revoke root privilege */ 707241852Seadler if (seteuid(getuid()) != 0) 708241852Seadler err(1, "seteuid() failed"); 709241852Seadler if (setuid(getuid()) != 0) 710241852Seadler err(1, "setuid() failed"); 71178064Sume 712121472Sume if ((options & F_FLOOD) && (options & F_INTERVAL)) 71355163Sshin errx(1, "-f and -i incompatible options"); 71455163Sshin 71578064Sume if ((options & F_NOUSERDATA) == 0) { 716121472Sume if (datalen >= sizeof(struct tv32)) { 71778064Sume /* we can time transfer */ 71878064Sume timing = 1; 71978064Sume } else 72078064Sume timing = 0; 72178064Sume /* in F_VERBOSE case, we may get non-echoreply packets*/ 72278064Sume if (options & F_VERBOSE) 72378064Sume packlen = 2048 + IP6LEN + ICMP6ECHOLEN + EXTRA; 72478064Sume else 72578064Sume packlen = datalen + IP6LEN + ICMP6ECHOLEN + EXTRA; 72678064Sume } else { 72778064Sume /* suppress timing for node information query */ 72878064Sume timing = 0; 72978064Sume datalen = 2048; 73078064Sume packlen = 2048 + IP6LEN + ICMP6ECHOLEN + EXTRA; 73178064Sume } 73278064Sume 73355163Sshin if (!(packet = (u_char *)malloc((u_int)packlen))) 734121472Sume err(1, "Unable to allocate packet"); 73555163Sshin if (!(options & F_PINGFILLED)) 73678064Sume for (i = ICMP6ECHOLEN; i < packlen; ++i) 73755163Sshin *datap++ = i; 73855163Sshin 73955163Sshin ident = getpid() & 0xFFFF; 740121472Sume#ifndef HAVE_ARC4RANDOM 741121472Sume gettimeofday(&seed, NULL); 742121472Sume srand((unsigned int)(seed.tv_sec ^ seed.tv_usec ^ (long)ident)); 74378064Sume memset(nonce, 0, sizeof(nonce)); 74478064Sume for (i = 0; i < sizeof(nonce); i += sizeof(int)) 74578064Sume *((int *)&nonce[i]) = rand(); 74678064Sume#else 74778064Sume memset(nonce, 0, sizeof(nonce)); 74878064Sume for (i = 0; i < sizeof(nonce); i += sizeof(u_int32_t)) 74978064Sume *((u_int32_t *)&nonce[i]) = arc4random(); 75078064Sume#endif 751206889Smaxim optval = 1; 752206889Smaxim if (options & F_DONTFRAG) 753206889Smaxim if (setsockopt(s, IPPROTO_IPV6, IPV6_DONTFRAG, 754206889Smaxim &optval, sizeof(optval)) == -1) 755206889Smaxim err(1, "IPV6_DONTFRAG"); 75655163Sshin hold = 1; 75755163Sshin 75855163Sshin if (options & F_SO_DEBUG) 75955163Sshin (void)setsockopt(s, SOL_SOCKET, SO_DEBUG, (char *)&hold, 76055163Sshin sizeof(hold)); 76155163Sshin optval = IPV6_DEFHLIM; 76255163Sshin if (IN6_IS_ADDR_MULTICAST(&dst.sin6_addr)) 76355163Sshin if (setsockopt(s, IPPROTO_IPV6, IPV6_MULTICAST_HOPS, 76478064Sume &optval, sizeof(optval)) == -1) 76555163Sshin err(1, "IPV6_MULTICAST_HOPS"); 76678064Sume#ifdef IPV6_USE_MIN_MTU 767121472Sume if (mflag != 1) { 768121472Sume optval = mflag > 1 ? 0 : 1; 769121472Sume 77078064Sume if (setsockopt(s, IPPROTO_IPV6, IPV6_USE_MIN_MTU, 77178064Sume &optval, sizeof(optval)) == -1) 77278064Sume err(1, "setsockopt(IPV6_USE_MIN_MTU)"); 77378064Sume } 77478064Sume#ifdef IPV6_RECVPATHMTU 77578064Sume else { 77678064Sume optval = 1; 77778064Sume if (setsockopt(s, IPPROTO_IPV6, IPV6_RECVPATHMTU, 77878064Sume &optval, sizeof(optval)) == -1) 77978064Sume err(1, "setsockopt(IPV6_RECVPATHMTU)"); 78078064Sume } 78178064Sume#endif /* IPV6_RECVPATHMTU */ 78278064Sume#endif /* IPV6_USE_MIN_MTU */ 78355163Sshin 78455163Sshin#ifdef IPSEC 78555163Sshin#ifdef IPSEC_POLICY_IPSEC 78655163Sshin if (options & F_POLICY) { 78755163Sshin if (setpolicy(s, policy_in) < 0) 78864277Skris errx(1, "%s", ipsec_strerror()); 78955163Sshin if (setpolicy(s, policy_out) < 0) 79064277Skris errx(1, "%s", ipsec_strerror()); 79155163Sshin } 79255163Sshin#else 79355163Sshin if (options & F_AUTHHDR) { 79455163Sshin optval = IPSEC_LEVEL_REQUIRE; 79555163Sshin#ifdef IPV6_AUTH_TRANS_LEVEL 79655163Sshin if (setsockopt(s, IPPROTO_IPV6, IPV6_AUTH_TRANS_LEVEL, 79778064Sume &optval, sizeof(optval)) == -1) 79855163Sshin err(1, "setsockopt(IPV6_AUTH_TRANS_LEVEL)"); 79955163Sshin#else /* old def */ 80055163Sshin if (setsockopt(s, IPPROTO_IPV6, IPV6_AUTH_LEVEL, 80178064Sume &optval, sizeof(optval)) == -1) 80255163Sshin err(1, "setsockopt(IPV6_AUTH_LEVEL)"); 80355163Sshin#endif 80455163Sshin } 80555163Sshin if (options & F_ENCRYPT) { 80655163Sshin optval = IPSEC_LEVEL_REQUIRE; 80755163Sshin if (setsockopt(s, IPPROTO_IPV6, IPV6_ESP_TRANS_LEVEL, 80878064Sume &optval, sizeof(optval)) == -1) 80955163Sshin err(1, "setsockopt(IPV6_ESP_TRANS_LEVEL)"); 81055163Sshin } 81155163Sshin#endif /*IPSEC_POLICY_IPSEC*/ 81255163Sshin#endif 81355163Sshin 81455163Sshin#ifdef ICMP6_FILTER 81555163Sshin { 81655163Sshin struct icmp6_filter filt; 81755163Sshin if (!(options & F_VERBOSE)) { 81855163Sshin ICMP6_FILTER_SETBLOCKALL(&filt); 81962627Skris if ((options & F_FQDN) || (options & F_FQDNOLD) || 82078064Sume (options & F_NODEADDR) || (options & F_SUPTYPES)) 82155163Sshin ICMP6_FILTER_SETPASS(ICMP6_NI_REPLY, &filt); 82255163Sshin else 82355163Sshin ICMP6_FILTER_SETPASS(ICMP6_ECHO_REPLY, &filt); 82455163Sshin } else { 82555163Sshin ICMP6_FILTER_SETPASSALL(&filt); 82655163Sshin } 82755163Sshin if (setsockopt(s, IPPROTO_ICMPV6, ICMP6_FILTER, &filt, 82878064Sume sizeof(filt)) < 0) 82955163Sshin err(1, "setsockopt(ICMP6_FILTER)"); 83055163Sshin } 83155163Sshin#endif /*ICMP6_FILTER*/ 83255163Sshin 83362627Skris /* let the kerel pass extension headers of incoming packets */ 83462627Skris if ((options & F_VERBOSE) != 0) { 83562627Skris int opton = 1; 83662627Skris 83762627Skris#ifdef IPV6_RECVRTHDR 83862627Skris if (setsockopt(s, IPPROTO_IPV6, IPV6_RECVRTHDR, &opton, 83978064Sume sizeof(opton))) 84062627Skris err(1, "setsockopt(IPV6_RECVRTHDR)"); 84162627Skris#else /* old adv. API */ 84262627Skris if (setsockopt(s, IPPROTO_IPV6, IPV6_RTHDR, &opton, 84378064Sume sizeof(opton))) 84462627Skris err(1, "setsockopt(IPV6_RTHDR)"); 84562627Skris#endif 84662627Skris } 84762627Skris 84855163Sshin/* 84955163Sshin optval = 1; 85055163Sshin if (IN6_IS_ADDR_MULTICAST(&dst.sin6_addr)) 85155163Sshin if (setsockopt(s, IPPROTO_IPV6, IPV6_MULTICAST_LOOP, 85278064Sume &optval, sizeof(optval)) == -1) 85355163Sshin err(1, "IPV6_MULTICAST_LOOP"); 85455163Sshin*/ 85555163Sshin 85662627Skris /* Specify the outgoing interface and/or the source address */ 85762627Skris if (usepktinfo) 85855163Sshin ip6optlen += CMSG_SPACE(sizeof(struct in6_pktinfo)); 85955163Sshin 86055163Sshin if (hoplimit != -1) 86155163Sshin ip6optlen += CMSG_SPACE(sizeof(int)); 86255163Sshin 86355163Sshin /* set IP6 packet options */ 86455163Sshin if (ip6optlen) { 86555163Sshin if ((scmsg = (char *)malloc(ip6optlen)) == 0) 86655163Sshin errx(1, "can't allocate enough memory"); 86755163Sshin smsghdr.msg_control = (caddr_t)scmsg; 86855163Sshin smsghdr.msg_controllen = ip6optlen; 86955163Sshin scmsgp = (struct cmsghdr *)scmsg; 87055163Sshin } 87162627Skris if (usepktinfo) { 87262627Skris pktinfo = (struct in6_pktinfo *)(CMSG_DATA(scmsgp)); 87362627Skris memset(pktinfo, 0, sizeof(*pktinfo)); 87455163Sshin scmsgp->cmsg_len = CMSG_LEN(sizeof(struct in6_pktinfo)); 87555163Sshin scmsgp->cmsg_level = IPPROTO_IPV6; 87655163Sshin scmsgp->cmsg_type = IPV6_PKTINFO; 87762627Skris scmsgp = CMSG_NXTHDR(&smsghdr, scmsgp); 87862627Skris } 87955163Sshin 88062627Skris /* set the outgoing interface */ 88162627Skris if (ifname) { 88262627Skris#ifndef USE_SIN6_SCOPE_ID 88362627Skris /* pktinfo must have already been allocated */ 88462627Skris if ((pktinfo->ipi6_ifindex = if_nametoindex(ifname)) == 0) 88578064Sume errx(1, "%s: invalid interface name", ifname); 88655163Sshin#else 88755163Sshin if ((dst.sin6_scope_id = if_nametoindex(ifname)) == 0) 88855163Sshin errx(1, "%s: invalid interface name", ifname); 88955163Sshin#endif 89055163Sshin } 89155163Sshin if (hoplimit != -1) { 89255163Sshin scmsgp->cmsg_len = CMSG_LEN(sizeof(int)); 89355163Sshin scmsgp->cmsg_level = IPPROTO_IPV6; 89455163Sshin scmsgp->cmsg_type = IPV6_HOPLIMIT; 89555163Sshin *(int *)(CMSG_DATA(scmsgp)) = hoplimit; 89655163Sshin 89755163Sshin scmsgp = CMSG_NXTHDR(&smsghdr, scmsgp); 89855163Sshin } 89962627Skris 90055163Sshin if (argc > 1) { /* some intermediate addrs are specified */ 90155163Sshin int hops, error; 90262627Skris#ifdef USE_RFC2292BIS 90362627Skris int rthdrlen; 90462627Skris#endif 90555163Sshin 90662627Skris#ifdef USE_RFC2292BIS 90762627Skris rthdrlen = inet6_rth_space(IPV6_RTHDR_TYPE_0, argc - 1); 90862627Skris scmsgp->cmsg_len = CMSG_LEN(rthdrlen); 90962627Skris scmsgp->cmsg_level = IPPROTO_IPV6; 91062627Skris scmsgp->cmsg_type = IPV6_RTHDR; 91162627Skris rthdr = (struct ip6_rthdr *)CMSG_DATA(scmsgp); 91262627Skris rthdr = inet6_rth_init((void *)rthdr, rthdrlen, 91378064Sume IPV6_RTHDR_TYPE_0, argc - 1); 91462627Skris if (rthdr == NULL) 91562627Skris errx(1, "can't initialize rthdr"); 91662627Skris#else /* old advanced API */ 91755163Sshin if ((scmsgp = (struct cmsghdr *)inet6_rthdr_init(scmsgp, 91878064Sume IPV6_RTHDR_TYPE_0)) == 0) 91955163Sshin errx(1, "can't initialize rthdr"); 92062627Skris#endif /* USE_RFC2292BIS */ 92155163Sshin 92255163Sshin for (hops = 0; hops < argc - 1; hops++) { 92355163Sshin struct addrinfo *iaip; 92455163Sshin 92578064Sume if ((error = getaddrinfo(argv[hops], NULL, &hints, 92678064Sume &iaip))) 92764277Skris errx(1, "%s", gai_strerror(error)); 92878064Sume if (SIN6(iaip->ai_addr)->sin6_family != AF_INET6) 92955163Sshin errx(1, 93078064Sume "bad addr family of an intermediate addr"); 93155163Sshin 93262627Skris#ifdef USE_RFC2292BIS 93362627Skris if (inet6_rth_add(rthdr, 93478064Sume &(SIN6(iaip->ai_addr))->sin6_addr)) 93562627Skris errx(1, "can't add an intermediate node"); 93662627Skris#else /* old advanced API */ 93755163Sshin if (inet6_rthdr_add(scmsgp, 93878064Sume &(SIN6(iaip->ai_addr))->sin6_addr, 93978064Sume IPV6_RTHDR_LOOSE)) 94055163Sshin errx(1, "can't add an intermediate node"); 94162627Skris#endif /* USE_RFC2292BIS */ 94278064Sume freeaddrinfo(iaip); 94355163Sshin } 94455163Sshin 94562627Skris#ifndef USE_RFC2292BIS 94655163Sshin if (inet6_rthdr_lasthop(scmsgp, IPV6_RTHDR_LOOSE)) 94755163Sshin errx(1, "can't set the last flag"); 94862627Skris#endif 94955163Sshin 95055163Sshin scmsgp = CMSG_NXTHDR(&smsghdr, scmsgp); 95155163Sshin } 95255163Sshin 953121472Sume if (!(options & F_SRCADDR)) { 95455163Sshin /* 955121472Sume * get the source address. XXX since we revoked the root 956121472Sume * privilege, we cannot use a raw socket for this. 95755163Sshin */ 958121472Sume int dummy; 959121472Sume socklen_t len = sizeof(src); 96078064Sume 96155163Sshin if ((dummy = socket(AF_INET6, SOCK_DGRAM, 0)) < 0) 96255163Sshin err(1, "UDP socket"); 96355163Sshin 96455163Sshin src.sin6_family = AF_INET6; 96555163Sshin src.sin6_addr = dst.sin6_addr; 96655163Sshin src.sin6_port = ntohs(DUMMY_PORT); 96755163Sshin src.sin6_scope_id = dst.sin6_scope_id; 96855163Sshin 96962627Skris#ifdef USE_RFC2292BIS 97062627Skris if (pktinfo && 97162627Skris setsockopt(dummy, IPPROTO_IPV6, IPV6_PKTINFO, 97278064Sume (void *)pktinfo, sizeof(*pktinfo))) 97362627Skris err(1, "UDP setsockopt(IPV6_PKTINFO)"); 97462627Skris 97562627Skris if (hoplimit != -1 && 976121472Sume setsockopt(dummy, IPPROTO_IPV6, IPV6_UNICAST_HOPS, 97778064Sume (void *)&hoplimit, sizeof(hoplimit))) 978121472Sume err(1, "UDP setsockopt(IPV6_UNICAST_HOPS)"); 97962627Skris 980121472Sume if (hoplimit != -1 && 981121472Sume setsockopt(dummy, IPPROTO_IPV6, IPV6_MULTICAST_HOPS, 982121472Sume (void *)&hoplimit, sizeof(hoplimit))) 983121472Sume err(1, "UDP setsockopt(IPV6_MULTICAST_HOPS)"); 984121472Sume 98562627Skris if (rthdr && 98662627Skris setsockopt(dummy, IPPROTO_IPV6, IPV6_RTHDR, 98778064Sume (void *)rthdr, (rthdr->ip6r_len + 1) << 3)) 98862627Skris err(1, "UDP setsockopt(IPV6_RTHDR)"); 98962627Skris#else /* old advanced API */ 99062627Skris if (smsghdr.msg_control && 99162627Skris setsockopt(dummy, IPPROTO_IPV6, IPV6_PKTOPTIONS, 99278064Sume (void *)smsghdr.msg_control, smsghdr.msg_controllen)) 99362627Skris err(1, "UDP setsockopt(IPV6_PKTOPTIONS)"); 99455163Sshin#endif 99578064Sume 99655163Sshin if (connect(dummy, (struct sockaddr *)&src, len) < 0) 99755163Sshin err(1, "UDP connect"); 99878064Sume 99955163Sshin if (getsockname(dummy, (struct sockaddr *)&src, &len) < 0) 100055163Sshin err(1, "getsockname"); 100155163Sshin 100255163Sshin close(dummy); 100355163Sshin } 100455163Sshin 100555163Sshin#if defined(SO_SNDBUF) && defined(SO_RCVBUF) 100655163Sshin if (sockbufsize) { 100755163Sshin if (datalen > sockbufsize) 100862627Skris warnx("you need -b to increase socket buffer size"); 100955163Sshin if (setsockopt(s, SOL_SOCKET, SO_SNDBUF, &sockbufsize, 101078064Sume sizeof(sockbufsize)) < 0) 101155163Sshin err(1, "setsockopt(SO_SNDBUF)"); 101255163Sshin if (setsockopt(s, SOL_SOCKET, SO_RCVBUF, &sockbufsize, 101378064Sume sizeof(sockbufsize)) < 0) 101455163Sshin err(1, "setsockopt(SO_RCVBUF)"); 101555163Sshin } 101655163Sshin else { 101755163Sshin if (datalen > 8 * 1024) /*XXX*/ 101855163Sshin warnx("you need -b to increase socket buffer size"); 101955163Sshin /* 102055163Sshin * When pinging the broadcast address, you can get a lot of 102155163Sshin * answers. Doing something so evil is useful if you are trying 102255163Sshin * to stress the ethernet, or just want to fill the arp cache 102355163Sshin * to get some stuff for /etc/ethers. 102455163Sshin */ 102555163Sshin hold = 48 * 1024; 102678064Sume setsockopt(s, SOL_SOCKET, SO_RCVBUF, (char *)&hold, 102778064Sume sizeof(hold)); 102855163Sshin } 102955163Sshin#endif 103055163Sshin 103155163Sshin optval = 1; 103255163Sshin#ifndef USE_SIN6_SCOPE_ID 103362627Skris#ifdef IPV6_RECVPKTINFO 103462627Skris if (setsockopt(s, IPPROTO_IPV6, IPV6_RECVPKTINFO, &optval, 103578064Sume sizeof(optval)) < 0) 103662627Skris warn("setsockopt(IPV6_RECVPKTINFO)"); /* XXX err? */ 103762627Skris#else /* old adv. API */ 103862627Skris if (setsockopt(s, IPPROTO_IPV6, IPV6_PKTINFO, &optval, 103978064Sume sizeof(optval)) < 0) 104062627Skris warn("setsockopt(IPV6_PKTINFO)"); /* XXX err? */ 104155163Sshin#endif 104262627Skris#endif /* USE_SIN6_SCOPE_ID */ 104362627Skris#ifdef IPV6_RECVHOPLIMIT 104462627Skris if (setsockopt(s, IPPROTO_IPV6, IPV6_RECVHOPLIMIT, &optval, 104578064Sume sizeof(optval)) < 0) 104662627Skris warn("setsockopt(IPV6_RECVHOPLIMIT)"); /* XXX err? */ 104762627Skris#else /* old adv. API */ 104862627Skris if (setsockopt(s, IPPROTO_IPV6, IPV6_HOPLIMIT, &optval, 104978064Sume sizeof(optval)) < 0) 105062627Skris warn("setsockopt(IPV6_HOPLIMIT)"); /* XXX err? */ 105162627Skris#endif 105255163Sshin 105378064Sume printf("PING6(%lu=40+8+%lu bytes) ", (unsigned long)(40 + pingerlen()), 105478064Sume (unsigned long)(pingerlen() - 8)); 105578064Sume printf("%s --> ", pr_addr((struct sockaddr *)&src, sizeof(src))); 105678064Sume printf("%s\n", pr_addr((struct sockaddr *)&dst, sizeof(dst))); 105755163Sshin 105855163Sshin while (preload--) /* Fire off them quickies. */ 105978984Sume (void)pinger(); 106055163Sshin 106178064Sume (void)signal(SIGINT, onsignal); 106278064Sume#ifdef SIGINFO 106378064Sume (void)signal(SIGINFO, onsignal); 106478064Sume#endif 106555163Sshin 106655163Sshin if ((options & F_FLOOD) == 0) { 106778064Sume (void)signal(SIGALRM, onsignal); 106878064Sume itimer.it_interval = interval; 106978064Sume itimer.it_value = interval; 107055163Sshin (void)setitimer(ITIMER_REAL, &itimer, NULL); 1071132656Ssuz if (ntransmitted == 0) 107289394Sru retransmit(); 107355163Sshin } 107455163Sshin 1075121472Sume#ifndef HAVE_POLL_H 107678064Sume fdmasks = howmany(s + 1, NFDBITS) * sizeof(fd_mask); 107766811Skris if ((fdmaskp = malloc(fdmasks)) == NULL) 107866811Skris err(1, "malloc"); 1079121472Sume#endif 108066811Skris 1081121472Sume seenalrm = seenint = 0; 108278064Sume#ifdef SIGINFO 108378064Sume seeninfo = 0; 108478064Sume#endif 108578064Sume 108655163Sshin for (;;) { 108755163Sshin struct msghdr m; 108855163Sshin struct iovec iov[2]; 108955163Sshin 109078064Sume /* signal handling */ 109178064Sume if (seenalrm) { 1092179356Sbz /* last packet sent, timeout reached? */ 1093179356Sbz if (npackets && ntransmitted >= npackets) 1094179356Sbz break; 109578064Sume retransmit(); 109678064Sume seenalrm = 0; 109778064Sume continue; 109878064Sume } 109978064Sume if (seenint) { 110078064Sume onint(SIGINT); 110178064Sume seenint = 0; 110278064Sume continue; 110378064Sume } 110478064Sume#ifdef SIGINFO 110578064Sume if (seeninfo) { 110678064Sume summary(); 110778064Sume seeninfo = 0; 110878064Sume continue; 110978064Sume } 111078064Sume#endif 111178064Sume 111255163Sshin if (options & F_FLOOD) { 111378984Sume (void)pinger(); 1114121472Sume#ifdef HAVE_POLL_H 1115121472Sume timeout = 10; 1116121472Sume#else 111766811Skris timeout.tv_sec = 0; 111866811Skris timeout.tv_usec = 10000; 111978064Sume tv = &timeout; 1120121472Sume#endif 1121121472Sume } else { 1122121472Sume#ifdef HAVE_POLL_H 1123121472Sume timeout = INFTIM; 1124121472Sume#else 112578064Sume tv = NULL; 1126121472Sume#endif 1127121472Sume } 1128121472Sume#ifdef HAVE_POLL_H 1129121472Sume fdmaskp[0].fd = s; 1130121472Sume fdmaskp[0].events = POLLIN; 1131121472Sume cc = poll(fdmaskp, 1, timeout); 1132121472Sume#else 113378064Sume memset(fdmaskp, 0, fdmasks); 113478064Sume FD_SET(s, fdmaskp); 113578064Sume cc = select(s + 1, fdmaskp, NULL, NULL, tv); 1136121472Sume#endif 113778064Sume if (cc < 0) { 113878064Sume if (errno != EINTR) { 1139121472Sume#ifdef HAVE_POLL_H 1140121472Sume warn("poll"); 1141121472Sume#else 114278064Sume warn("select"); 1143121472Sume#endif 114478064Sume sleep(1); 114578064Sume } 114678064Sume continue; 114778064Sume } else if (cc == 0) 114878064Sume continue; 114978064Sume 115055163Sshin m.msg_name = (caddr_t)&from; 115155163Sshin m.msg_namelen = sizeof(from); 115255163Sshin memset(&iov, 0, sizeof(iov)); 115355163Sshin iov[0].iov_base = (caddr_t)packet; 115455163Sshin iov[0].iov_len = packlen; 115555163Sshin m.msg_iov = iov; 115655163Sshin m.msg_iovlen = 1; 1157168866Smtm memset(cm, 0, CONTROLLEN); 1158168866Smtm m.msg_control = (void *)cm; 1159168866Smtm m.msg_controllen = CONTROLLEN; 116055163Sshin 116178064Sume cc = recvmsg(s, &m, 0); 116278064Sume if (cc < 0) { 116378064Sume if (errno != EINTR) { 116478064Sume warn("recvmsg"); 116578064Sume sleep(1); 116678064Sume } 116755163Sshin continue; 116878064Sume } else if (cc == 0) { 116978064Sume int mtu; 117078064Sume 117178064Sume /* 117278064Sume * receive control messages only. Process the 1173229778Suqs * exceptions (currently the only possibility is 117478064Sume * a path MTU notification.) 117578064Sume */ 117678064Sume if ((mtu = get_pathmtu(&m)) > 0) { 117778064Sume if ((options & F_VERBOSE) != 0) { 117878064Sume printf("new path MTU (%d) is " 117978064Sume "notified\n", mtu); 118078064Sume } 118178064Sume } 118278064Sume continue; 118378064Sume } else { 118478064Sume /* 118578064Sume * an ICMPv6 message (probably an echoreply) arrived. 118678064Sume */ 118778064Sume pr_pack(packet, cc, &m); 118855163Sshin } 1189173765Sdd if (((options & F_ONCE) != 0 && nreceived > 0) || 1190173765Sdd (npackets > 0 && nreceived >= npackets)) 119155163Sshin break; 1192182276Smatteo if (ntransmitted - nreceived - 1 > nmissedmax) { 1193182276Smatteo nmissedmax = ntransmitted - nreceived - 1; 1194182276Smatteo if (options & F_MISSED) 1195182276Smatteo (void)write(STDOUT_FILENO, &BBELL, 1); 1196182276Smatteo } 119755163Sshin } 119855163Sshin summary(); 1199209236Sbrucec 1200209236Sbrucec if (res != NULL) 1201209236Sbrucec freeaddrinfo(res); 1202209236Sbrucec 1203209236Sbrucec if(packet != NULL) 1204209236Sbrucec free(packet); 1205209236Sbrucec 1206209236Sbrucec#ifndef HAVE_POLL_H 1207209236Sbrucec if(fdmaskp != NULL) 1208209236Sbrucec free(fdmaskp); 1209209236Sbrucec#endif 1210209236Sbrucec 1211179356Sbz exit(nreceived == 0 ? 2 : 0); 121255163Sshin} 121355163Sshin 121478064Sumevoid 1215216561Scharnieronsignal(int sig) 121678064Sume{ 1217121472Sume 121878064Sume switch (sig) { 121978064Sume case SIGALRM: 122078064Sume seenalrm++; 122178064Sume break; 122278064Sume case SIGINT: 122378064Sume seenint++; 122478064Sume break; 122578064Sume#ifdef SIGINFO 122678064Sume case SIGINFO: 122778064Sume seeninfo++; 122878064Sume break; 122978064Sume#endif 123078064Sume } 123178064Sume} 123278064Sume 123355163Sshin/* 123478064Sume * retransmit -- 123555163Sshin * This routine transmits another ping6. 123655163Sshin */ 123755163Sshinvoid 1238216561Scharnierretransmit(void) 123955163Sshin{ 124055163Sshin struct itimerval itimer; 124155163Sshin 124278984Sume if (pinger() == 0) 124355163Sshin return; 124455163Sshin 124555163Sshin /* 124655163Sshin * If we're not transmitting any more packets, change the timer 124755163Sshin * to wait two round-trip times if we've received any packets or 124855163Sshin * ten seconds if we haven't. 124955163Sshin */ 125055163Sshin#define MAXWAIT 10 125155163Sshin if (nreceived) { 125255163Sshin itimer.it_value.tv_sec = 2 * tmax / 1000; 125355163Sshin if (itimer.it_value.tv_sec == 0) 125455163Sshin itimer.it_value.tv_sec = 1; 125555163Sshin } else 125655163Sshin itimer.it_value.tv_sec = MAXWAIT; 125755163Sshin itimer.it_interval.tv_sec = 0; 125855163Sshin itimer.it_interval.tv_usec = 0; 125955163Sshin itimer.it_value.tv_usec = 0; 126055163Sshin 1261179356Sbz (void)signal(SIGALRM, onsignal); 126255163Sshin (void)setitimer(ITIMER_REAL, &itimer, NULL); 126355163Sshin} 126455163Sshin 126555163Sshin/* 126655163Sshin * pinger -- 126755163Sshin * Compose and transmit an ICMP ECHO REQUEST packet. The IP packet 126855163Sshin * will be added on by the kernel. The ID field is our UNIX process ID, 126955163Sshin * and the sequence number is an ascending integer. The first 8 bytes 127055163Sshin * of the data portion are used to hold a UNIX "timeval" struct in VAX 127155163Sshin * byte-order, to compute the round-trip time. 127255163Sshin */ 127378064Sumesize_t 1274216561Scharnierpingerlen(void) 127578064Sume{ 127678064Sume size_t l; 127778064Sume 127878064Sume if (options & F_FQDN) 127978064Sume l = ICMP6_NIQLEN + sizeof(dst.sin6_addr); 128078064Sume else if (options & F_FQDNOLD) 128178064Sume l = ICMP6_NIQLEN; 128278064Sume else if (options & F_NODEADDR) 128378064Sume l = ICMP6_NIQLEN + sizeof(dst.sin6_addr); 128478064Sume else if (options & F_SUPTYPES) 128578064Sume l = ICMP6_NIQLEN; 128678064Sume else 128778064Sume l = ICMP6ECHOLEN + datalen; 128878064Sume 128978064Sume return l; 129078064Sume} 129178064Sume 129278984Sumeint 1293216561Scharnierpinger(void) 129455163Sshin{ 129555163Sshin struct icmp6_hdr *icp; 129655163Sshin struct iovec iov[2]; 129755163Sshin int i, cc; 129878064Sume struct icmp6_nodeinfo *nip; 129978064Sume int seq; 130055163Sshin 130178984Sume if (npackets && ntransmitted >= npackets) 130278984Sume return(-1); /* no more transmission */ 130378984Sume 130455163Sshin icp = (struct icmp6_hdr *)outpack; 130578064Sume nip = (struct icmp6_nodeinfo *)outpack; 130662627Skris memset(icp, 0, sizeof(*icp)); 130755163Sshin icp->icmp6_cksum = 0; 130878064Sume seq = ntransmitted++; 130978064Sume CLR(seq % mx_dup_ck); 131055163Sshin 131155163Sshin if (options & F_FQDN) { 131255163Sshin icp->icmp6_type = ICMP6_NI_QUERY; 131362627Skris icp->icmp6_code = ICMP6_NI_SUBJ_IPV6; 131478064Sume nip->ni_qtype = htons(NI_QTYPE_FQDN); 131578064Sume nip->ni_flags = htons(0); 131678064Sume 131778064Sume memcpy(nip->icmp6_ni_nonce, nonce, 131878064Sume sizeof(nip->icmp6_ni_nonce)); 131978064Sume *(u_int16_t *)nip->icmp6_ni_nonce = ntohs(seq); 132078064Sume 132162627Skris memcpy(&outpack[ICMP6_NIQLEN], &dst.sin6_addr, 132262627Skris sizeof(dst.sin6_addr)); 132362627Skris cc = ICMP6_NIQLEN + sizeof(dst.sin6_addr); 132462627Skris datalen = 0; 132562627Skris } else if (options & F_FQDNOLD) { 132662627Skris /* packet format in 03 draft - no Subject data on queries */ 132762627Skris icp->icmp6_type = ICMP6_NI_QUERY; 132878064Sume icp->icmp6_code = 0; /* code field is always 0 */ 132978064Sume nip->ni_qtype = htons(NI_QTYPE_FQDN); 133078064Sume nip->ni_flags = htons(0); 133178064Sume 133278064Sume memcpy(nip->icmp6_ni_nonce, nonce, 133378064Sume sizeof(nip->icmp6_ni_nonce)); 133478064Sume *(u_int16_t *)nip->icmp6_ni_nonce = ntohs(seq); 133578064Sume 133655163Sshin cc = ICMP6_NIQLEN; 133755163Sshin datalen = 0; 133855163Sshin } else if (options & F_NODEADDR) { 133955163Sshin icp->icmp6_type = ICMP6_NI_QUERY; 134062627Skris icp->icmp6_code = ICMP6_NI_SUBJ_IPV6; 134178064Sume nip->ni_qtype = htons(NI_QTYPE_NODEADDR); 134278064Sume nip->ni_flags = naflags; 134378064Sume 134478064Sume memcpy(nip->icmp6_ni_nonce, nonce, 134578064Sume sizeof(nip->icmp6_ni_nonce)); 134678064Sume *(u_int16_t *)nip->icmp6_ni_nonce = ntohs(seq); 134778064Sume 134862627Skris memcpy(&outpack[ICMP6_NIQLEN], &dst.sin6_addr, 134962627Skris sizeof(dst.sin6_addr)); 135062627Skris cc = ICMP6_NIQLEN + sizeof(dst.sin6_addr); 135155163Sshin datalen = 0; 135278064Sume } else if (options & F_SUPTYPES) { 135378064Sume icp->icmp6_type = ICMP6_NI_QUERY; 135478064Sume icp->icmp6_code = ICMP6_NI_SUBJ_FQDN; /*empty*/ 135578064Sume nip->ni_qtype = htons(NI_QTYPE_SUPTYPES); 135678064Sume /* we support compressed bitmap */ 135778064Sume nip->ni_flags = NI_SUPTYPE_FLAG_COMPRESS; 135878064Sume 135978064Sume memcpy(nip->icmp6_ni_nonce, nonce, 136078064Sume sizeof(nip->icmp6_ni_nonce)); 136178064Sume *(u_int16_t *)nip->icmp6_ni_nonce = ntohs(seq); 136278064Sume cc = ICMP6_NIQLEN; 136378064Sume datalen = 0; 136478064Sume } else { 136555163Sshin icp->icmp6_type = ICMP6_ECHO_REQUEST; 136678064Sume icp->icmp6_code = 0; 136778064Sume icp->icmp6_id = htons(ident); 136878064Sume icp->icmp6_seq = ntohs(seq); 1369121472Sume if (timing) { 1370121472Sume struct timeval tv; 1371121472Sume struct tv32 *tv32; 1372121472Sume (void)gettimeofday(&tv, NULL); 1373121472Sume tv32 = (struct tv32 *)&outpack[ICMP6ECHOLEN]; 1374121472Sume tv32->tv32_sec = htonl(tv.tv_sec); 1375121472Sume tv32->tv32_usec = htonl(tv.tv_usec); 1376121472Sume } 137755163Sshin cc = ICMP6ECHOLEN + datalen; 137855163Sshin } 137955163Sshin 138078064Sume#ifdef DIAGNOSTIC 138178064Sume if (pingerlen() != cc) 138278064Sume errx(1, "internal error; length mismatch"); 138378064Sume#endif 138478064Sume 138555163Sshin smsghdr.msg_name = (caddr_t)&dst; 138655163Sshin smsghdr.msg_namelen = sizeof(dst); 138755163Sshin memset(&iov, 0, sizeof(iov)); 138855163Sshin iov[0].iov_base = (caddr_t)outpack; 138955163Sshin iov[0].iov_len = cc; 139055163Sshin smsghdr.msg_iov = iov; 139155163Sshin smsghdr.msg_iovlen = 1; 139255163Sshin 139355163Sshin i = sendmsg(s, &smsghdr, 0); 139455163Sshin 139555163Sshin if (i < 0 || i != cc) { 139655163Sshin if (i < 0) 139755163Sshin warn("sendmsg"); 139855163Sshin (void)printf("ping6: wrote %s %d chars, ret=%d\n", 139955163Sshin hostname, cc, i); 140055163Sshin } 140155163Sshin if (!(options & F_QUIET) && options & F_FLOOD) 140255163Sshin (void)write(STDOUT_FILENO, &DOT, 1); 140378984Sume 140478984Sume return(0); 140555163Sshin} 140655163Sshin 140778064Sumeint 1408216561Scharniermyechoreply(const struct icmp6_hdr *icp) 140978064Sume{ 141078064Sume if (ntohs(icp->icmp6_id) == ident) 141178064Sume return 1; 141278064Sume else 141378064Sume return 0; 141478064Sume} 141578064Sume 141678064Sumeint 1417216561Scharniermynireply(const struct icmp6_nodeinfo *nip) 141878064Sume{ 141978064Sume if (memcmp(nip->icmp6_ni_nonce + sizeof(u_int16_t), 142078064Sume nonce + sizeof(u_int16_t), 142178064Sume sizeof(nonce) - sizeof(u_int16_t)) == 0) 142278064Sume return 1; 142378064Sume else 142478064Sume return 0; 142578064Sume} 142678064Sume 142778064Sumechar * 1428216561Scharnierdnsdecode(const u_char **sp, const u_char *ep, const u_char *base, char *buf, 1429216561Scharnier size_t bufsiz) 1430216561Scharnier /*base for compressed name*/ 143178064Sume{ 143278064Sume int i; 143378064Sume const u_char *cp; 143478064Sume char cresult[MAXDNAME + 1]; 143578064Sume const u_char *comp; 143678064Sume int l; 143778064Sume 143878064Sume cp = *sp; 143978064Sume *buf = '\0'; 144078064Sume 144178064Sume if (cp >= ep) 144278064Sume return NULL; 144378064Sume while (cp < ep) { 144478064Sume i = *cp; 144578064Sume if (i == 0 || cp != *sp) { 1446121472Sume if (strlcat((char *)buf, ".", bufsiz) >= bufsiz) 144778064Sume return NULL; /*result overrun*/ 144878064Sume } 144978064Sume if (i == 0) 145078064Sume break; 145178064Sume cp++; 145278064Sume 145378064Sume if ((i & 0xc0) == 0xc0 && cp - base > (i & 0x3f)) { 145478064Sume /* DNS compression */ 145578064Sume if (!base) 145678064Sume return NULL; 145778064Sume 145878064Sume comp = base + (i & 0x3f); 145978064Sume if (dnsdecode(&comp, cp, base, cresult, 146078064Sume sizeof(cresult)) == NULL) 146178064Sume return NULL; 146278064Sume if (strlcat(buf, cresult, bufsiz) >= bufsiz) 146378064Sume return NULL; /*result overrun*/ 146478064Sume break; 146578064Sume } else if ((i & 0x3f) == i) { 146678064Sume if (i > ep - cp) 146778064Sume return NULL; /*source overrun*/ 146878064Sume while (i-- > 0 && cp < ep) { 146978064Sume l = snprintf(cresult, sizeof(cresult), 147078064Sume isprint(*cp) ? "%c" : "\\%03o", *cp & 0xff); 1471121472Sume if (l >= sizeof(cresult) || l < 0) 147278064Sume return NULL; 147378064Sume if (strlcat(buf, cresult, bufsiz) >= bufsiz) 147478064Sume return NULL; /*result overrun*/ 147578064Sume cp++; 147678064Sume } 147778064Sume } else 147878064Sume return NULL; /*invalid label*/ 147978064Sume } 148078064Sume if (i != 0) 148178064Sume return NULL; /*not terminated*/ 148278064Sume cp++; 148378064Sume *sp = cp; 148478064Sume return buf; 148578064Sume} 148678064Sume 148755163Sshin/* 148855163Sshin * pr_pack -- 148955163Sshin * Print out the packet, if it came from us. This logic is necessary 149055163Sshin * because ALL readers of the ICMP socket get a copy of ALL ICMP packets 149155163Sshin * which arrive ('tis only fair). This permits multiple copies of this 149255163Sshin * program to be run without having intermingled output (or statistics!). 149355163Sshin */ 149455163Sshinvoid 1495216561Scharnierpr_pack(u_char *buf, int cc, struct msghdr *mhdr) 149655163Sshin{ 149762627Skris#define safeputc(c) printf((isprint((c)) ? "%c" : "\\%03o"), c) 149855163Sshin struct icmp6_hdr *icp; 149978064Sume struct icmp6_nodeinfo *ni; 150055163Sshin int i; 150155163Sshin int hoplim; 150278064Sume struct sockaddr *from; 150378064Sume int fromlen; 150455163Sshin u_char *cp = NULL, *dp, *end = buf + cc; 150562627Skris struct in6_pktinfo *pktinfo = NULL; 1506121472Sume struct timeval tv, tp; 1507121472Sume struct tv32 *tpp; 150855163Sshin double triptime = 0; 150955163Sshin int dupflag; 151055163Sshin size_t off; 151162627Skris int oldfqdn; 151278064Sume u_int16_t seq; 151378064Sume char dnsname[MAXDNAME + 1]; 151455163Sshin 151555163Sshin (void)gettimeofday(&tv, NULL); 151655163Sshin 151778064Sume if (!mhdr || !mhdr->msg_name || 151878064Sume mhdr->msg_namelen != sizeof(struct sockaddr_in6) || 151978064Sume ((struct sockaddr *)mhdr->msg_name)->sa_family != AF_INET6) { 152078064Sume if (options & F_VERBOSE) 1521121472Sume warnx("invalid peername"); 152278064Sume return; 152378064Sume } 152478064Sume from = (struct sockaddr *)mhdr->msg_name; 152578064Sume fromlen = mhdr->msg_namelen; 152655163Sshin if (cc < sizeof(struct icmp6_hdr)) { 152755163Sshin if (options & F_VERBOSE) 152887668Scharnier warnx("packet too short (%d bytes) from %s", cc, 152978064Sume pr_addr(from, fromlen)); 153055163Sshin return; 153155163Sshin } 1532168866Smtm if (((mhdr->msg_flags & MSG_CTRUNC) != 0) && 1533168866Smtm (options & F_VERBOSE) != 0) 1534168866Smtm warnx("some control data discarded, insufficient buffer size"); 153555163Sshin icp = (struct icmp6_hdr *)buf; 153678064Sume ni = (struct icmp6_nodeinfo *)buf; 153755163Sshin off = 0; 153855163Sshin 153955163Sshin if ((hoplim = get_hoplim(mhdr)) == -1) { 154055163Sshin warnx("failed to get receiving hop limit"); 154155163Sshin return; 154255163Sshin } 154362627Skris if ((pktinfo = get_rcvpktinfo(mhdr)) == NULL) { 154487668Scharnier warnx("failed to get receiving packet information"); 154562627Skris return; 154662627Skris } 154755163Sshin 154878064Sume if (icp->icmp6_type == ICMP6_ECHO_REPLY && myechoreply(icp)) { 154978064Sume seq = ntohs(icp->icmp6_seq); 155055163Sshin ++nreceived; 155155163Sshin if (timing) { 1552121472Sume tpp = (struct tv32 *)(icp + 1); 1553121472Sume tp.tv_sec = ntohl(tpp->tv32_sec); 1554121472Sume tp.tv_usec = ntohl(tpp->tv32_usec); 1555121472Sume tvsub(&tv, &tp); 155655163Sshin triptime = ((double)tv.tv_sec) * 1000.0 + 155755163Sshin ((double)tv.tv_usec) / 1000.0; 155855163Sshin tsum += triptime; 155978064Sume tsumsq += triptime * triptime; 156055163Sshin if (triptime < tmin) 156155163Sshin tmin = triptime; 156255163Sshin if (triptime > tmax) 156355163Sshin tmax = triptime; 156455163Sshin } 156555163Sshin 156678064Sume if (TST(seq % mx_dup_ck)) { 156755163Sshin ++nrepeats; 156855163Sshin --nreceived; 156955163Sshin dupflag = 1; 157055163Sshin } else { 157178064Sume SET(seq % mx_dup_ck); 157255163Sshin dupflag = 0; 157355163Sshin } 157455163Sshin 157555163Sshin if (options & F_QUIET) 157655163Sshin return; 157755163Sshin 157855163Sshin if (options & F_FLOOD) 157955163Sshin (void)write(STDOUT_FILENO, &BSPACE, 1); 158055163Sshin else { 1581182195Smatteo if (options & F_AUDIBLE) 1582182195Smatteo (void)write(STDOUT_FILENO, &BBELL, 1); 158355163Sshin (void)printf("%d bytes from %s, icmp_seq=%u", cc, 158478064Sume pr_addr(from, fromlen), seq); 158555163Sshin (void)printf(" hlim=%d", hoplim); 158662627Skris if ((options & F_VERBOSE) != 0) { 158762627Skris struct sockaddr_in6 dstsa; 158862627Skris 158962627Skris memset(&dstsa, 0, sizeof(dstsa)); 159062627Skris dstsa.sin6_family = AF_INET6; 159162627Skris dstsa.sin6_len = sizeof(dstsa); 159262627Skris dstsa.sin6_scope_id = pktinfo->ipi6_ifindex; 159362627Skris dstsa.sin6_addr = pktinfo->ipi6_addr; 159478064Sume (void)printf(" dst=%s", 159578064Sume pr_addr((struct sockaddr *)&dstsa, 159678064Sume sizeof(dstsa))); 159762627Skris } 159855163Sshin if (timing) 1599117824Smaxim (void)printf(" time=%.3f ms", triptime); 160055163Sshin if (dupflag) 160155163Sshin (void)printf("(DUP!)"); 160255163Sshin /* check the data */ 160355163Sshin cp = buf + off + ICMP6ECHOLEN + ICMP6ECHOTMLEN; 160455163Sshin dp = outpack + ICMP6ECHOLEN + ICMP6ECHOTMLEN; 160555163Sshin for (i = 8; cp < end; ++i, ++cp, ++dp) { 160655163Sshin if (*cp != *dp) { 160755163Sshin (void)printf("\nwrong data byte #%d should be 0x%x but was 0x%x", i, *dp, *cp); 160855163Sshin break; 160955163Sshin } 161055163Sshin } 161155163Sshin } 161278064Sume } else if (icp->icmp6_type == ICMP6_NI_REPLY && mynireply(ni)) { 161378064Sume seq = ntohs(*(u_int16_t *)ni->icmp6_ni_nonce); 161478064Sume ++nreceived; 161578064Sume if (TST(seq % mx_dup_ck)) { 161678064Sume ++nrepeats; 161778064Sume --nreceived; 161878064Sume dupflag = 1; 161978064Sume } else { 162078064Sume SET(seq % mx_dup_ck); 162178064Sume dupflag = 0; 162278064Sume } 162355163Sshin 162478064Sume if (options & F_QUIET) 162578064Sume return; 162655163Sshin 162778064Sume (void)printf("%d bytes from %s: ", cc, pr_addr(from, fromlen)); 162878064Sume 162978064Sume switch (ntohs(ni->ni_code)) { 163078064Sume case ICMP6_NI_SUCCESS: 163178064Sume break; 163278064Sume case ICMP6_NI_REFUSED: 163378064Sume printf("refused, type 0x%x", ntohs(ni->ni_type)); 163478064Sume goto fqdnend; 163578064Sume case ICMP6_NI_UNKNOWN: 163678064Sume printf("unknown, type 0x%x", ntohs(ni->ni_type)); 163778064Sume goto fqdnend; 163878064Sume default: 163978064Sume printf("unknown code 0x%x, type 0x%x", 164078064Sume ntohs(ni->ni_code), ntohs(ni->ni_type)); 164178064Sume goto fqdnend; 164278064Sume } 164378064Sume 164478064Sume switch (ntohs(ni->ni_qtype)) { 164578064Sume case NI_QTYPE_NOOP: 164678064Sume printf("NodeInfo NOOP"); 164778064Sume break; 164878064Sume case NI_QTYPE_SUPTYPES: 164978064Sume pr_suptypes(ni, end - (u_char *)ni); 165078064Sume break; 165178064Sume case NI_QTYPE_NODEADDR: 165278064Sume pr_nodeaddr(ni, end - (u_char *)ni); 165378064Sume break; 165478064Sume case NI_QTYPE_FQDN: 165578064Sume default: /* XXX: for backward compatibility */ 165662627Skris cp = (u_char *)ni + ICMP6_NIRLEN; 165762627Skris if (buf[off + ICMP6_NIRLEN] == 165862627Skris cc - off - ICMP6_NIRLEN - 1) 165962627Skris oldfqdn = 1; 166062627Skris else 166162627Skris oldfqdn = 0; 166262627Skris if (oldfqdn) { 166378064Sume cp++; /* skip length */ 166462627Skris while (cp < end) { 166562627Skris safeputc(*cp & 0xff); 166662627Skris cp++; 166762627Skris } 166862627Skris } else { 166978064Sume i = 0; 167062627Skris while (cp < end) { 167178064Sume if (dnsdecode((const u_char **)&cp, end, 167278064Sume (const u_char *)(ni + 1), dnsname, 167378064Sume sizeof(dnsname)) == NULL) { 167478064Sume printf("???"); 167562627Skris break; 167662627Skris } 167778064Sume /* 167878064Sume * name-lookup special handling for 167978064Sume * truncated name 168078064Sume */ 168178064Sume if (cp + 1 <= end && !*cp && 168278064Sume strlen(dnsname) > 0) { 168378064Sume dnsname[strlen(dnsname) - 1] = '\0'; 168478064Sume cp++; 168578064Sume } 168678064Sume printf("%s%s", i > 0 ? "," : "", 168778064Sume dnsname); 168862627Skris } 168962627Skris } 169062627Skris if (options & F_VERBOSE) { 169162627Skris int32_t ttl; 169262627Skris int comma = 0; 169355163Sshin 169462627Skris (void)printf(" ("); /*)*/ 169555163Sshin 169678064Sume switch (ni->ni_code) { 169762627Skris case ICMP6_NI_REFUSED: 169862627Skris (void)printf("refused"); 169962627Skris comma++; 170062627Skris break; 170162627Skris case ICMP6_NI_UNKNOWN: 170287668Scharnier (void)printf("unknown qtype"); 170362627Skris comma++; 170462627Skris break; 170562627Skris } 170655163Sshin 170762627Skris if ((end - (u_char *)ni) < ICMP6_NIRLEN) { 170878064Sume /* case of refusion, unknown */ 170978064Sume /*(*/ 171078064Sume putchar(')'); 171162627Skris goto fqdnend; 171262627Skris } 171362627Skris ttl = (int32_t)ntohl(*(u_long *)&buf[off+ICMP6ECHOLEN+8]); 171462627Skris if (comma) 171562627Skris printf(","); 171678064Sume if (!(ni->ni_flags & NI_FQDN_FLAG_VALIDTTL)) { 171762627Skris (void)printf("TTL=%d:meaningless", 171878064Sume (int)ttl); 171978064Sume } else { 172062627Skris if (ttl < 0) { 172162627Skris (void)printf("TTL=%d:invalid", 172278064Sume ttl); 172362627Skris } else 172462627Skris (void)printf("TTL=%d", ttl); 172562627Skris } 172662627Skris comma++; 172755163Sshin 172862627Skris if (oldfqdn) { 172962627Skris if (comma) 173062627Skris printf(","); 173162627Skris printf("03 draft"); 173262627Skris comma++; 173362627Skris } else { 173462627Skris cp = (u_char *)ni + ICMP6_NIRLEN; 173562627Skris if (cp == end) { 173662627Skris if (comma) 173762627Skris printf(","); 173862627Skris printf("no name"); 173962627Skris comma++; 174062627Skris } 174162627Skris } 174262627Skris 174362627Skris if (buf[off + ICMP6_NIRLEN] != 174462627Skris cc - off - ICMP6_NIRLEN - 1 && oldfqdn) { 174562627Skris if (comma) 174662627Skris printf(","); 174762627Skris (void)printf("invalid namelen:%d/%lu", 174878064Sume buf[off + ICMP6_NIRLEN], 174978064Sume (u_long)cc - off - ICMP6_NIRLEN - 1); 175062627Skris comma++; 175162627Skris } 175262627Skris /*(*/ 175362627Skris putchar(')'); 175462627Skris } 175578064Sume fqdnend: 175662627Skris ; 175755163Sshin } 175855163Sshin } else { 175955163Sshin /* We've got something other than an ECHOREPLY */ 176055163Sshin if (!(options & F_VERBOSE)) 176155163Sshin return; 176278064Sume (void)printf("%d bytes from %s: ", cc, pr_addr(from, fromlen)); 176355163Sshin pr_icmph(icp, end); 176455163Sshin } 176555163Sshin 176655163Sshin if (!(options & F_FLOOD)) { 176755163Sshin (void)putchar('\n'); 176862627Skris if (options & F_VERBOSE) 176962627Skris pr_exthdrs(mhdr); 177055163Sshin (void)fflush(stdout); 177155163Sshin } 177262627Skris#undef safeputc 177355163Sshin} 177455163Sshin 177555163Sshinvoid 1776216561Scharnierpr_exthdrs(struct msghdr *mhdr) 177762627Skris{ 1778168866Smtm ssize_t bufsize; 1779168866Smtm void *bufp; 178062627Skris struct cmsghdr *cm; 178162627Skris 1782168866Smtm bufsize = 0; 1783168866Smtm bufp = mhdr->msg_control; 178462627Skris for (cm = (struct cmsghdr *)CMSG_FIRSTHDR(mhdr); cm; 178562627Skris cm = (struct cmsghdr *)CMSG_NXTHDR(mhdr, cm)) { 178662627Skris if (cm->cmsg_level != IPPROTO_IPV6) 178762627Skris continue; 178862627Skris 1789168866Smtm bufsize = CONTROLLEN - ((caddr_t)CMSG_DATA(cm) - (caddr_t)bufp); 1790168866Smtm if (bufsize <= 0) 1791168866Smtm continue; 179278064Sume switch (cm->cmsg_type) { 179362627Skris case IPV6_HOPOPTS: 179462627Skris printf(" HbH Options: "); 1795168866Smtm pr_ip6opt(CMSG_DATA(cm), (size_t)bufsize); 179662627Skris break; 179762627Skris case IPV6_DSTOPTS: 179862627Skris#ifdef IPV6_RTHDRDSTOPTS 179962627Skris case IPV6_RTHDRDSTOPTS: 180062627Skris#endif 180162627Skris printf(" Dst Options: "); 1802168866Smtm pr_ip6opt(CMSG_DATA(cm), (size_t)bufsize); 180362627Skris break; 180462627Skris case IPV6_RTHDR: 180562627Skris printf(" Routing: "); 1806168866Smtm pr_rthdr(CMSG_DATA(cm), (size_t)bufsize); 180762627Skris break; 180862627Skris } 180962627Skris } 181062627Skris} 181162627Skris 181262627Skris#ifdef USE_RFC2292BIS 181362627Skrisvoid 1814168866Smtmpr_ip6opt(void *extbuf, size_t bufsize) 181562627Skris{ 181662627Skris struct ip6_hbh *ext; 181762627Skris int currentlen; 181862627Skris u_int8_t type; 1819229912Seadler socklen_t extlen, len; 182062627Skris void *databuf; 182162627Skris size_t offset; 182262627Skris u_int16_t value2; 182362627Skris u_int32_t value4; 182462627Skris 182562627Skris ext = (struct ip6_hbh *)extbuf; 182662627Skris extlen = (ext->ip6h_len + 1) * 8; 182778064Sume printf("nxt %u, len %u (%lu bytes)\n", ext->ip6h_nxt, 182878064Sume (unsigned int)ext->ip6h_len, (unsigned long)extlen); 182962627Skris 1830168866Smtm /* 1831168866Smtm * Bounds checking on the ancillary data buffer: 1832168866Smtm * subtract the size of a cmsg structure from the buffer size. 1833168866Smtm */ 1834168866Smtm if (bufsize < (extlen + CMSG_SPACE(0))) { 1835168866Smtm extlen = bufsize - CMSG_SPACE(0); 1836168866Smtm warnx("options truncated, showing only %u (total=%u)", 1837168866Smtm (unsigned int)(extlen / 8 - 1), 1838168866Smtm (unsigned int)(ext->ip6h_len)); 1839168866Smtm } 1840168866Smtm 184162627Skris currentlen = 0; 184262627Skris while (1) { 184362627Skris currentlen = inet6_opt_next(extbuf, extlen, currentlen, 184478064Sume &type, &len, &databuf); 184562627Skris if (currentlen == -1) 184662627Skris break; 184762627Skris switch (type) { 184862627Skris /* 184962627Skris * Note that inet6_opt_next automatically skips any padding 185062627Skris * optins. 185162627Skris */ 185262627Skris case IP6OPT_JUMBO: 185362627Skris offset = 0; 185462627Skris offset = inet6_opt_get_val(databuf, offset, 185578064Sume &value4, sizeof(value4)); 185662627Skris printf(" Jumbo Payload Opt: Length %u\n", 185778064Sume (u_int32_t)ntohl(value4)); 185862627Skris break; 185962627Skris case IP6OPT_ROUTER_ALERT: 186062627Skris offset = 0; 186162627Skris offset = inet6_opt_get_val(databuf, offset, 186262627Skris &value2, sizeof(value2)); 186362627Skris printf(" Router Alert Opt: Type %u\n", 186478064Sume ntohs(value2)); 186562627Skris break; 186662627Skris default: 186778064Sume printf(" Received Opt %u len %lu\n", 186878064Sume type, (unsigned long)len); 186962627Skris break; 187062627Skris } 187162627Skris } 187262627Skris return; 187362627Skris} 187462627Skris#else /* !USE_RFC2292BIS */ 187562627Skris/* ARGSUSED */ 187662627Skrisvoid 1877168866Smtmpr_ip6opt(void *extbuf, size_t bufsize __unused) 187862627Skris{ 187962627Skris putchar('\n'); 188062627Skris return; 188162627Skris} 188262627Skris#endif /* USE_RFC2292BIS */ 188362627Skris 188462627Skris#ifdef USE_RFC2292BIS 188562627Skrisvoid 1886168866Smtmpr_rthdr(void *extbuf, size_t bufsize) 188762627Skris{ 188862627Skris struct in6_addr *in6; 188962627Skris char ntopbuf[INET6_ADDRSTRLEN]; 189062627Skris struct ip6_rthdr *rh = (struct ip6_rthdr *)extbuf; 1891168866Smtm int i, segments, origsegs, rthsize, size0, size1; 189262627Skris 189362627Skris /* print fixed part of the header */ 189462627Skris printf("nxt %u, len %u (%d bytes), type %u, ", rh->ip6r_nxt, 189578064Sume rh->ip6r_len, (rh->ip6r_len + 1) << 3, rh->ip6r_type); 1896168866Smtm if ((segments = inet6_rth_segments(extbuf)) >= 0) { 189762627Skris printf("%d segments, ", segments); 1898168866Smtm printf("%d left\n", rh->ip6r_segleft); 1899168866Smtm } else { 190062627Skris printf("segments unknown, "); 1901168866Smtm printf("%d left\n", rh->ip6r_segleft); 1902168866Smtm return; 1903168866Smtm } 190462627Skris 1905168866Smtm /* 1906168866Smtm * Bounds checking on the ancillary data buffer. When calculating 1907168866Smtm * the number of items to show keep in mind: 1908168866Smtm * - The size of the cmsg structure 1909168866Smtm * - The size of one segment (the size of a Type 0 routing header) 1910168866Smtm * - When dividing add a fudge factor of one in case the 1911168866Smtm * dividend is not evenly divisible by the divisor 1912168866Smtm */ 1913168866Smtm rthsize = (rh->ip6r_len + 1) * 8; 1914168866Smtm if (bufsize < (rthsize + CMSG_SPACE(0))) { 1915168866Smtm origsegs = segments; 1916168866Smtm size0 = inet6_rth_space(IPV6_RTHDR_TYPE_0, 0); 1917168866Smtm size1 = inet6_rth_space(IPV6_RTHDR_TYPE_0, 1); 1918168866Smtm segments -= (rthsize - (bufsize - CMSG_SPACE(0))) / 1919168866Smtm (size1 - size0) + 1; 1920168866Smtm warnx("segments truncated, showing only %d (total=%d)", 1921168866Smtm segments, origsegs); 1922168866Smtm } 1923168866Smtm 192462627Skris for (i = 0; i < segments; i++) { 192562627Skris in6 = inet6_rth_getaddr(extbuf, i); 192662627Skris if (in6 == NULL) 192762627Skris printf(" [%d]<NULL>\n", i); 192878064Sume else { 192978064Sume if (!inet_ntop(AF_INET6, in6, ntopbuf, 193078064Sume sizeof(ntopbuf))) 1931121472Sume strlcpy(ntopbuf, "?", sizeof(ntopbuf)); 193278064Sume printf(" [%d]%s\n", i, ntopbuf); 193378064Sume } 193462627Skris } 193562627Skris 193662627Skris return; 193778064Sume 193862627Skris} 193978064Sume 194062627Skris#else /* !USE_RFC2292BIS */ 194162627Skris/* ARGSUSED */ 194262627Skrisvoid 1943168866Smtmpr_rthdr(void *extbuf, size_t bufsize __unused) 194462627Skris{ 194562627Skris putchar('\n'); 194662627Skris return; 194762627Skris} 194862627Skris#endif /* USE_RFC2292BIS */ 194962627Skris 195078064Sumeint 1951216561Scharnierpr_bitrange(u_int32_t v, int soff, int ii) 195278064Sume{ 195378064Sume int off; 195478064Sume int i; 195562627Skris 195678064Sume off = 0; 195778064Sume while (off < 32) { 195878064Sume /* shift till we have 0x01 */ 195978064Sume if ((v & 0x01) == 0) { 196078064Sume if (ii > 1) 1961121472Sume printf("-%u", soff + off - 1); 196278064Sume ii = 0; 196378064Sume switch (v & 0x0f) { 196478064Sume case 0x00: 196578064Sume v >>= 4; 196678064Sume off += 4; 196778064Sume continue; 196878064Sume case 0x08: 196978064Sume v >>= 3; 197078064Sume off += 3; 197178064Sume continue; 197278064Sume case 0x04: case 0x0c: 197378064Sume v >>= 2; 197478064Sume off += 2; 197578064Sume continue; 197678064Sume default: 197778064Sume v >>= 1; 197878064Sume off += 1; 197978064Sume continue; 198078064Sume } 198178064Sume } 198278064Sume 198378064Sume /* we have 0x01 with us */ 198478064Sume for (i = 0; i < 32 - off; i++) { 198578064Sume if ((v & (0x01 << i)) == 0) 198678064Sume break; 198778064Sume } 198878064Sume if (!ii) 1989121472Sume printf(" %u", soff + off); 199078064Sume ii += i; 199178064Sume v >>= i; off += i; 199278064Sume } 199378064Sume return ii; 199478064Sume} 199578064Sume 199662627Skrisvoid 1997216561Scharnierpr_suptypes(struct icmp6_nodeinfo *ni, size_t nilen) 1998216561Scharnier /* ni->qtype must be SUPTYPES */ 199978064Sume{ 200078064Sume size_t clen; 200178064Sume u_int32_t v; 200278064Sume const u_char *cp, *end; 200378064Sume u_int16_t cur; 200478064Sume struct cbit { 200578064Sume u_int16_t words; /*32bit count*/ 200678064Sume u_int16_t skip; 200778064Sume } cbit; 200878064Sume#define MAXQTYPES (1 << 16) 200978064Sume size_t off; 201078064Sume int b; 201178064Sume 201278064Sume cp = (u_char *)(ni + 1); 201378064Sume end = ((u_char *)ni) + nilen; 201478064Sume cur = 0; 201578064Sume b = 0; 201678064Sume 201778064Sume printf("NodeInfo Supported Qtypes"); 201878064Sume if (options & F_VERBOSE) { 201978064Sume if (ni->ni_flags & NI_SUPTYPE_FLAG_COMPRESS) 202078064Sume printf(", compressed bitmap"); 202178064Sume else 202278064Sume printf(", raw bitmap"); 202378064Sume } 202478064Sume 202578064Sume while (cp < end) { 202678064Sume clen = (size_t)(end - cp); 202778064Sume if ((ni->ni_flags & NI_SUPTYPE_FLAG_COMPRESS) == 0) { 202878064Sume if (clen == 0 || clen > MAXQTYPES / 8 || 202978064Sume clen % sizeof(v)) { 203078064Sume printf("???"); 203178064Sume return; 203278064Sume } 203378064Sume } else { 203478064Sume if (clen < sizeof(cbit) || clen % sizeof(v)) 203578064Sume return; 203678064Sume memcpy(&cbit, cp, sizeof(cbit)); 203778064Sume if (sizeof(cbit) + ntohs(cbit.words) * sizeof(v) > 203878064Sume clen) 203978064Sume return; 204078064Sume cp += sizeof(cbit); 204178064Sume clen = ntohs(cbit.words) * sizeof(v); 204278064Sume if (cur + clen * 8 + (u_long)ntohs(cbit.skip) * 32 > 204378064Sume MAXQTYPES) 204478064Sume return; 204578064Sume } 204678064Sume 204778064Sume for (off = 0; off < clen; off += sizeof(v)) { 204878064Sume memcpy(&v, cp + off, sizeof(v)); 204978064Sume v = (u_int32_t)ntohl(v); 205078064Sume b = pr_bitrange(v, (int)(cur + off * 8), b); 205178064Sume } 205278064Sume /* flush the remaining bits */ 205378064Sume b = pr_bitrange(0, (int)(cur + off * 8), b); 205478064Sume 205578064Sume cp += clen; 205678064Sume cur += clen * 8; 205778064Sume if ((ni->ni_flags & NI_SUPTYPE_FLAG_COMPRESS) != 0) 205878064Sume cur += ntohs(cbit.skip) * 32; 205978064Sume } 206078064Sume} 206178064Sume 206278064Sumevoid 2063216561Scharnierpr_nodeaddr(struct icmp6_nodeinfo *ni, int nilen) 2064216561Scharnier /* ni->qtype must be NODEADDR */ 206555163Sshin{ 206678064Sume u_char *cp = (u_char *)(ni + 1); 206762627Skris char ntop_buf[INET6_ADDRSTRLEN]; 206878064Sume int withttl = 0; 206955163Sshin 207055163Sshin nilen -= sizeof(struct icmp6_nodeinfo); 207155163Sshin 207255163Sshin if (options & F_VERBOSE) { 207378064Sume switch (ni->ni_code) { 207478064Sume case ICMP6_NI_REFUSED: 207578064Sume (void)printf("refused"); 207678064Sume break; 207778064Sume case ICMP6_NI_UNKNOWN: 207878064Sume (void)printf("unknown qtype"); 207978064Sume break; 208055163Sshin } 208178064Sume if (ni->ni_flags & NI_NODEADDR_FLAG_TRUNCATE) 208255163Sshin (void)printf(" truncated"); 208355163Sshin } 208455163Sshin putchar('\n'); 208555163Sshin if (nilen <= 0) 208655163Sshin printf(" no address\n"); 208778064Sume 208878064Sume /* 208978064Sume * In icmp-name-lookups 05 and later, TTL of each returned address 209078064Sume * is contained in the resposne. We try to detect the version 209178064Sume * by the length of the data, but note that the detection algorithm 209278064Sume * is incomplete. We assume the latest draft by default. 209378064Sume */ 209478064Sume if (nilen % (sizeof(u_int32_t) + sizeof(struct in6_addr)) == 0) 209578064Sume withttl = 1; 209678064Sume while (nilen > 0) { 209778064Sume u_int32_t ttl; 209878064Sume 209978064Sume if (withttl) { 210078064Sume /* XXX: alignment? */ 210178064Sume ttl = (u_int32_t)ntohl(*(u_int32_t *)cp); 210278064Sume cp += sizeof(u_int32_t); 210378064Sume nilen -= sizeof(u_int32_t); 210478064Sume } 210578064Sume 210678064Sume if (inet_ntop(AF_INET6, cp, ntop_buf, sizeof(ntop_buf)) == 210778064Sume NULL) 2108121472Sume strlcpy(ntop_buf, "?", sizeof(ntop_buf)); 210978064Sume printf(" %s", ntop_buf); 211078064Sume if (withttl) { 211178064Sume if (ttl == 0xffffffff) { 211278064Sume /* 211378064Sume * XXX: can this convention be applied to all 211478064Sume * type of TTL (i.e. non-ND TTL)? 211578064Sume */ 211678064Sume printf("(TTL=infty)"); 211778064Sume } 211878064Sume else 211978064Sume printf("(TTL=%u)", ttl); 212078064Sume } 212178064Sume putchar('\n'); 212278064Sume 212378064Sume nilen -= sizeof(struct in6_addr); 212478064Sume cp += sizeof(struct in6_addr); 212555163Sshin } 212655163Sshin} 212755163Sshin 212855163Sshinint 2129216561Scharnierget_hoplim(struct msghdr *mhdr) 213055163Sshin{ 213155163Sshin struct cmsghdr *cm; 213255163Sshin 213355163Sshin for (cm = (struct cmsghdr *)CMSG_FIRSTHDR(mhdr); cm; 213455163Sshin cm = (struct cmsghdr *)CMSG_NXTHDR(mhdr, cm)) { 213578064Sume if (cm->cmsg_len == 0) 213678064Sume return(-1); 213778064Sume 213855163Sshin if (cm->cmsg_level == IPPROTO_IPV6 && 213955163Sshin cm->cmsg_type == IPV6_HOPLIMIT && 214055163Sshin cm->cmsg_len == CMSG_LEN(sizeof(int))) 214155163Sshin return(*(int *)CMSG_DATA(cm)); 214255163Sshin } 214355163Sshin 214455163Sshin return(-1); 214555163Sshin} 214655163Sshin 214762627Skrisstruct in6_pktinfo * 2148216561Scharnierget_rcvpktinfo(struct msghdr *mhdr) 214962627Skris{ 215062627Skris struct cmsghdr *cm; 215162627Skris 215262627Skris for (cm = (struct cmsghdr *)CMSG_FIRSTHDR(mhdr); cm; 215362627Skris cm = (struct cmsghdr *)CMSG_NXTHDR(mhdr, cm)) { 215478064Sume if (cm->cmsg_len == 0) 215578064Sume return(NULL); 215678064Sume 215762627Skris if (cm->cmsg_level == IPPROTO_IPV6 && 215862627Skris cm->cmsg_type == IPV6_PKTINFO && 215962627Skris cm->cmsg_len == CMSG_LEN(sizeof(struct in6_pktinfo))) 216062627Skris return((struct in6_pktinfo *)CMSG_DATA(cm)); 216162627Skris } 216262627Skris 216362627Skris return(NULL); 216462627Skris} 216562627Skris 216678064Sumeint 2167216561Scharnierget_pathmtu(struct msghdr *mhdr) 216878064Sume{ 216978064Sume#ifdef IPV6_RECVPATHMTU 217078064Sume struct cmsghdr *cm; 217178064Sume struct ip6_mtuinfo *mtuctl = NULL; 217278064Sume 217378064Sume for (cm = (struct cmsghdr *)CMSG_FIRSTHDR(mhdr); cm; 217478064Sume cm = (struct cmsghdr *)CMSG_NXTHDR(mhdr, cm)) { 217578064Sume if (cm->cmsg_len == 0) 217678064Sume return(0); 217778064Sume 217878064Sume if (cm->cmsg_level == IPPROTO_IPV6 && 217978064Sume cm->cmsg_type == IPV6_PATHMTU && 218078064Sume cm->cmsg_len == CMSG_LEN(sizeof(struct ip6_mtuinfo))) { 218178064Sume mtuctl = (struct ip6_mtuinfo *)CMSG_DATA(cm); 218278064Sume 218378064Sume /* 218478064Sume * If the notified destination is different from 218578064Sume * the one we are pinging, just ignore the info. 218678064Sume * We check the scope ID only when both notified value 218778064Sume * and our own value have non-0 values, because we may 218878064Sume * have used the default scope zone ID for sending, 218978064Sume * in which case the scope ID value is 0. 219078064Sume */ 219178064Sume if (!IN6_ARE_ADDR_EQUAL(&mtuctl->ip6m_addr.sin6_addr, 219278064Sume &dst.sin6_addr) || 219378064Sume (mtuctl->ip6m_addr.sin6_scope_id && 219478064Sume dst.sin6_scope_id && 219578064Sume mtuctl->ip6m_addr.sin6_scope_id != 219678064Sume dst.sin6_scope_id)) { 219778064Sume if ((options & F_VERBOSE) != 0) { 219878064Sume printf("path MTU for %s is notified. " 219978064Sume "(ignored)\n", 220078064Sume pr_addr((struct sockaddr *)&mtuctl->ip6m_addr, 220178064Sume sizeof(mtuctl->ip6m_addr))); 220278064Sume } 220378064Sume return(0); 220478064Sume } 220578064Sume 220678064Sume /* 220778064Sume * Ignore an invalid MTU. XXX: can we just believe 220878064Sume * the kernel check? 220978064Sume */ 221078064Sume if (mtuctl->ip6m_mtu < IPV6_MMTU) 221178064Sume return(0); 221278064Sume 221378064Sume /* notification for our destination. return the MTU. */ 221478064Sume return((int)mtuctl->ip6m_mtu); 221578064Sume } 221678064Sume } 221778064Sume#endif 221878064Sume return(0); 221978064Sume} 222078064Sume 222155163Sshin/* 222255163Sshin * tvsub -- 222355163Sshin * Subtract 2 timeval structs: out = out - in. Out is assumed to 222455163Sshin * be >= in. 222555163Sshin */ 222655163Sshinvoid 2227216561Scharniertvsub(struct timeval *out, struct timeval *in) 222855163Sshin{ 222955163Sshin if ((out->tv_usec -= in->tv_usec) < 0) { 223055163Sshin --out->tv_sec; 223155163Sshin out->tv_usec += 1000000; 223255163Sshin } 223355163Sshin out->tv_sec -= in->tv_sec; 223455163Sshin} 223555163Sshin 223655163Sshin/* 223755163Sshin * onint -- 223855163Sshin * SIGINT handler. 223955163Sshin */ 224062627Skris/* ARGSUSED */ 224155163Sshinvoid 2242216561Scharnieronint(int notused __unused) 224355163Sshin{ 224455163Sshin summary(); 224555163Sshin 2246209236Sbrucec if (res != NULL) 2247209236Sbrucec freeaddrinfo(res); 2248209236Sbrucec 2249209236Sbrucec if(packet != NULL) 2250209236Sbrucec free(packet); 2251209236Sbrucec 2252209236Sbrucec#ifndef HAVE_POLL_H 2253209236Sbrucec if(fdmaskp != NULL) 2254209236Sbrucec free(fdmaskp); 2255209236Sbrucec#endif 2256209236Sbrucec 2257121472Sume (void)signal(SIGINT, SIG_DFL); 2258121472Sume (void)kill(getpid(), SIGINT); 2259121472Sume 2260121472Sume /* NOTREACHED */ 2261121472Sume exit(1); 226255163Sshin} 226355163Sshin 226455163Sshin/* 226555163Sshin * summary -- 226655163Sshin * Print out statistics. 226755163Sshin */ 226855163Sshinvoid 2269216561Scharniersummary(void) 227055163Sshin{ 227155163Sshin 227255163Sshin (void)printf("\n--- %s ping6 statistics ---\n", hostname); 227355163Sshin (void)printf("%ld packets transmitted, ", ntransmitted); 227455163Sshin (void)printf("%ld packets received, ", nreceived); 227555163Sshin if (nrepeats) 227655163Sshin (void)printf("+%ld duplicates, ", nrepeats); 227755163Sshin if (ntransmitted) { 227855163Sshin if (nreceived > ntransmitted) 2279121472Sume (void)printf("-- somebody's duplicating packets!"); 228055163Sshin else 2281121472Sume (void)printf("%.1f%% packet loss", 2282121472Sume ((((double)ntransmitted - nreceived) * 100.0) / 228355163Sshin ntransmitted)); 228455163Sshin } 228555163Sshin (void)putchar('\n'); 228655163Sshin if (nreceived && timing) { 228755163Sshin /* Only display average to microseconds */ 228878064Sume double num = nreceived + nrepeats; 228978064Sume double avg = tsum / num; 229078064Sume double dev = sqrt(tsumsq / num - avg * avg); 229178064Sume (void)printf( 229278064Sume "round-trip min/avg/max/std-dev = %.3f/%.3f/%.3f/%.3f ms\n", 229378064Sume tmin, avg, tmax, dev); 229455163Sshin (void)fflush(stdout); 229555163Sshin } 229678064Sume (void)fflush(stdout); 229755163Sshin} 229855163Sshin 229978064Sume/*subject type*/ 2300121472Sumestatic const char *niqcode[] = { 230178064Sume "IPv6 address", 230278064Sume "DNS label", /*or empty*/ 230378064Sume "IPv4 address", 230455163Sshin}; 230555163Sshin 230678064Sume/*result code*/ 2307121472Sumestatic const char *nircode[] = { 230878064Sume "Success", "Refused", "Unknown", 230978064Sume}; 231078064Sume 231178064Sume 231255163Sshin/* 231355163Sshin * pr_icmph -- 231455163Sshin * Print a descriptive string about an ICMP header. 231555163Sshin */ 231655163Sshinvoid 2317216561Scharnierpr_icmph(struct icmp6_hdr *icp, u_char *end) 231855163Sshin{ 231962627Skris char ntop_buf[INET6_ADDRSTRLEN]; 232078064Sume struct nd_redirect *red; 232178064Sume struct icmp6_nodeinfo *ni; 232278064Sume char dnsname[MAXDNAME + 1]; 232378064Sume const u_char *cp; 232478064Sume size_t l; 232562627Skris 232678064Sume switch (icp->icmp6_type) { 232755163Sshin case ICMP6_DST_UNREACH: 232878064Sume switch (icp->icmp6_code) { 232955163Sshin case ICMP6_DST_UNREACH_NOROUTE: 233055163Sshin (void)printf("No Route to Destination\n"); 233155163Sshin break; 233255163Sshin case ICMP6_DST_UNREACH_ADMIN: 233355163Sshin (void)printf("Destination Administratively " 233478064Sume "Unreachable\n"); 233555163Sshin break; 233655163Sshin case ICMP6_DST_UNREACH_BEYONDSCOPE: 233755163Sshin (void)printf("Destination Unreachable Beyond Scope\n"); 233855163Sshin break; 233955163Sshin case ICMP6_DST_UNREACH_ADDR: 234055163Sshin (void)printf("Destination Host Unreachable\n"); 234155163Sshin break; 234255163Sshin case ICMP6_DST_UNREACH_NOPORT: 234355163Sshin (void)printf("Destination Port Unreachable\n"); 234455163Sshin break; 234555163Sshin default: 234655163Sshin (void)printf("Destination Unreachable, Bad Code: %d\n", 234755163Sshin icp->icmp6_code); 234855163Sshin break; 234955163Sshin } 235055163Sshin /* Print returned IP header information */ 235155163Sshin pr_retip((struct ip6_hdr *)(icp + 1), end); 235255163Sshin break; 235355163Sshin case ICMP6_PACKET_TOO_BIG: 235455163Sshin (void)printf("Packet too big mtu = %d\n", 235578064Sume (int)ntohl(icp->icmp6_mtu)); 235662627Skris pr_retip((struct ip6_hdr *)(icp + 1), end); 235755163Sshin break; 235855163Sshin case ICMP6_TIME_EXCEEDED: 235978064Sume switch (icp->icmp6_code) { 236055163Sshin case ICMP6_TIME_EXCEED_TRANSIT: 236155163Sshin (void)printf("Time to live exceeded\n"); 236255163Sshin break; 236355163Sshin case ICMP6_TIME_EXCEED_REASSEMBLY: 236455163Sshin (void)printf("Frag reassembly time exceeded\n"); 236555163Sshin break; 236655163Sshin default: 236755163Sshin (void)printf("Time exceeded, Bad Code: %d\n", 236855163Sshin icp->icmp6_code); 236955163Sshin break; 237055163Sshin } 237155163Sshin pr_retip((struct ip6_hdr *)(icp + 1), end); 237255163Sshin break; 237355163Sshin case ICMP6_PARAM_PROB: 237455163Sshin (void)printf("Parameter problem: "); 237578064Sume switch (icp->icmp6_code) { 237678064Sume case ICMP6_PARAMPROB_HEADER: 237778064Sume (void)printf("Erroneous Header "); 237878064Sume break; 237978064Sume case ICMP6_PARAMPROB_NEXTHEADER: 238078064Sume (void)printf("Unknown Nextheader "); 238178064Sume break; 238278064Sume case ICMP6_PARAMPROB_OPTION: 238378064Sume (void)printf("Unrecognized Option "); 238478064Sume break; 238578064Sume default: 238678064Sume (void)printf("Bad code(%d) ", icp->icmp6_code); 238778064Sume break; 238855163Sshin } 238955163Sshin (void)printf("pointer = 0x%02x\n", 239078064Sume (u_int32_t)ntohl(icp->icmp6_pptr)); 239155163Sshin pr_retip((struct ip6_hdr *)(icp + 1), end); 239255163Sshin break; 239355163Sshin case ICMP6_ECHO_REQUEST: 239462627Skris (void)printf("Echo Request"); 239555163Sshin /* XXX ID + Seq + Data */ 239655163Sshin break; 239755163Sshin case ICMP6_ECHO_REPLY: 239862627Skris (void)printf("Echo Reply"); 239955163Sshin /* XXX ID + Seq + Data */ 240055163Sshin break; 240155163Sshin case ICMP6_MEMBERSHIP_QUERY: 240262627Skris (void)printf("Listener Query"); 240355163Sshin break; 240455163Sshin case ICMP6_MEMBERSHIP_REPORT: 240562627Skris (void)printf("Listener Report"); 240655163Sshin break; 240755163Sshin case ICMP6_MEMBERSHIP_REDUCTION: 240862627Skris (void)printf("Listener Done"); 240955163Sshin break; 241055163Sshin case ND_ROUTER_SOLICIT: 241162627Skris (void)printf("Router Solicitation"); 241255163Sshin break; 241355163Sshin case ND_ROUTER_ADVERT: 241462627Skris (void)printf("Router Advertisement"); 241555163Sshin break; 241655163Sshin case ND_NEIGHBOR_SOLICIT: 241762627Skris (void)printf("Neighbor Solicitation"); 241855163Sshin break; 241955163Sshin case ND_NEIGHBOR_ADVERT: 242062627Skris (void)printf("Neighbor Advertisement"); 242155163Sshin break; 242255163Sshin case ND_REDIRECT: 242378064Sume red = (struct nd_redirect *)icp; 242455163Sshin (void)printf("Redirect\n"); 242578064Sume if (!inet_ntop(AF_INET6, &red->nd_rd_dst, ntop_buf, 242678064Sume sizeof(ntop_buf))) 2427121472Sume strlcpy(ntop_buf, "?", sizeof(ntop_buf)); 242878064Sume (void)printf("Destination: %s", ntop_buf); 242978064Sume if (!inet_ntop(AF_INET6, &red->nd_rd_target, ntop_buf, 243078064Sume sizeof(ntop_buf))) 2431121472Sume strlcpy(ntop_buf, "?", sizeof(ntop_buf)); 243278064Sume (void)printf(" New Target: %s", ntop_buf); 243355163Sshin break; 243455163Sshin case ICMP6_NI_QUERY: 243562627Skris (void)printf("Node Information Query"); 243655163Sshin /* XXX ID + Seq + Data */ 243778064Sume ni = (struct icmp6_nodeinfo *)icp; 243878064Sume l = end - (u_char *)(ni + 1); 243978064Sume printf(", "); 244078064Sume switch (ntohs(ni->ni_qtype)) { 244178064Sume case NI_QTYPE_NOOP: 244278064Sume (void)printf("NOOP"); 244378064Sume break; 244478064Sume case NI_QTYPE_SUPTYPES: 244578064Sume (void)printf("Supported qtypes"); 244678064Sume break; 244778064Sume case NI_QTYPE_FQDN: 244878064Sume (void)printf("DNS name"); 244978064Sume break; 245078064Sume case NI_QTYPE_NODEADDR: 245178064Sume (void)printf("nodeaddr"); 245278064Sume break; 245378064Sume case NI_QTYPE_IPV4ADDR: 245478064Sume (void)printf("IPv4 nodeaddr"); 245578064Sume break; 245678064Sume default: 245778064Sume (void)printf("unknown qtype"); 245878064Sume break; 245978064Sume } 246078064Sume if (options & F_VERBOSE) { 246178064Sume switch (ni->ni_code) { 246278064Sume case ICMP6_NI_SUBJ_IPV6: 246378064Sume if (l == sizeof(struct in6_addr) && 246478064Sume inet_ntop(AF_INET6, ni + 1, ntop_buf, 246578064Sume sizeof(ntop_buf)) != NULL) { 246678064Sume (void)printf(", subject=%s(%s)", 246778064Sume niqcode[ni->ni_code], ntop_buf); 246878064Sume } else { 246978064Sume#if 1 247078064Sume /* backward compat to -W */ 247178064Sume (void)printf(", oldfqdn"); 247278064Sume#else 247378064Sume (void)printf(", invalid"); 247478064Sume#endif 247578064Sume } 247678064Sume break; 247778064Sume case ICMP6_NI_SUBJ_FQDN: 247878064Sume if (end == (u_char *)(ni + 1)) { 247978064Sume (void)printf(", no subject"); 248078064Sume break; 248178064Sume } 248278064Sume printf(", subject=%s", niqcode[ni->ni_code]); 248378064Sume cp = (const u_char *)(ni + 1); 248478064Sume if (dnsdecode(&cp, end, NULL, dnsname, 248578064Sume sizeof(dnsname)) != NULL) 248678064Sume printf("(%s)", dnsname); 248778064Sume else 248878064Sume printf("(invalid)"); 248978064Sume break; 249078064Sume case ICMP6_NI_SUBJ_IPV4: 249178064Sume if (l == sizeof(struct in_addr) && 249278064Sume inet_ntop(AF_INET, ni + 1, ntop_buf, 249378064Sume sizeof(ntop_buf)) != NULL) { 249478064Sume (void)printf(", subject=%s(%s)", 249578064Sume niqcode[ni->ni_code], ntop_buf); 249678064Sume } else 249778064Sume (void)printf(", invalid"); 249878064Sume break; 249978064Sume default: 250078064Sume (void)printf(", invalid"); 250178064Sume break; 250278064Sume } 250378064Sume } 250455163Sshin break; 250555163Sshin case ICMP6_NI_REPLY: 250662627Skris (void)printf("Node Information Reply"); 250755163Sshin /* XXX ID + Seq + Data */ 250878064Sume ni = (struct icmp6_nodeinfo *)icp; 250978064Sume printf(", "); 251078064Sume switch (ntohs(ni->ni_qtype)) { 251178064Sume case NI_QTYPE_NOOP: 251278064Sume (void)printf("NOOP"); 251378064Sume break; 251478064Sume case NI_QTYPE_SUPTYPES: 251578064Sume (void)printf("Supported qtypes"); 251678064Sume break; 251778064Sume case NI_QTYPE_FQDN: 251878064Sume (void)printf("DNS name"); 251978064Sume break; 252078064Sume case NI_QTYPE_NODEADDR: 252178064Sume (void)printf("nodeaddr"); 252278064Sume break; 252378064Sume case NI_QTYPE_IPV4ADDR: 252478064Sume (void)printf("IPv4 nodeaddr"); 252578064Sume break; 252678064Sume default: 252778064Sume (void)printf("unknown qtype"); 252878064Sume break; 252978064Sume } 253078064Sume if (options & F_VERBOSE) { 253178064Sume if (ni->ni_code > sizeof(nircode) / sizeof(nircode[0])) 253278064Sume printf(", invalid"); 253378064Sume else 253478064Sume printf(", %s", nircode[ni->ni_code]); 253578064Sume } 253655163Sshin break; 253755163Sshin default: 253862627Skris (void)printf("Bad ICMP type: %d", icp->icmp6_type); 253955163Sshin } 254055163Sshin} 254155163Sshin 254255163Sshin/* 254355163Sshin * pr_iph -- 254455163Sshin * Print an IP6 header. 254555163Sshin */ 254655163Sshinvoid 2547216561Scharnierpr_iph(struct ip6_hdr *ip6) 254855163Sshin{ 254955163Sshin u_int32_t flow = ip6->ip6_flow & IPV6_FLOWLABEL_MASK; 255055163Sshin u_int8_t tc; 255162627Skris char ntop_buf[INET6_ADDRSTRLEN]; 255255163Sshin 255355163Sshin tc = *(&ip6->ip6_vfc + 1); /* XXX */ 255455163Sshin tc = (tc >> 4) & 0x0f; 255555163Sshin tc |= (ip6->ip6_vfc << 4); 255655163Sshin 255755163Sshin printf("Vr TC Flow Plen Nxt Hlim\n"); 255855163Sshin printf(" %1x %02x %05x %04x %02x %02x\n", 255978064Sume (ip6->ip6_vfc & IPV6_VERSION_MASK) >> 4, tc, (u_int32_t)ntohl(flow), 256078064Sume ntohs(ip6->ip6_plen), ip6->ip6_nxt, ip6->ip6_hlim); 256178064Sume if (!inet_ntop(AF_INET6, &ip6->ip6_src, ntop_buf, sizeof(ntop_buf))) 2562121472Sume strlcpy(ntop_buf, "?", sizeof(ntop_buf)); 256378064Sume printf("%s->", ntop_buf); 256478064Sume if (!inet_ntop(AF_INET6, &ip6->ip6_dst, ntop_buf, sizeof(ntop_buf))) 2565121472Sume strlcpy(ntop_buf, "?", sizeof(ntop_buf)); 256678064Sume printf("%s\n", ntop_buf); 256755163Sshin} 256855163Sshin 256955163Sshin/* 257055163Sshin * pr_addr -- 257155163Sshin * Return an ascii host address as a dotted quad and optionally with 257255163Sshin * a hostname. 257355163Sshin */ 257462627Skrisconst char * 2575216561Scharnierpr_addr(struct sockaddr *addr, int addrlen) 257655163Sshin{ 257778064Sume static char buf[NI_MAXHOST]; 2578121316Sume int flag = 0; 257955163Sshin 258062627Skris if ((options & F_HOSTNAME) == 0) 258155163Sshin flag |= NI_NUMERICHOST; 258255163Sshin 258378064Sume if (getnameinfo(addr, addrlen, buf, sizeof(buf), NULL, 0, flag) == 0) 258478064Sume return (buf); 258578064Sume else 258678064Sume return "?"; 258755163Sshin} 258855163Sshin 258955163Sshin/* 259055163Sshin * pr_retip -- 259155163Sshin * Dump some info on a returned (via ICMPv6) IPv6 packet. 259255163Sshin */ 259355163Sshinvoid 2594216561Scharnierpr_retip(struct ip6_hdr *ip6, u_char *end) 259555163Sshin{ 259655163Sshin u_char *cp = (u_char *)ip6, nh; 259755163Sshin int hlen; 259855163Sshin 259955163Sshin if (end - (u_char *)ip6 < sizeof(*ip6)) { 260055163Sshin printf("IP6"); 260155163Sshin goto trunc; 260255163Sshin } 260355163Sshin pr_iph(ip6); 260455163Sshin hlen = sizeof(*ip6); 260555163Sshin 260655163Sshin nh = ip6->ip6_nxt; 260755163Sshin cp += hlen; 260855163Sshin while (end - cp >= 8) { 260955163Sshin switch (nh) { 261078064Sume case IPPROTO_HOPOPTS: 261178064Sume printf("HBH "); 261278064Sume hlen = (((struct ip6_hbh *)cp)->ip6h_len+1) << 3; 261378064Sume nh = ((struct ip6_hbh *)cp)->ip6h_nxt; 261478064Sume break; 261578064Sume case IPPROTO_DSTOPTS: 261678064Sume printf("DSTOPT "); 261778064Sume hlen = (((struct ip6_dest *)cp)->ip6d_len+1) << 3; 261878064Sume nh = ((struct ip6_dest *)cp)->ip6d_nxt; 261978064Sume break; 262078064Sume case IPPROTO_FRAGMENT: 262178064Sume printf("FRAG "); 262278064Sume hlen = sizeof(struct ip6_frag); 262378064Sume nh = ((struct ip6_frag *)cp)->ip6f_nxt; 262478064Sume break; 262578064Sume case IPPROTO_ROUTING: 262678064Sume printf("RTHDR "); 262778064Sume hlen = (((struct ip6_rthdr *)cp)->ip6r_len+1) << 3; 262878064Sume nh = ((struct ip6_rthdr *)cp)->ip6r_nxt; 262978064Sume break; 263055163Sshin#ifdef IPSEC 263178064Sume case IPPROTO_AH: 263278064Sume printf("AH "); 263378064Sume hlen = (((struct ah *)cp)->ah_len+2) << 2; 263478064Sume nh = ((struct ah *)cp)->ah_nxt; 263578064Sume break; 263655163Sshin#endif 263778064Sume case IPPROTO_ICMPV6: 263878064Sume printf("ICMP6: type = %d, code = %d\n", 263978064Sume *cp, *(cp + 1)); 264078064Sume return; 264178064Sume case IPPROTO_ESP: 264278064Sume printf("ESP\n"); 264378064Sume return; 264478064Sume case IPPROTO_TCP: 264578064Sume printf("TCP: from port %u, to port %u (decimal)\n", 264678064Sume (*cp * 256 + *(cp + 1)), 264778064Sume (*(cp + 2) * 256 + *(cp + 3))); 264878064Sume return; 264978064Sume case IPPROTO_UDP: 265078064Sume printf("UDP: from port %u, to port %u (decimal)\n", 265178064Sume (*cp * 256 + *(cp + 1)), 265278064Sume (*(cp + 2) * 256 + *(cp + 3))); 265378064Sume return; 265478064Sume default: 265578064Sume printf("Unknown Header(%d)\n", nh); 265678064Sume return; 265755163Sshin } 265855163Sshin 265955163Sshin if ((cp += hlen) >= end) 266055163Sshin goto trunc; 266155163Sshin } 266255163Sshin if (end - cp < 8) 266355163Sshin goto trunc; 266455163Sshin 266555163Sshin putchar('\n'); 266655163Sshin return; 266755163Sshin 266855163Sshin trunc: 266955163Sshin printf("...\n"); 267055163Sshin return; 267155163Sshin} 267255163Sshin 267355163Sshinvoid 2674216561Scharnierfill(char *bp, char *patp) 267555163Sshin{ 267692806Sobrien int ii, jj, kk; 267755163Sshin int pat[16]; 267855163Sshin char *cp; 267955163Sshin 268055163Sshin for (cp = patp; *cp; cp++) 268155163Sshin if (!isxdigit(*cp)) 268255163Sshin errx(1, "patterns must be specified as hex digits"); 268355163Sshin ii = sscanf(patp, 268455163Sshin "%2x%2x%2x%2x%2x%2x%2x%2x%2x%2x%2x%2x%2x%2x%2x%2x", 268555163Sshin &pat[0], &pat[1], &pat[2], &pat[3], &pat[4], &pat[5], &pat[6], 268655163Sshin &pat[7], &pat[8], &pat[9], &pat[10], &pat[11], &pat[12], 268755163Sshin &pat[13], &pat[14], &pat[15]); 268855163Sshin 268978064Sume/* xxx */ 269055163Sshin if (ii > 0) 269155163Sshin for (kk = 0; 2692121472Sume kk <= MAXDATALEN - (8 + sizeof(struct tv32) + ii); 269355163Sshin kk += ii) 269455163Sshin for (jj = 0; jj < ii; ++jj) 269555163Sshin bp[jj + kk] = pat[jj]; 269655163Sshin if (!(options & F_QUIET)) { 269755163Sshin (void)printf("PATTERN: 0x"); 269855163Sshin for (jj = 0; jj < ii; ++jj) 269955163Sshin (void)printf("%02x", bp[jj] & 0xFF); 270055163Sshin (void)printf("\n"); 270155163Sshin } 270255163Sshin} 270355163Sshin 270455163Sshin#ifdef IPSEC 270555163Sshin#ifdef IPSEC_POLICY_IPSEC 270655163Sshinint 2707216561Scharniersetpolicy(int so __unused, char *policy) 270855163Sshin{ 270955163Sshin char *buf; 271055163Sshin 271155163Sshin if (policy == NULL) 271255163Sshin return 0; /* ignore */ 271355163Sshin 271455163Sshin buf = ipsec_set_policy(policy, strlen(policy)); 271555163Sshin if (buf == NULL) 271664277Skris errx(1, "%s", ipsec_strerror()); 271778064Sume if (setsockopt(s, IPPROTO_IPV6, IPV6_IPSEC_POLICY, buf, 271878064Sume ipsec_get_policylen(buf)) < 0) 2719121472Sume warnx("Unable to set IPsec policy"); 272055163Sshin free(buf); 272155163Sshin 272255163Sshin return 0; 272355163Sshin} 272455163Sshin#endif 272555163Sshin#endif 272655163Sshin 272762627Skrischar * 2728250251Shrsnigroup(char *name, int nig_oldmcprefix) 272962627Skris{ 273062627Skris char *p; 2731121472Sume char *q; 273262627Skris MD5_CTX ctxt; 273362627Skris u_int8_t digest[16]; 273478064Sume u_int8_t c; 273578064Sume size_t l; 273662627Skris char hbuf[NI_MAXHOST]; 273762627Skris struct in6_addr in6; 2738250251Shrs int valid; 273962627Skris 274078064Sume p = strchr(name, '.'); 274178064Sume if (!p) 274278064Sume p = name + strlen(name); 274378064Sume l = p - name; 274478064Sume if (l > 63 || l > sizeof(hbuf) - 1) 274562627Skris return NULL; /*label too long*/ 274678064Sume strncpy(hbuf, name, l); 274778064Sume hbuf[(int)l] = '\0'; 274862627Skris 274978064Sume for (q = name; *q; q++) { 2750121472Sume if (isupper(*(unsigned char *)q)) 2751121472Sume *q = tolower(*(unsigned char *)q); 275278064Sume } 275378064Sume 2754250251Shrs /* generate 16 bytes of pseudo-random value. */ 2755121472Sume memset(&ctxt, 0, sizeof(ctxt)); 275662627Skris MD5Init(&ctxt); 275778064Sume c = l & 0xff; 275878064Sume MD5Update(&ctxt, &c, sizeof(c)); 2759121472Sume MD5Update(&ctxt, (unsigned char *)name, l); 276062627Skris MD5Final(digest, &ctxt); 276162627Skris 2762250251Shrs if (nig_oldmcprefix) { 2763250251Shrs /* draft-ietf-ipngwg-icmp-name-lookup */ 2764250251Shrs valid = inet_pton(AF_INET6, "ff02::2:0000:0000", &in6); 2765250251Shrs } else { 2766250251Shrs /* RFC 4620 */ 2767250251Shrs valid = inet_pton(AF_INET6, "ff02::2:ff00:0000", &in6); 2768250251Shrs } 2769250251Shrs if (valid != 1) 277078064Sume return NULL; /*XXX*/ 2771250251Shrs 2772250251Shrs if (nig_oldmcprefix) { 2773250251Shrs /* draft-ietf-ipngwg-icmp-name-lookup */ 2774250251Shrs bcopy(digest, &in6.s6_addr[12], 4); 2775250251Shrs } else { 2776250251Shrs /* RFC 4620 */ 2777250251Shrs bcopy(digest, &in6.s6_addr[13], 3); 2778250251Shrs } 277962627Skris 278062627Skris if (inet_ntop(AF_INET6, &in6, hbuf, sizeof(hbuf)) == NULL) 278162627Skris return NULL; 278262627Skris 278362627Skris return strdup(hbuf); 278462627Skris} 278562627Skris 278655163Sshinvoid 2787216561Scharnierusage(void) 278855163Sshin{ 278955163Sshin (void)fprintf(stderr, 2790141611Sru#if defined(IPSEC) && !defined(IPSEC_POLICY_IPSEC) 2791141611Sru "A" 2792141611Sru#endif 2793141611Sru "usage: ping6 [-" 2794206889Smaxim "Dd" 2795141611Sru#if defined(IPSEC) && !defined(IPSEC_POLICY_IPSEC) 2796141611Sru "E" 2797141611Sru#endif 2798141611Sru "fH" 279978990Sume#ifdef IPV6_USE_MIN_MTU 280078990Sume "m" 280178990Sume#endif 2802182276Smatteo "nNoqrRtvwW] " 2803141611Sru "[-a addrtype] [-b bufsiz] [-c count] [-g gateway]\n" 2804141611Sru " [-h hoplimit] [-I interface] [-i wait] [-l preload]" 2805141611Sru#if defined(IPSEC) && defined(IPSEC_POLICY_IPSEC) 2806141611Sru " [-P policy]" 280755163Sshin#endif 2808141611Sru "\n" 2809141611Sru " [-p pattern] [-S sourceaddr] [-s packetsize] " 2810141611Sru "[hops ...] host\n"); 281155163Sshin exit(1); 281255163Sshin} 2813