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>
128285820Shrs#include <sysexits.h>
12955163Sshin#include <unistd.h>
13055163Sshin
13155163Sshin#ifdef IPSEC
132171135Sgnn#include <netipsec/ah.h>
133171135Sgnn#include <netipsec/ipsec.h>
13455163Sshin#endif
13555163Sshin
13662627Skris#include <md5.h>
13762627Skris
138121472Sumestruct tv32 {
139121472Sume	u_int32_t tv32_sec;
140121472Sume	u_int32_t tv32_usec;
141121472Sume};
142121472Sume
14362627Skris#define MAXPACKETLEN	131072
14455163Sshin#define	IP6LEN		40
145121472Sume#define ICMP6ECHOLEN	8	/* icmp echo header len excluding time */
146121472Sume#define ICMP6ECHOTMLEN sizeof(struct tv32)
14762627Skris#define ICMP6_NIQLEN	(ICMP6ECHOLEN + 8)
148168866Smtm# define CONTROLLEN	10240	/* ancillary data buffer size RFC3542 20.1 */
14978064Sume/* FQDN case, 64 bits of nonce + 32 bits ttl */
15078064Sume#define ICMP6_NIRLEN	(ICMP6ECHOLEN + 12)
15155163Sshin#define	EXTRA		256	/* for AH and various other headers. weird. */
15255163Sshin#define	DEFDATALEN	ICMP6ECHOTMLEN
15362627Skris#define MAXDATALEN	MAXPACKETLEN - IP6LEN - ICMP6ECHOLEN
15455163Sshin#define	NROUTES		9		/* number of record route slots */
155285820Shrs#define	MAXWAIT		10000		/* max ms to wait for response */
156285820Shrs#define	MAXALARM	(60 * 60)	/* max seconds for alarm timeout */
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)
193285820Shrs#define	F_WAITTIME	0x2000000
19455163Sshinu_int options;
19555163Sshin
19662627Skris#define IN6LEN		sizeof(struct in6_addr)
19762627Skris#define SA6LEN		sizeof(struct sockaddr_in6)
19878064Sume#define DUMMY_PORT	10101
19955163Sshin
20078064Sume#define SIN6(s)	((struct sockaddr_in6 *)(s))
20155163Sshin
20255163Sshin/*
20355163Sshin * MAX_DUP_CHK is the number of bits in received table, i.e. the maximum
20455163Sshin * number of received sequence numbers we can keep track of.  Change 128
20555163Sshin * to 8192 for complete accuracy...
20655163Sshin */
20755163Sshin#define	MAX_DUP_CHK	(8 * 8192)
20855163Sshinint mx_dup_ck = MAX_DUP_CHK;
20955163Sshinchar rcvd_tbl[MAX_DUP_CHK / 8];
21055163Sshin
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;
22655163Sshin
22755163Sshin/* counters */
228182276Smatteolong nmissedmax;		/* max value of ntransmitted - nreceived - 1 */
22955163Sshinlong npackets;			/* max packets to transmit */
23055163Sshinlong nreceived;			/* # of packets we got back */
23155163Sshinlong nrepeats;			/* number of duplicates */
23255163Sshinlong ntransmitted;		/* sequence # for outbound packets = #sent */
233285820Shrsint interval = 1000;		/* interval between packets in ms */
234285820Shrsint waittime = MAXWAIT;		/* timeout for each packet */
235285820Shrslong nrcvtimeout = 0;		/* # of packets we got back after waittime */
23655163Sshin
23755163Sshin/* timing */
23855163Sshinint timing;			/* flag to do timing */
23955163Sshindouble tmin = 999999999.0;	/* minimum round trip time */
24055163Sshindouble tmax = 0.0;		/* maximum round trip time */
24155163Sshindouble tsum = 0.0;		/* sum of all times, for doing average */
24278064Sumedouble tsumsq = 0.0;		/* sum of all times squared, for std. dev. */
24355163Sshin
24455163Sshin/* for node addresses */
24555163Sshinu_short naflags;
24655163Sshin
24755163Sshin/* for ancillary data(advanced API) */
24855163Sshinstruct msghdr smsghdr;
24955163Sshinstruct iovec smsgiov;
25055163Sshinchar *scmsg = 0;
25155163Sshin
25278064Sumevolatile sig_atomic_t seenint;
25378064Sume#ifdef SIGINFO
25478064Sumevolatile sig_atomic_t seeninfo;
25578064Sume#endif
25678064Sume
257121472Sumeint	 main(int, char *[]);
25892883Simpvoid	 fill(char *, char *);
25992883Simpint	 get_hoplim(struct msghdr *);
26092883Simpint	 get_pathmtu(struct msghdr *);
26192883Simpstruct in6_pktinfo *get_rcvpktinfo(struct msghdr *);
26292883Simpvoid	 onsignal(int);
26392883Simpvoid	 onint(int);
26492883Simpsize_t	 pingerlen(void);
26592883Simpint	 pinger(void);
26692883Simpconst char *pr_addr(struct sockaddr *, int);
26792883Simpvoid	 pr_icmph(struct icmp6_hdr *, u_char *);
26892883Simpvoid	 pr_iph(struct ip6_hdr *);
26992883Simpvoid	 pr_suptypes(struct icmp6_nodeinfo *, size_t);
27092883Simpvoid	 pr_nodeaddr(struct icmp6_nodeinfo *, int);
27192883Simpint	 myechoreply(const struct icmp6_hdr *);
27292883Simpint	 mynireply(const struct icmp6_nodeinfo *);
27392883Simpchar *dnsdecode(const u_char **, const u_char *, const u_char *,
274121472Sume	char *, size_t);
27592883Simpvoid	 pr_pack(u_char *, int, struct msghdr *);
27692883Simpvoid	 pr_exthdrs(struct msghdr *);
277168866Smtmvoid	 pr_ip6opt(void *, size_t);
278168866Smtmvoid	 pr_rthdr(void *, size_t);
27992883Simpint	 pr_bitrange(u_int32_t, int, int);
28092883Simpvoid	 pr_retip(struct ip6_hdr *, u_char *);
28192883Simpvoid	 summary(void);
28292883Simpvoid	 tvsub(struct timeval *, struct timeval *);
28392883Simpint	 setpolicy(int, char *);
284250251Shrschar	*nigroup(char *, int);
28592883Simpvoid	 usage(void);
28655163Sshin
28755163Sshinint
288216561Scharniermain(int argc, char *argv[])
28955163Sshin{
290285820Shrs	struct timeval last, intvl;
291285820Shrs	struct sockaddr_in6 from, *sin6;
292285820Shrs	struct addrinfo hints, *res;
293285820Shrs	struct sigaction si_sa;
29492806Sobrien	int cc, i;
295285820Shrs	int almost_done, ch, hold, packlen, preload, optval, error;
296250251Shrs	int nig_oldmcprefix = -1;
297209236Sbrucec	u_char *datap;
298121472Sume	char *e, *target, *ifname = NULL, *gateway = NULL;
29955163Sshin	int ip6optlen = 0;
30055163Sshin	struct cmsghdr *scmsgp = NULL;
301209236Sbrucec	/* For control (ancillary) data received from recvmsg() */
302209236Sbrucec	struct cmsghdr cm[CONTROLLEN];
303121472Sume#if defined(SO_SNDBUF) && defined(SO_RCVBUF)
304121472Sume	u_long lsockbufsize;
30555163Sshin	int sockbufsize = 0;
306121472Sume#endif
30762627Skris	int usepktinfo = 0;
30862627Skris	struct in6_pktinfo *pktinfo = NULL;
30962627Skris#ifdef USE_RFC2292BIS
31062627Skris	struct ip6_rthdr *rthdr = NULL;
31162627Skris#endif
31255163Sshin#ifdef IPSEC_POLICY_IPSEC
31355163Sshin	char *policy_in = NULL;
31455163Sshin	char *policy_out = NULL;
31555163Sshin#endif
316285820Shrs	double t;
317285820Shrs	u_long alarmtimeout;
31878064Sume	size_t rthlen;
319121472Sume#ifdef IPV6_USE_MIN_MTU
320121472Sume	int mflag = 0;
321121472Sume#endif
32255163Sshin
32355163Sshin	/* just to be sure */
324107652Ssuz	memset(&smsghdr, 0, sizeof(smsghdr));
325107652Ssuz	memset(&smsgiov, 0, sizeof(smsgiov));
32655163Sshin
327285820Shrs	alarmtimeout = preload = 0;
32855163Sshin	datap = &outpack[ICMP6ECHOLEN + ICMP6ECHOTMLEN];
32955163Sshin#ifndef IPSEC
33078064Sume#define ADDOPTS
33155163Sshin#else
33255163Sshin#ifdef IPSEC_POLICY_IPSEC
33378064Sume#define ADDOPTS	"P:"
33455163Sshin#else
33578064Sume#define ADDOPTS	"AE"
33655163Sshin#endif /*IPSEC_POLICY_IPSEC*/
33755163Sshin#endif
33878064Sume	while ((ch = getopt(argc, argv,
339285820Shrs	    "a:b:c:DdfHg:h:I:i:l:mnNop:qrRS:s:tvwWx:X:" ADDOPTS)) != -1) {
34078064Sume#undef ADDOPTS
34178064Sume		switch (ch) {
34278064Sume		case 'a':
34378064Sume		{
34478064Sume			char *cp;
34555163Sshin
34678064Sume			options &= ~F_NOUSERDATA;
34778064Sume			options |= F_NODEADDR;
34878064Sume			for (cp = optarg; *cp != '\0'; cp++) {
34978064Sume				switch (*cp) {
35078064Sume				case 'a':
35178064Sume					naflags |= NI_NODEADDR_FLAG_ALL;
35278064Sume					break;
35378064Sume				case 'c':
35478064Sume				case 'C':
35578064Sume					naflags |= NI_NODEADDR_FLAG_COMPAT;
35678064Sume					break;
35778064Sume				case 'l':
35878064Sume				case 'L':
35978064Sume					naflags |= NI_NODEADDR_FLAG_LINKLOCAL;
36078064Sume					break;
36178064Sume				case 's':
36278064Sume				case 'S':
36378064Sume					naflags |= NI_NODEADDR_FLAG_SITELOCAL;
36478064Sume					break;
36578064Sume				case 'g':
36678064Sume				case 'G':
36778064Sume					naflags |= NI_NODEADDR_FLAG_GLOBAL;
36878064Sume					break;
36978064Sume				case 'A': /* experimental. not in the spec */
37078064Sume#ifdef NI_NODEADDR_FLAG_ANYCAST
37178064Sume					naflags |= NI_NODEADDR_FLAG_ANYCAST;
37278064Sume					break;
37378064Sume#else
37478064Sume					errx(1,
37578064Sume"-a A is not supported on the platform");
37678064Sume					/*NOTREACHED*/
37778064Sume#endif
37878064Sume				default:
37978064Sume					usage();
38078064Sume					/*NOTREACHED*/
38178064Sume				}
38278064Sume			}
38378064Sume			break;
38478064Sume		}
38578064Sume		case 'b':
38655163Sshin#if defined(SO_SNDBUF) && defined(SO_RCVBUF)
387121472Sume			errno = 0;
388121472Sume			e = NULL;
389121472Sume			lsockbufsize = strtoul(optarg, &e, 10);
390121472Sume			sockbufsize = lsockbufsize;
391121472Sume			if (errno || !*optarg || *e ||
392121472Sume			    sockbufsize != lsockbufsize)
393121472Sume				errx(1, "invalid socket buffer size");
39455163Sshin#else
39587668Scharnier			errx(1,
39655163Sshin"-b option ignored: SO_SNDBUF/SO_RCVBUF socket options not supported");
39755163Sshin#endif
39855163Sshin			break;
39955163Sshin		case 'c':
40055163Sshin			npackets = strtol(optarg, &e, 10);
40155163Sshin			if (npackets <= 0 || *optarg == '\0' || *e != '\0')
40255163Sshin				errx(1,
40355163Sshin				    "illegal number of packets -- %s", optarg);
40455163Sshin			break;
405206889Smaxim		case 'D':
406206889Smaxim			options |= F_DONTFRAG;
407206889Smaxim			break;
40855163Sshin		case 'd':
40955163Sshin			options |= F_SO_DEBUG;
41055163Sshin			break;
41155163Sshin		case 'f':
412121472Sume			if (getuid()) {
413121472Sume				errno = EPERM;
414121472Sume				errx(1, "Must be superuser to flood ping");
415121472Sume			}
41655163Sshin			options |= F_FLOOD;
41755163Sshin			setbuf(stdout, (char *)NULL);
41855163Sshin			break;
419121472Sume		case 'g':
420121472Sume			gateway = optarg;
421121472Sume			break;
42262627Skris		case 'H':
42362627Skris			options |= F_HOSTNAME;
42462627Skris			break;
42555163Sshin		case 'h':		/* hoplimit */
42655163Sshin			hoplimit = strtol(optarg, &e, 10);
427121472Sume			if (*optarg == '\0' || *e != '\0')
428121472Sume				errx(1, "illegal hoplimit %s", optarg);
42955163Sshin			if (255 < hoplimit || hoplimit < -1)
43055163Sshin				errx(1,
43155163Sshin				    "illegal hoplimit -- %s", optarg);
43255163Sshin			break;
43355163Sshin		case 'I':
43455163Sshin			ifname = optarg;
43555163Sshin			options |= F_INTERFACE;
43662627Skris#ifndef USE_SIN6_SCOPE_ID
43762627Skris			usepktinfo++;
43862627Skris#endif
43955163Sshin			break;
44055163Sshin		case 'i':		/* wait between sending packets */
441285820Shrs			t = strtod(optarg, &e);
44278064Sume			if (*optarg == '\0' || *e != '\0')
44378064Sume				errx(1, "illegal timing interval %s", optarg);
444285820Shrs			if (t < 1 && getuid()) {
44578064Sume				errx(1, "%s: only root may use interval < 1s",
44678064Sume				    strerror(EPERM));
44778064Sume			}
448285820Shrs			intvl.tv_sec = (long)t;
449285820Shrs			intvl.tv_usec =
450285820Shrs			    (long)((t - intvl.tv_sec) * 1000000);
451285820Shrs			if (intvl.tv_sec < 0)
45278064Sume				errx(1, "illegal timing interval %s", optarg);
45378064Sume			/* less than 1/hz does not make sense */
454285820Shrs			if (intvl.tv_sec == 0 && intvl.tv_usec < 1) {
455176549Ssilby				warnx("too small interval, raised to .000001");
456285820Shrs				intvl.tv_usec = 1;
45778064Sume			}
45855163Sshin			options |= F_INTERVAL;
45955163Sshin			break;
46055163Sshin		case 'l':
461121472Sume			if (getuid()) {
462121472Sume				errno = EPERM;
463121472Sume				errx(1, "Must be superuser to preload");
464121472Sume			}
46555163Sshin			preload = strtol(optarg, &e, 10);
46655163Sshin			if (preload < 0 || *optarg == '\0' || *e != '\0')
46755163Sshin				errx(1, "illegal preload value -- %s", optarg);
46855163Sshin			break;
46978064Sume		case 'm':
47078064Sume#ifdef IPV6_USE_MIN_MTU
471121472Sume			mflag++;
47278064Sume			break;
47378064Sume#else
47478064Sume			errx(1, "-%c is not supported on this platform", ch);
47578064Sume			/*NOTREACHED*/
47678064Sume#endif
47755163Sshin		case 'n':
47878064Sume			options &= ~F_HOSTNAME;
47955163Sshin			break;
48062627Skris		case 'N':
48162627Skris			options |= F_NIGROUP;
482250251Shrs			nig_oldmcprefix++;
48362627Skris			break;
484173765Sdd		case 'o':
485173765Sdd			options |= F_ONCE;
486173765Sdd			break;
48755163Sshin		case 'p':		/* fill buffer with user pattern */
48855163Sshin			options |= F_PINGFILLED;
48955163Sshin			fill((char *)datap, optarg);
49055163Sshin				break;
49155163Sshin		case 'q':
49255163Sshin			options |= F_QUIET;
49355163Sshin			break;
494182276Smatteo		case 'r':
495182276Smatteo			options |= F_AUDIBLE;
496182276Smatteo			break;
497182276Smatteo		case 'R':
498182276Smatteo			options |= F_MISSED;
499182276Smatteo			break;
50062627Skris		case 'S':
501121472Sume			memset(&hints, 0, sizeof(struct addrinfo));
502121472Sume			hints.ai_flags = AI_NUMERICHOST; /* allow hostname? */
503121472Sume			hints.ai_family = AF_INET6;
504121472Sume			hints.ai_socktype = SOCK_RAW;
505121472Sume			hints.ai_protocol = IPPROTO_ICMPV6;
506121472Sume
507285820Shrs			error = getaddrinfo(optarg, NULL, &hints, &res);
508285820Shrs			if (error) {
509121472Sume				errx(1, "invalid source address: %s",
510285820Shrs				     gai_strerror(error));
511121472Sume			}
512121472Sume			/*
513121472Sume			 * res->ai_family must be AF_INET6 and res->ai_addrlen
514121472Sume			 * must be sizeof(src).
515121472Sume			 */
516121472Sume			memcpy(&src, res->ai_addr, res->ai_addrlen);
517121472Sume			srclen = res->ai_addrlen;
518121472Sume			freeaddrinfo(res);
519209236Sbrucec			res = NULL;
52062627Skris			options |= F_SRCADDR;
52162627Skris			break;
52255163Sshin		case 's':		/* size of packet to send */
52355163Sshin			datalen = strtol(optarg, &e, 10);
52455163Sshin			if (datalen <= 0 || *optarg == '\0' || *e != '\0')
52555163Sshin				errx(1, "illegal datalen value -- %s", optarg);
52678064Sume			if (datalen > MAXDATALEN) {
52755163Sshin				errx(1,
52855163Sshin				    "datalen value too large, maximum is %d",
52955163Sshin				    MAXDATALEN);
53078064Sume			}
53155163Sshin			break;
53278064Sume		case 't':
53378064Sume			options &= ~F_NOUSERDATA;
53478064Sume			options |= F_SUPTYPES;
53578064Sume			break;
53655163Sshin		case 'v':
53755163Sshin			options |= F_VERBOSE;
53855163Sshin			break;
53955163Sshin		case 'w':
54078064Sume			options &= ~F_NOUSERDATA;
54155163Sshin			options |= F_FQDN;
54255163Sshin			break;
54362627Skris		case 'W':
54478064Sume			options &= ~F_NOUSERDATA;
54562627Skris			options |= F_FQDNOLD;
54662627Skris			break;
547285820Shrs		case 'x':
548285820Shrs			t = strtod(optarg, &e);
549285820Shrs			if (*e || e == optarg || t > (double)INT_MAX)
550285820Shrs				err(EX_USAGE, "invalid timing interval: `%s'",
551285820Shrs				    optarg);
552285820Shrs			options |= F_WAITTIME;
553285820Shrs			waittime = (int)t;
554285820Shrs			break;
555285820Shrs		case 'X':
556285820Shrs			alarmtimeout = strtoul(optarg, &e, 0);
557285820Shrs			if ((alarmtimeout < 1) || (alarmtimeout == ULONG_MAX))
558285820Shrs				errx(EX_USAGE, "invalid timeout: `%s'",
559285820Shrs				    optarg);
560285820Shrs			if (alarmtimeout > MAXALARM)
561285820Shrs				errx(EX_USAGE, "invalid timeout: `%s' > %d",
562285820Shrs				    optarg, MAXALARM);
563285820Shrs			alarm((int)alarmtimeout);
564285820Shrs			break;
56555163Sshin#ifdef IPSEC
56655163Sshin#ifdef IPSEC_POLICY_IPSEC
56755163Sshin		case 'P':
56855163Sshin			options |= F_POLICY;
56969571Sume			if (!strncmp("in", optarg, 2)) {
57069571Sume				if ((policy_in = strdup(optarg)) == NULL)
57169571Sume					errx(1, "strdup");
57269571Sume			} else if (!strncmp("out", optarg, 3)) {
57369571Sume				if ((policy_out = strdup(optarg)) == NULL)
57469571Sume					errx(1, "strdup");
57569571Sume			} else
57655163Sshin				errx(1, "invalid security policy");
57755163Sshin			break;
57855163Sshin#else
57955163Sshin		case 'A':
58055163Sshin			options |= F_AUTHHDR;
58155163Sshin			break;
58255163Sshin		case 'E':
58355163Sshin			options |= F_ENCRYPT;
58455163Sshin			break;
58555163Sshin#endif /*IPSEC_POLICY_IPSEC*/
58655163Sshin#endif /*IPSEC*/
58755163Sshin		default:
58855163Sshin			usage();
58962627Skris			/*NOTREACHED*/
59055163Sshin		}
59162627Skris	}
592121472Sume
59355163Sshin	argc -= optind;
59455163Sshin	argv += optind;
59555163Sshin
59662627Skris	if (argc < 1) {
59755163Sshin		usage();
59862627Skris		/*NOTREACHED*/
59962627Skris	}
60055163Sshin
60162627Skris	if (argc > 1) {
60278064Sume#ifdef IPV6_RECVRTHDR	/* 2292bis */
60378064Sume		rthlen = CMSG_SPACE(inet6_rth_space(IPV6_RTHDR_TYPE_0,
60478064Sume		    argc - 1));
60578064Sume#else  /* RFC2292 */
60678064Sume		rthlen = inet6_rthdr_space(IPV6_RTHDR_TYPE_0, argc - 1);
60762627Skris#endif
60878064Sume		if (rthlen == 0) {
60978064Sume			errx(1, "too many intermediate hops");
61078064Sume			/*NOTREACHED*/
61178064Sume		}
61278064Sume		ip6optlen += rthlen;
61362627Skris	}
61455163Sshin
61562627Skris	if (options & F_NIGROUP) {
616250251Shrs		target = nigroup(argv[argc - 1], nig_oldmcprefix);
61762627Skris		if (target == NULL) {
61862627Skris			usage();
61962627Skris			/*NOTREACHED*/
62062627Skris		}
62162627Skris	} else
62262627Skris		target = argv[argc - 1];
62355163Sshin
62455163Sshin	/* getaddrinfo */
625121472Sume	memset(&hints, 0, sizeof(struct addrinfo));
62678064Sume	hints.ai_flags = AI_CANONNAME;
62755163Sshin	hints.ai_family = AF_INET6;
62855163Sshin	hints.ai_socktype = SOCK_RAW;
62955163Sshin	hints.ai_protocol = IPPROTO_ICMPV6;
63055163Sshin
631285820Shrs	error = getaddrinfo(target, NULL, &hints, &res);
632285820Shrs	if (error)
633285820Shrs		errx(1, "%s", gai_strerror(error));
63455163Sshin	if (res->ai_canonname)
63555163Sshin		hostname = res->ai_canonname;
63655163Sshin	else
63755163Sshin		hostname = target;
63878064Sume
63955163Sshin	if (!res->ai_addr)
64055163Sshin		errx(1, "getaddrinfo failed");
64155163Sshin
64255163Sshin	(void)memcpy(&dst, res->ai_addr, res->ai_addrlen);
64355163Sshin
64478064Sume	if ((s = socket(res->ai_family, res->ai_socktype,
64578064Sume	    res->ai_protocol)) < 0)
64678064Sume		err(1, "socket");
64778064Sume
648121472Sume	/* set the source address if specified. */
649121472Sume	if ((options & F_SRCADDR) &&
650121472Sume	    bind(s, (struct sockaddr *)&src, srclen) != 0) {
651121472Sume		err(1, "bind");
652121472Sume	}
653121472Sume
654121472Sume	/* set the gateway (next hop) if specified */
655121472Sume	if (gateway) {
656285820Shrs		memset(&hints, 0, sizeof(hints));
657285820Shrs		hints.ai_family = AF_INET6;
658285820Shrs		hints.ai_socktype = SOCK_RAW;
659285820Shrs		hints.ai_protocol = IPPROTO_ICMPV6;
660121472Sume
661285820Shrs		error = getaddrinfo(gateway, NULL, &hints, &res);
662121472Sume		if (error) {
663121472Sume			errx(1, "getaddrinfo for the gateway %s: %s",
664121472Sume			     gateway, gai_strerror(error));
665121472Sume		}
666285820Shrs		if (res->ai_next && (options & F_VERBOSE))
667121472Sume			warnx("gateway resolves to multiple addresses");
668121472Sume
669121472Sume		if (setsockopt(s, IPPROTO_IPV6, IPV6_NEXTHOP,
670285820Shrs		    res->ai_addr, res->ai_addrlen)) {
671121472Sume			err(1, "setsockopt(IPV6_NEXTHOP)");
672121472Sume		}
673121472Sume
674285820Shrs		freeaddrinfo(res);
675121472Sume	}
676121472Sume
67778064Sume	/*
67878064Sume	 * let the kerel pass extension headers of incoming packets,
67978064Sume	 * for privileged socket options
68078064Sume	 */
68178064Sume	if ((options & F_VERBOSE) != 0) {
68278064Sume		int opton = 1;
68378064Sume
68478064Sume#ifdef IPV6_RECVHOPOPTS
68578064Sume		if (setsockopt(s, IPPROTO_IPV6, IPV6_RECVHOPOPTS, &opton,
68678064Sume		    sizeof(opton)))
68778064Sume			err(1, "setsockopt(IPV6_RECVHOPOPTS)");
68878064Sume#else  /* old adv. API */
68978064Sume		if (setsockopt(s, IPPROTO_IPV6, IPV6_HOPOPTS, &opton,
69078064Sume		    sizeof(opton)))
69178064Sume			err(1, "setsockopt(IPV6_HOPOPTS)");
69278064Sume#endif
69378064Sume#ifdef IPV6_RECVDSTOPTS
69478064Sume		if (setsockopt(s, IPPROTO_IPV6, IPV6_RECVDSTOPTS, &opton,
69578064Sume		    sizeof(opton)))
69678064Sume			err(1, "setsockopt(IPV6_RECVDSTOPTS)");
69778064Sume#else  /* old adv. API */
69878064Sume		if (setsockopt(s, IPPROTO_IPV6, IPV6_DSTOPTS, &opton,
69978064Sume		    sizeof(opton)))
70078064Sume			err(1, "setsockopt(IPV6_DSTOPTS)");
70178064Sume#endif
70278064Sume#ifdef IPV6_RECVRTHDRDSTOPTS
70378064Sume		if (setsockopt(s, IPPROTO_IPV6, IPV6_RECVRTHDRDSTOPTS, &opton,
70478064Sume		    sizeof(opton)))
70578064Sume			err(1, "setsockopt(IPV6_RECVRTHDRDSTOPTS)");
70678064Sume#endif
70778064Sume	}
70878064Sume
70978064Sume	/* revoke root privilege */
710241852Seadler	if (seteuid(getuid()) != 0)
711241852Seadler		err(1, "seteuid() failed");
712241852Seadler	if (setuid(getuid()) != 0)
713241852Seadler		err(1, "setuid() failed");
71478064Sume
715121472Sume	if ((options & F_FLOOD) && (options & F_INTERVAL))
71655163Sshin		errx(1, "-f and -i incompatible options");
71755163Sshin
71878064Sume	if ((options & F_NOUSERDATA) == 0) {
719121472Sume		if (datalen >= sizeof(struct tv32)) {
72078064Sume			/* we can time transfer */
72178064Sume			timing = 1;
72278064Sume		} else
72378064Sume			timing = 0;
72478064Sume		/* in F_VERBOSE case, we may get non-echoreply packets*/
72578064Sume		if (options & F_VERBOSE)
72678064Sume			packlen = 2048 + IP6LEN + ICMP6ECHOLEN + EXTRA;
72778064Sume		else
72878064Sume			packlen = datalen + IP6LEN + ICMP6ECHOLEN + EXTRA;
72978064Sume	} else {
73078064Sume		/* suppress timing for node information query */
73178064Sume		timing = 0;
73278064Sume		datalen = 2048;
73378064Sume		packlen = 2048 + IP6LEN + ICMP6ECHOLEN + EXTRA;
73478064Sume	}
73578064Sume
73655163Sshin	if (!(packet = (u_char *)malloc((u_int)packlen)))
737121472Sume		err(1, "Unable to allocate packet");
73855163Sshin	if (!(options & F_PINGFILLED))
73978064Sume		for (i = ICMP6ECHOLEN; i < packlen; ++i)
74055163Sshin			*datap++ = i;
74155163Sshin
74255163Sshin	ident = getpid() & 0xFFFF;
743287328Sdelphij	arc4random_buf(nonce, sizeof(nonce));
744206889Smaxim	optval = 1;
745206889Smaxim	if (options & F_DONTFRAG)
746206889Smaxim		if (setsockopt(s, IPPROTO_IPV6, IPV6_DONTFRAG,
747206889Smaxim		    &optval, sizeof(optval)) == -1)
748206889Smaxim			err(1, "IPV6_DONTFRAG");
74955163Sshin	hold = 1;
75055163Sshin
75155163Sshin	if (options & F_SO_DEBUG)
75255163Sshin		(void)setsockopt(s, SOL_SOCKET, SO_DEBUG, (char *)&hold,
75355163Sshin		    sizeof(hold));
75455163Sshin	optval = IPV6_DEFHLIM;
75555163Sshin	if (IN6_IS_ADDR_MULTICAST(&dst.sin6_addr))
75655163Sshin		if (setsockopt(s, IPPROTO_IPV6, IPV6_MULTICAST_HOPS,
75778064Sume		    &optval, sizeof(optval)) == -1)
75855163Sshin			err(1, "IPV6_MULTICAST_HOPS");
75978064Sume#ifdef IPV6_USE_MIN_MTU
760121472Sume	if (mflag != 1) {
761121472Sume		optval = mflag > 1 ? 0 : 1;
762121472Sume
76378064Sume		if (setsockopt(s, IPPROTO_IPV6, IPV6_USE_MIN_MTU,
76478064Sume		    &optval, sizeof(optval)) == -1)
76578064Sume			err(1, "setsockopt(IPV6_USE_MIN_MTU)");
76678064Sume	}
76778064Sume#ifdef IPV6_RECVPATHMTU
76878064Sume	else {
76978064Sume		optval = 1;
77078064Sume		if (setsockopt(s, IPPROTO_IPV6, IPV6_RECVPATHMTU,
77178064Sume		    &optval, sizeof(optval)) == -1)
77278064Sume			err(1, "setsockopt(IPV6_RECVPATHMTU)");
77378064Sume	}
77478064Sume#endif /* IPV6_RECVPATHMTU */
77578064Sume#endif /* IPV6_USE_MIN_MTU */
77655163Sshin
77755163Sshin#ifdef IPSEC
77855163Sshin#ifdef IPSEC_POLICY_IPSEC
77955163Sshin	if (options & F_POLICY) {
78055163Sshin		if (setpolicy(s, policy_in) < 0)
78164277Skris			errx(1, "%s", ipsec_strerror());
78255163Sshin		if (setpolicy(s, policy_out) < 0)
78364277Skris			errx(1, "%s", ipsec_strerror());
78455163Sshin	}
78555163Sshin#else
78655163Sshin	if (options & F_AUTHHDR) {
78755163Sshin		optval = IPSEC_LEVEL_REQUIRE;
78855163Sshin#ifdef IPV6_AUTH_TRANS_LEVEL
78955163Sshin		if (setsockopt(s, IPPROTO_IPV6, IPV6_AUTH_TRANS_LEVEL,
79078064Sume		    &optval, sizeof(optval)) == -1)
79155163Sshin			err(1, "setsockopt(IPV6_AUTH_TRANS_LEVEL)");
79255163Sshin#else /* old def */
79355163Sshin		if (setsockopt(s, IPPROTO_IPV6, IPV6_AUTH_LEVEL,
79478064Sume		    &optval, sizeof(optval)) == -1)
79555163Sshin			err(1, "setsockopt(IPV6_AUTH_LEVEL)");
79655163Sshin#endif
79755163Sshin	}
79855163Sshin	if (options & F_ENCRYPT) {
79955163Sshin		optval = IPSEC_LEVEL_REQUIRE;
80055163Sshin		if (setsockopt(s, IPPROTO_IPV6, IPV6_ESP_TRANS_LEVEL,
80178064Sume		    &optval, sizeof(optval)) == -1)
80255163Sshin			err(1, "setsockopt(IPV6_ESP_TRANS_LEVEL)");
80355163Sshin	}
80455163Sshin#endif /*IPSEC_POLICY_IPSEC*/
80555163Sshin#endif
80655163Sshin
80755163Sshin#ifdef ICMP6_FILTER
80855163Sshin    {
80955163Sshin	struct icmp6_filter filt;
81055163Sshin	if (!(options & F_VERBOSE)) {
81155163Sshin		ICMP6_FILTER_SETBLOCKALL(&filt);
81262627Skris		if ((options & F_FQDN) || (options & F_FQDNOLD) ||
81378064Sume		    (options & F_NODEADDR) || (options & F_SUPTYPES))
81455163Sshin			ICMP6_FILTER_SETPASS(ICMP6_NI_REPLY, &filt);
81555163Sshin		else
81655163Sshin			ICMP6_FILTER_SETPASS(ICMP6_ECHO_REPLY, &filt);
81755163Sshin	} else {
81855163Sshin		ICMP6_FILTER_SETPASSALL(&filt);
81955163Sshin	}
82055163Sshin	if (setsockopt(s, IPPROTO_ICMPV6, ICMP6_FILTER, &filt,
82178064Sume	    sizeof(filt)) < 0)
82255163Sshin		err(1, "setsockopt(ICMP6_FILTER)");
82355163Sshin    }
82455163Sshin#endif /*ICMP6_FILTER*/
82555163Sshin
82662627Skris	/* let the kerel pass extension headers of incoming packets */
82762627Skris	if ((options & F_VERBOSE) != 0) {
82862627Skris		int opton = 1;
82962627Skris
83062627Skris#ifdef IPV6_RECVRTHDR
83162627Skris		if (setsockopt(s, IPPROTO_IPV6, IPV6_RECVRTHDR, &opton,
83278064Sume		    sizeof(opton)))
83362627Skris			err(1, "setsockopt(IPV6_RECVRTHDR)");
83462627Skris#else  /* old adv. API */
83562627Skris		if (setsockopt(s, IPPROTO_IPV6, IPV6_RTHDR, &opton,
83678064Sume		    sizeof(opton)))
83762627Skris			err(1, "setsockopt(IPV6_RTHDR)");
83862627Skris#endif
83962627Skris	}
84062627Skris
84155163Sshin/*
84255163Sshin	optval = 1;
84355163Sshin	if (IN6_IS_ADDR_MULTICAST(&dst.sin6_addr))
84455163Sshin		if (setsockopt(s, IPPROTO_IPV6, IPV6_MULTICAST_LOOP,
84578064Sume		    &optval, sizeof(optval)) == -1)
84655163Sshin			err(1, "IPV6_MULTICAST_LOOP");
84755163Sshin*/
84855163Sshin
84962627Skris	/* Specify the outgoing interface and/or the source address */
85062627Skris	if (usepktinfo)
85155163Sshin		ip6optlen += CMSG_SPACE(sizeof(struct in6_pktinfo));
85255163Sshin
85355163Sshin	if (hoplimit != -1)
85455163Sshin		ip6optlen += CMSG_SPACE(sizeof(int));
85555163Sshin
85655163Sshin	/* set IP6 packet options */
85755163Sshin	if (ip6optlen) {
85855163Sshin		if ((scmsg = (char *)malloc(ip6optlen)) == 0)
85955163Sshin			errx(1, "can't allocate enough memory");
86055163Sshin		smsghdr.msg_control = (caddr_t)scmsg;
86155163Sshin		smsghdr.msg_controllen = ip6optlen;
86255163Sshin		scmsgp = (struct cmsghdr *)scmsg;
86355163Sshin	}
86462627Skris	if (usepktinfo) {
86562627Skris		pktinfo = (struct in6_pktinfo *)(CMSG_DATA(scmsgp));
86662627Skris		memset(pktinfo, 0, sizeof(*pktinfo));
86755163Sshin		scmsgp->cmsg_len = CMSG_LEN(sizeof(struct in6_pktinfo));
86855163Sshin		scmsgp->cmsg_level = IPPROTO_IPV6;
86955163Sshin		scmsgp->cmsg_type = IPV6_PKTINFO;
87062627Skris		scmsgp = CMSG_NXTHDR(&smsghdr, scmsgp);
87162627Skris	}
87255163Sshin
87362627Skris	/* set the outgoing interface */
87462627Skris	if (ifname) {
87562627Skris#ifndef USE_SIN6_SCOPE_ID
87662627Skris		/* pktinfo must have already been allocated */
87762627Skris		if ((pktinfo->ipi6_ifindex = if_nametoindex(ifname)) == 0)
87878064Sume			errx(1, "%s: invalid interface name", ifname);
87955163Sshin#else
88055163Sshin		if ((dst.sin6_scope_id = if_nametoindex(ifname)) == 0)
88155163Sshin			errx(1, "%s: invalid interface name", ifname);
88255163Sshin#endif
88355163Sshin	}
88455163Sshin	if (hoplimit != -1) {
88555163Sshin		scmsgp->cmsg_len = CMSG_LEN(sizeof(int));
88655163Sshin		scmsgp->cmsg_level = IPPROTO_IPV6;
88755163Sshin		scmsgp->cmsg_type = IPV6_HOPLIMIT;
88855163Sshin		*(int *)(CMSG_DATA(scmsgp)) = hoplimit;
88955163Sshin
89055163Sshin		scmsgp = CMSG_NXTHDR(&smsghdr, scmsgp);
89155163Sshin	}
89262627Skris
89355163Sshin	if (argc > 1) {	/* some intermediate addrs are specified */
894285820Shrs		int hops;
89562627Skris#ifdef USE_RFC2292BIS
89662627Skris		int rthdrlen;
89762627Skris#endif
89855163Sshin
89962627Skris#ifdef USE_RFC2292BIS
90062627Skris		rthdrlen = inet6_rth_space(IPV6_RTHDR_TYPE_0, argc - 1);
90162627Skris		scmsgp->cmsg_len = CMSG_LEN(rthdrlen);
90262627Skris		scmsgp->cmsg_level = IPPROTO_IPV6;
90362627Skris		scmsgp->cmsg_type = IPV6_RTHDR;
90462627Skris		rthdr = (struct ip6_rthdr *)CMSG_DATA(scmsgp);
90562627Skris		rthdr = inet6_rth_init((void *)rthdr, rthdrlen,
90678064Sume		    IPV6_RTHDR_TYPE_0, argc - 1);
90762627Skris		if (rthdr == NULL)
90862627Skris			errx(1, "can't initialize rthdr");
90962627Skris#else  /* old advanced API */
91055163Sshin		if ((scmsgp = (struct cmsghdr *)inet6_rthdr_init(scmsgp,
91178064Sume		    IPV6_RTHDR_TYPE_0)) == 0)
91255163Sshin			errx(1, "can't initialize rthdr");
91362627Skris#endif /* USE_RFC2292BIS */
91455163Sshin
91555163Sshin		for (hops = 0; hops < argc - 1; hops++) {
916285820Shrs			memset(&hints, 0, sizeof(hints));
917285820Shrs			hints.ai_family = AF_INET6;
91855163Sshin
91978064Sume			if ((error = getaddrinfo(argv[hops], NULL, &hints,
920285820Shrs			    &res)))
92164277Skris				errx(1, "%s", gai_strerror(error));
922285820Shrs			if (res->ai_addr->sa_family != AF_INET6)
92355163Sshin				errx(1,
92478064Sume				    "bad addr family of an intermediate addr");
925285820Shrs			sin6 = (struct sockaddr_in6 *)(void *)res->ai_addr;
92662627Skris#ifdef USE_RFC2292BIS
927285820Shrs			if (inet6_rth_add(rthdr, &sin6->sin6_addr))
92862627Skris				errx(1, "can't add an intermediate node");
92962627Skris#else  /* old advanced API */
930285820Shrs			if (inet6_rthdr_add(scmsg, &sin6->sin6_addr,
93178064Sume			    IPV6_RTHDR_LOOSE))
93255163Sshin				errx(1, "can't add an intermediate node");
93362627Skris#endif /* USE_RFC2292BIS */
934285820Shrs			freeaddrinfo(res);
93555163Sshin		}
93655163Sshin
93762627Skris#ifndef USE_RFC2292BIS
93855163Sshin		if (inet6_rthdr_lasthop(scmsgp, IPV6_RTHDR_LOOSE))
93955163Sshin			errx(1, "can't set the last flag");
94062627Skris#endif
94155163Sshin
94255163Sshin		scmsgp = CMSG_NXTHDR(&smsghdr, scmsgp);
94355163Sshin	}
94455163Sshin
945121472Sume	if (!(options & F_SRCADDR)) {
94655163Sshin		/*
947121472Sume		 * get the source address. XXX since we revoked the root
948121472Sume		 * privilege, we cannot use a raw socket for this.
94955163Sshin		 */
950121472Sume		int dummy;
951121472Sume		socklen_t len = sizeof(src);
95278064Sume
95355163Sshin		if ((dummy = socket(AF_INET6, SOCK_DGRAM, 0)) < 0)
95455163Sshin			err(1, "UDP socket");
95555163Sshin
95655163Sshin		src.sin6_family = AF_INET6;
95755163Sshin		src.sin6_addr = dst.sin6_addr;
95855163Sshin		src.sin6_port = ntohs(DUMMY_PORT);
95955163Sshin		src.sin6_scope_id = dst.sin6_scope_id;
96055163Sshin
96162627Skris#ifdef USE_RFC2292BIS
96262627Skris		if (pktinfo &&
96362627Skris		    setsockopt(dummy, IPPROTO_IPV6, IPV6_PKTINFO,
96478064Sume		    (void *)pktinfo, sizeof(*pktinfo)))
96562627Skris			err(1, "UDP setsockopt(IPV6_PKTINFO)");
96662627Skris
96762627Skris		if (hoplimit != -1 &&
968121472Sume		    setsockopt(dummy, IPPROTO_IPV6, IPV6_UNICAST_HOPS,
96978064Sume		    (void *)&hoplimit, sizeof(hoplimit)))
970121472Sume			err(1, "UDP setsockopt(IPV6_UNICAST_HOPS)");
97162627Skris
972121472Sume		if (hoplimit != -1 &&
973121472Sume		    setsockopt(dummy, IPPROTO_IPV6, IPV6_MULTICAST_HOPS,
974121472Sume		    (void *)&hoplimit, sizeof(hoplimit)))
975121472Sume			err(1, "UDP setsockopt(IPV6_MULTICAST_HOPS)");
976121472Sume
97762627Skris		if (rthdr &&
97862627Skris		    setsockopt(dummy, IPPROTO_IPV6, IPV6_RTHDR,
97978064Sume		    (void *)rthdr, (rthdr->ip6r_len + 1) << 3))
98062627Skris			err(1, "UDP setsockopt(IPV6_RTHDR)");
98162627Skris#else  /* old advanced API */
98262627Skris		if (smsghdr.msg_control &&
98362627Skris		    setsockopt(dummy, IPPROTO_IPV6, IPV6_PKTOPTIONS,
98478064Sume		    (void *)smsghdr.msg_control, smsghdr.msg_controllen))
98562627Skris			err(1, "UDP setsockopt(IPV6_PKTOPTIONS)");
98655163Sshin#endif
98778064Sume
98855163Sshin		if (connect(dummy, (struct sockaddr *)&src, len) < 0)
98955163Sshin			err(1, "UDP connect");
99078064Sume
99155163Sshin		if (getsockname(dummy, (struct sockaddr *)&src, &len) < 0)
99255163Sshin			err(1, "getsockname");
99355163Sshin
99455163Sshin		close(dummy);
99555163Sshin	}
99655163Sshin
99755163Sshin#if defined(SO_SNDBUF) && defined(SO_RCVBUF)
99855163Sshin	if (sockbufsize) {
99955163Sshin		if (datalen > sockbufsize)
100062627Skris			warnx("you need -b to increase socket buffer size");
100155163Sshin		if (setsockopt(s, SOL_SOCKET, SO_SNDBUF, &sockbufsize,
100278064Sume		    sizeof(sockbufsize)) < 0)
100355163Sshin			err(1, "setsockopt(SO_SNDBUF)");
100455163Sshin		if (setsockopt(s, SOL_SOCKET, SO_RCVBUF, &sockbufsize,
100578064Sume		    sizeof(sockbufsize)) < 0)
100655163Sshin			err(1, "setsockopt(SO_RCVBUF)");
100755163Sshin	}
100855163Sshin	else {
100955163Sshin		if (datalen > 8 * 1024)	/*XXX*/
101055163Sshin			warnx("you need -b to increase socket buffer size");
101155163Sshin		/*
101255163Sshin		 * When pinging the broadcast address, you can get a lot of
101355163Sshin		 * answers. Doing something so evil is useful if you are trying
101455163Sshin		 * to stress the ethernet, or just want to fill the arp cache
101555163Sshin		 * to get some stuff for /etc/ethers.
101655163Sshin		 */
101755163Sshin		hold = 48 * 1024;
101878064Sume		setsockopt(s, SOL_SOCKET, SO_RCVBUF, (char *)&hold,
101978064Sume		    sizeof(hold));
102055163Sshin	}
102155163Sshin#endif
102255163Sshin
102355163Sshin	optval = 1;
102455163Sshin#ifndef USE_SIN6_SCOPE_ID
102562627Skris#ifdef IPV6_RECVPKTINFO
102662627Skris	if (setsockopt(s, IPPROTO_IPV6, IPV6_RECVPKTINFO, &optval,
102778064Sume	    sizeof(optval)) < 0)
102862627Skris		warn("setsockopt(IPV6_RECVPKTINFO)"); /* XXX err? */
102962627Skris#else  /* old adv. API */
103062627Skris	if (setsockopt(s, IPPROTO_IPV6, IPV6_PKTINFO, &optval,
103178064Sume	    sizeof(optval)) < 0)
103262627Skris		warn("setsockopt(IPV6_PKTINFO)"); /* XXX err? */
103355163Sshin#endif
103462627Skris#endif /* USE_SIN6_SCOPE_ID */
103562627Skris#ifdef IPV6_RECVHOPLIMIT
103662627Skris	if (setsockopt(s, IPPROTO_IPV6, IPV6_RECVHOPLIMIT, &optval,
103778064Sume	    sizeof(optval)) < 0)
103862627Skris		warn("setsockopt(IPV6_RECVHOPLIMIT)"); /* XXX err? */
103962627Skris#else  /* old adv. API */
104062627Skris	if (setsockopt(s, IPPROTO_IPV6, IPV6_HOPLIMIT, &optval,
104178064Sume	    sizeof(optval)) < 0)
104262627Skris		warn("setsockopt(IPV6_HOPLIMIT)"); /* XXX err? */
104362627Skris#endif
104455163Sshin
104578064Sume	printf("PING6(%lu=40+8+%lu bytes) ", (unsigned long)(40 + pingerlen()),
104678064Sume	    (unsigned long)(pingerlen() - 8));
104778064Sume	printf("%s --> ", pr_addr((struct sockaddr *)&src, sizeof(src)));
104878064Sume	printf("%s\n", pr_addr((struct sockaddr *)&dst, sizeof(dst)));
104955163Sshin
1050285820Shrs	if (preload == 0)
1051285820Shrs		pinger();
1052285820Shrs	else {
1053285820Shrs		if (npackets != 0 && preload > npackets)
1054285820Shrs			preload = npackets;
1055285820Shrs		while (preload--)
1056285820Shrs			pinger();
105755163Sshin	}
1058285820Shrs	gettimeofday(&last, NULL);
105955163Sshin
1060285820Shrs	sigemptyset(&si_sa.sa_mask);
1061285820Shrs	si_sa.sa_flags = 0;
1062285820Shrs	si_sa.sa_handler = onsignal;
1063285820Shrs	if (sigaction(SIGINT, &si_sa, 0) == -1)
1064285820Shrs		err(EX_OSERR, "sigaction SIGINT");
1065285820Shrs	seenint = 0;
106678064Sume#ifdef SIGINFO
1067285820Shrs	if (sigaction(SIGINFO, &si_sa, 0) == -1)
1068285820Shrs		err(EX_OSERR, "sigaction SIGINFO");
106978064Sume	seeninfo = 0;
107078064Sume#endif
1071285820Shrs	if (alarmtimeout > 0) {
1072285820Shrs		if (sigaction(SIGALRM, &si_sa, 0) == -1)
1073285820Shrs			err(EX_OSERR, "sigaction SIGALRM");
1074285820Shrs	}
1075285820Shrs	if (options & F_FLOOD) {
1076285820Shrs		intvl.tv_sec = 0;
1077285820Shrs		intvl.tv_usec = 10000;
1078285820Shrs	} else if ((options & F_INTERVAL) == 0) {
1079285820Shrs		intvl.tv_sec = interval / 1000;
1080285820Shrs		intvl.tv_usec = interval % 1000 * 1000;
1081285820Shrs	}
108278064Sume
1083285820Shrs	almost_done = 0;
1084285820Shrs	while (seenint == 0) {
1085285820Shrs		struct timeval now, timeout;
108655163Sshin		struct msghdr m;
108755163Sshin		struct iovec iov[2];
1088285820Shrs		fd_set rfds;
1089285820Shrs		int n;
109055163Sshin
109178064Sume		/* signal handling */
1092285820Shrs		if (seenint)
109378064Sume			onint(SIGINT);
109478064Sume#ifdef SIGINFO
109578064Sume		if (seeninfo) {
109678064Sume			summary();
109778064Sume			seeninfo = 0;
109878064Sume			continue;
109978064Sume		}
110078064Sume#endif
1101285820Shrs		FD_ZERO(&rfds);
1102285820Shrs		FD_SET(s, &rfds);
1103285820Shrs		gettimeofday(&now, NULL);
1104285820Shrs		timeout.tv_sec = last.tv_sec + intvl.tv_sec - now.tv_sec;
1105285820Shrs		timeout.tv_usec = last.tv_usec + intvl.tv_usec - now.tv_usec;
1106285820Shrs		while (timeout.tv_usec < 0) {
1107285820Shrs			timeout.tv_usec += 1000000;
1108285820Shrs			timeout.tv_sec--;
1109121472Sume		}
1110285820Shrs		while (timeout.tv_usec > 1000000) {
1111285820Shrs			timeout.tv_usec -= 1000000;
1112285820Shrs			timeout.tv_sec++;
1113285820Shrs		}
1114285820Shrs		if (timeout.tv_sec < 0)
1115285820Shrs			timeout.tv_sec = timeout.tv_usec = 0;
111678064Sume
1117285820Shrs		n = select(s + 1, &rfds, NULL, NULL, &timeout);
1118285820Shrs		if (n < 0)
1119285820Shrs			continue;	/* EINTR */
1120285820Shrs		if (n == 1) {
1121285820Shrs			m.msg_name = (caddr_t)&from;
1122285820Shrs			m.msg_namelen = sizeof(from);
1123285820Shrs			memset(&iov, 0, sizeof(iov));
1124285820Shrs			iov[0].iov_base = (caddr_t)packet;
1125285820Shrs			iov[0].iov_len = packlen;
1126285820Shrs			m.msg_iov = iov;
1127285820Shrs			m.msg_iovlen = 1;
1128285820Shrs			memset(cm, 0, CONTROLLEN);
1129285820Shrs			m.msg_control = (void *)cm;
1130285820Shrs			m.msg_controllen = CONTROLLEN;
113155163Sshin
1132285820Shrs			cc = recvmsg(s, &m, 0);
1133285820Shrs			if (cc < 0) {
1134285820Shrs				if (errno != EINTR) {
1135285820Shrs					warn("recvmsg");
1136285820Shrs					sleep(1);
1137285820Shrs				}
1138285820Shrs				continue;
1139285820Shrs			} else if (cc == 0) {
1140285820Shrs				int mtu;
1141285820Shrs
1142285820Shrs				/*
1143285820Shrs				 * receive control messages only. Process the
1144285820Shrs				 * exceptions (currently the only possibility is
1145285820Shrs				 * a path MTU notification.)
1146285820Shrs				 */
1147285820Shrs				if ((mtu = get_pathmtu(&m)) > 0) {
1148285820Shrs					if ((options & F_VERBOSE) != 0) {
1149285820Shrs						printf("new path MTU (%d) is "
1150285820Shrs						    "notified\n", mtu);
1151285820Shrs					}
1152285820Shrs				}
1153285820Shrs				continue;
1154285820Shrs			} else {
1155285820Shrs				/*
1156285820Shrs				 * an ICMPv6 message (probably an echoreply)
1157285820Shrs				 * arrived.
1158285820Shrs				 */
1159285820Shrs				pr_pack(packet, cc, &m);
116078064Sume			}
1161285820Shrs			if (((options & F_ONCE) != 0 && nreceived > 0) ||
1162285820Shrs			    (npackets > 0 && nreceived >= npackets))
1163285820Shrs				break;
1164285820Shrs		}
1165285820Shrs		if (n == 0 || (options & F_FLOOD)) {
1166285820Shrs			if (npackets == 0 || ntransmitted < npackets)
1167285820Shrs				pinger();
1168285820Shrs			else {
1169285820Shrs				if (almost_done)
1170285820Shrs					break;
1171285820Shrs				almost_done = 1;
117278064Sume			/*
1173285820Shrs			 * If we're not transmitting any more packets,
1174285820Shrs			 * change the timer to wait two round-trip times
1175285820Shrs			 * if we've received any packets or (waittime)
1176285820Shrs			 * milliseconds if we haven't.
117778064Sume			 */
1178285820Shrs				intvl.tv_usec = 0;
1179285820Shrs				if (nreceived) {
1180285820Shrs					intvl.tv_sec = 2 * tmax / 1000;
1181285820Shrs					if (intvl.tv_sec == 0)
1182285820Shrs						intvl.tv_sec = 1;
1183285820Shrs				} else {
1184285820Shrs					intvl.tv_sec = waittime / 1000;
1185285820Shrs					intvl.tv_usec = waittime % 1000 * 1000;
118678064Sume				}
118778064Sume			}
1188285820Shrs			gettimeofday(&last, NULL);
1189285820Shrs			if (ntransmitted - nreceived - 1 > nmissedmax) {
1190285820Shrs				nmissedmax = ntransmitted - nreceived - 1;
1191285820Shrs				if (options & F_MISSED)
1192285820Shrs					(void)write(STDOUT_FILENO, &BBELL, 1);
1193285820Shrs			}
119455163Sshin		}
119555163Sshin	}
1196285820Shrs	sigemptyset(&si_sa.sa_mask);
1197285820Shrs	si_sa.sa_flags = 0;
1198285820Shrs	si_sa.sa_handler = SIG_IGN;
1199285820Shrs	sigaction(SIGINT, &si_sa, 0);
1200285820Shrs	sigaction(SIGALRM, &si_sa, 0);
120155163Sshin	summary();
1202209236Sbrucec
1203209236Sbrucec	if (res != NULL)
1204209236Sbrucec		freeaddrinfo(res);
1205209236Sbrucec
1206209236Sbrucec        if(packet != NULL)
1207209236Sbrucec                free(packet);
1208209236Sbrucec
1209179356Sbz	exit(nreceived == 0 ? 2 : 0);
121055163Sshin}
121155163Sshin
121278064Sumevoid
1213216561Scharnieronsignal(int sig)
121478064Sume{
1215121472Sume
121678064Sume	switch (sig) {
1217285820Shrs	case SIGINT:
121878064Sume	case SIGALRM:
121978064Sume		seenint++;
122078064Sume		break;
122178064Sume#ifdef SIGINFO
122278064Sume	case SIGINFO:
122378064Sume		seeninfo++;
122478064Sume		break;
122578064Sume#endif
122678064Sume	}
122778064Sume}
122878064Sume
122955163Sshin/*
123055163Sshin * pinger --
123155163Sshin *	Compose and transmit an ICMP ECHO REQUEST packet.  The IP packet
123255163Sshin * will be added on by the kernel.  The ID field is our UNIX process ID,
123355163Sshin * and the sequence number is an ascending integer.  The first 8 bytes
123455163Sshin * of the data portion are used to hold a UNIX "timeval" struct in VAX
123555163Sshin * byte-order, to compute the round-trip time.
123655163Sshin */
123778064Sumesize_t
1238216561Scharnierpingerlen(void)
123978064Sume{
124078064Sume	size_t l;
124178064Sume
124278064Sume	if (options & F_FQDN)
124378064Sume		l = ICMP6_NIQLEN + sizeof(dst.sin6_addr);
124478064Sume	else if (options & F_FQDNOLD)
124578064Sume		l = ICMP6_NIQLEN;
124678064Sume	else if (options & F_NODEADDR)
124778064Sume		l = ICMP6_NIQLEN + sizeof(dst.sin6_addr);
124878064Sume	else if (options & F_SUPTYPES)
124978064Sume		l = ICMP6_NIQLEN;
125078064Sume	else
125178064Sume		l = ICMP6ECHOLEN + datalen;
125278064Sume
125378064Sume	return l;
125478064Sume}
125578064Sume
125678984Sumeint
1257216561Scharnierpinger(void)
125855163Sshin{
125955163Sshin	struct icmp6_hdr *icp;
126055163Sshin	struct iovec iov[2];
126155163Sshin	int i, cc;
126278064Sume	struct icmp6_nodeinfo *nip;
126378064Sume	int seq;
126455163Sshin
126578984Sume	if (npackets && ntransmitted >= npackets)
126678984Sume		return(-1);	/* no more transmission */
126778984Sume
126855163Sshin	icp = (struct icmp6_hdr *)outpack;
126978064Sume	nip = (struct icmp6_nodeinfo *)outpack;
127062627Skris	memset(icp, 0, sizeof(*icp));
127155163Sshin	icp->icmp6_cksum = 0;
127278064Sume	seq = ntransmitted++;
127378064Sume	CLR(seq % mx_dup_ck);
127455163Sshin
127555163Sshin	if (options & F_FQDN) {
127655163Sshin		icp->icmp6_type = ICMP6_NI_QUERY;
127762627Skris		icp->icmp6_code = ICMP6_NI_SUBJ_IPV6;
127878064Sume		nip->ni_qtype = htons(NI_QTYPE_FQDN);
127978064Sume		nip->ni_flags = htons(0);
128078064Sume
128178064Sume		memcpy(nip->icmp6_ni_nonce, nonce,
128278064Sume		    sizeof(nip->icmp6_ni_nonce));
128378064Sume		*(u_int16_t *)nip->icmp6_ni_nonce = ntohs(seq);
128478064Sume
128562627Skris		memcpy(&outpack[ICMP6_NIQLEN], &dst.sin6_addr,
128662627Skris		    sizeof(dst.sin6_addr));
128762627Skris		cc = ICMP6_NIQLEN + sizeof(dst.sin6_addr);
128862627Skris		datalen = 0;
128962627Skris	} else if (options & F_FQDNOLD) {
129062627Skris		/* packet format in 03 draft - no Subject data on queries */
129162627Skris		icp->icmp6_type = ICMP6_NI_QUERY;
129278064Sume		icp->icmp6_code = 0;	/* code field is always 0 */
129378064Sume		nip->ni_qtype = htons(NI_QTYPE_FQDN);
129478064Sume		nip->ni_flags = htons(0);
129578064Sume
129678064Sume		memcpy(nip->icmp6_ni_nonce, nonce,
129778064Sume		    sizeof(nip->icmp6_ni_nonce));
129878064Sume		*(u_int16_t *)nip->icmp6_ni_nonce = ntohs(seq);
129978064Sume
130055163Sshin		cc = ICMP6_NIQLEN;
130155163Sshin		datalen = 0;
130255163Sshin	} else if (options & F_NODEADDR) {
130355163Sshin		icp->icmp6_type = ICMP6_NI_QUERY;
130462627Skris		icp->icmp6_code = ICMP6_NI_SUBJ_IPV6;
130578064Sume		nip->ni_qtype = htons(NI_QTYPE_NODEADDR);
130678064Sume		nip->ni_flags = naflags;
130778064Sume
130878064Sume		memcpy(nip->icmp6_ni_nonce, nonce,
130978064Sume		    sizeof(nip->icmp6_ni_nonce));
131078064Sume		*(u_int16_t *)nip->icmp6_ni_nonce = ntohs(seq);
131178064Sume
131262627Skris		memcpy(&outpack[ICMP6_NIQLEN], &dst.sin6_addr,
131362627Skris		    sizeof(dst.sin6_addr));
131462627Skris		cc = ICMP6_NIQLEN + sizeof(dst.sin6_addr);
131555163Sshin		datalen = 0;
131678064Sume	} else if (options & F_SUPTYPES) {
131778064Sume		icp->icmp6_type = ICMP6_NI_QUERY;
131878064Sume		icp->icmp6_code = ICMP6_NI_SUBJ_FQDN;	/*empty*/
131978064Sume		nip->ni_qtype = htons(NI_QTYPE_SUPTYPES);
132078064Sume		/* we support compressed bitmap */
132178064Sume		nip->ni_flags = NI_SUPTYPE_FLAG_COMPRESS;
132278064Sume
132378064Sume		memcpy(nip->icmp6_ni_nonce, nonce,
132478064Sume		    sizeof(nip->icmp6_ni_nonce));
132578064Sume		*(u_int16_t *)nip->icmp6_ni_nonce = ntohs(seq);
132678064Sume		cc = ICMP6_NIQLEN;
132778064Sume		datalen = 0;
132878064Sume	} else {
132955163Sshin		icp->icmp6_type = ICMP6_ECHO_REQUEST;
133078064Sume		icp->icmp6_code = 0;
133178064Sume		icp->icmp6_id = htons(ident);
133278064Sume		icp->icmp6_seq = ntohs(seq);
1333121472Sume		if (timing) {
1334121472Sume			struct timeval tv;
1335121472Sume			struct tv32 *tv32;
1336121472Sume			(void)gettimeofday(&tv, NULL);
1337121472Sume			tv32 = (struct tv32 *)&outpack[ICMP6ECHOLEN];
1338121472Sume			tv32->tv32_sec = htonl(tv.tv_sec);
1339121472Sume			tv32->tv32_usec = htonl(tv.tv_usec);
1340121472Sume		}
134155163Sshin		cc = ICMP6ECHOLEN + datalen;
134255163Sshin	}
134355163Sshin
134478064Sume#ifdef DIAGNOSTIC
134578064Sume	if (pingerlen() != cc)
134678064Sume		errx(1, "internal error; length mismatch");
134778064Sume#endif
134878064Sume
134955163Sshin	smsghdr.msg_name = (caddr_t)&dst;
135055163Sshin	smsghdr.msg_namelen = sizeof(dst);
135155163Sshin	memset(&iov, 0, sizeof(iov));
135255163Sshin	iov[0].iov_base = (caddr_t)outpack;
135355163Sshin	iov[0].iov_len = cc;
135455163Sshin	smsghdr.msg_iov = iov;
135555163Sshin	smsghdr.msg_iovlen = 1;
135655163Sshin
135755163Sshin	i = sendmsg(s, &smsghdr, 0);
135855163Sshin
135955163Sshin	if (i < 0 || i != cc)  {
136055163Sshin		if (i < 0)
136155163Sshin			warn("sendmsg");
136255163Sshin		(void)printf("ping6: wrote %s %d chars, ret=%d\n",
136355163Sshin		    hostname, cc, i);
136455163Sshin	}
136555163Sshin	if (!(options & F_QUIET) && options & F_FLOOD)
136655163Sshin		(void)write(STDOUT_FILENO, &DOT, 1);
136778984Sume
136878984Sume	return(0);
136955163Sshin}
137055163Sshin
137178064Sumeint
1372216561Scharniermyechoreply(const struct icmp6_hdr *icp)
137378064Sume{
137478064Sume	if (ntohs(icp->icmp6_id) == ident)
137578064Sume		return 1;
137678064Sume	else
137778064Sume		return 0;
137878064Sume}
137978064Sume
138078064Sumeint
1381216561Scharniermynireply(const struct icmp6_nodeinfo *nip)
138278064Sume{
138378064Sume	if (memcmp(nip->icmp6_ni_nonce + sizeof(u_int16_t),
138478064Sume	    nonce + sizeof(u_int16_t),
138578064Sume	    sizeof(nonce) - sizeof(u_int16_t)) == 0)
138678064Sume		return 1;
138778064Sume	else
138878064Sume		return 0;
138978064Sume}
139078064Sume
139178064Sumechar *
1392216561Scharnierdnsdecode(const u_char **sp, const u_char *ep, const u_char *base, char *buf,
1393216561Scharnier	size_t bufsiz)
1394216561Scharnier	/*base for compressed name*/
139578064Sume{
139678064Sume	int i;
139778064Sume	const u_char *cp;
139878064Sume	char cresult[MAXDNAME + 1];
139978064Sume	const u_char *comp;
140078064Sume	int l;
140178064Sume
140278064Sume	cp = *sp;
140378064Sume	*buf = '\0';
140478064Sume
140578064Sume	if (cp >= ep)
140678064Sume		return NULL;
140778064Sume	while (cp < ep) {
140878064Sume		i = *cp;
140978064Sume		if (i == 0 || cp != *sp) {
1410121472Sume			if (strlcat((char *)buf, ".", bufsiz) >= bufsiz)
141178064Sume				return NULL;	/*result overrun*/
141278064Sume		}
141378064Sume		if (i == 0)
141478064Sume			break;
141578064Sume		cp++;
141678064Sume
141778064Sume		if ((i & 0xc0) == 0xc0 && cp - base > (i & 0x3f)) {
141878064Sume			/* DNS compression */
141978064Sume			if (!base)
142078064Sume				return NULL;
142178064Sume
142278064Sume			comp = base + (i & 0x3f);
142378064Sume			if (dnsdecode(&comp, cp, base, cresult,
142478064Sume			    sizeof(cresult)) == NULL)
142578064Sume				return NULL;
142678064Sume			if (strlcat(buf, cresult, bufsiz) >= bufsiz)
142778064Sume				return NULL;	/*result overrun*/
142878064Sume			break;
142978064Sume		} else if ((i & 0x3f) == i) {
143078064Sume			if (i > ep - cp)
143178064Sume				return NULL;	/*source overrun*/
143278064Sume			while (i-- > 0 && cp < ep) {
143378064Sume				l = snprintf(cresult, sizeof(cresult),
143478064Sume				    isprint(*cp) ? "%c" : "\\%03o", *cp & 0xff);
1435121472Sume				if (l >= sizeof(cresult) || l < 0)
143678064Sume					return NULL;
143778064Sume				if (strlcat(buf, cresult, bufsiz) >= bufsiz)
143878064Sume					return NULL;	/*result overrun*/
143978064Sume				cp++;
144078064Sume			}
144178064Sume		} else
144278064Sume			return NULL;	/*invalid label*/
144378064Sume	}
144478064Sume	if (i != 0)
144578064Sume		return NULL;	/*not terminated*/
144678064Sume	cp++;
144778064Sume	*sp = cp;
144878064Sume	return buf;
144978064Sume}
145078064Sume
145155163Sshin/*
145255163Sshin * pr_pack --
145355163Sshin *	Print out the packet, if it came from us.  This logic is necessary
145455163Sshin * because ALL readers of the ICMP socket get a copy of ALL ICMP packets
145555163Sshin * which arrive ('tis only fair).  This permits multiple copies of this
145655163Sshin * program to be run without having intermingled output (or statistics!).
145755163Sshin */
145855163Sshinvoid
1459216561Scharnierpr_pack(u_char *buf, int cc, struct msghdr *mhdr)
146055163Sshin{
146162627Skris#define safeputc(c)	printf((isprint((c)) ? "%c" : "\\%03o"), c)
146255163Sshin	struct icmp6_hdr *icp;
146378064Sume	struct icmp6_nodeinfo *ni;
146455163Sshin	int i;
146555163Sshin	int hoplim;
146678064Sume	struct sockaddr *from;
146778064Sume	int fromlen;
146855163Sshin	u_char *cp = NULL, *dp, *end = buf + cc;
146962627Skris	struct in6_pktinfo *pktinfo = NULL;
1470121472Sume	struct timeval tv, tp;
1471121472Sume	struct tv32 *tpp;
147255163Sshin	double triptime = 0;
147355163Sshin	int dupflag;
147455163Sshin	size_t off;
147562627Skris	int oldfqdn;
147678064Sume	u_int16_t seq;
147778064Sume	char dnsname[MAXDNAME + 1];
147855163Sshin
147955163Sshin	(void)gettimeofday(&tv, NULL);
148055163Sshin
148178064Sume	if (!mhdr || !mhdr->msg_name ||
148278064Sume	    mhdr->msg_namelen != sizeof(struct sockaddr_in6) ||
148378064Sume	    ((struct sockaddr *)mhdr->msg_name)->sa_family != AF_INET6) {
148478064Sume		if (options & F_VERBOSE)
1485121472Sume			warnx("invalid peername");
148678064Sume		return;
148778064Sume	}
148878064Sume	from = (struct sockaddr *)mhdr->msg_name;
148978064Sume	fromlen = mhdr->msg_namelen;
149055163Sshin	if (cc < sizeof(struct icmp6_hdr)) {
149155163Sshin		if (options & F_VERBOSE)
149287668Scharnier			warnx("packet too short (%d bytes) from %s", cc,
149378064Sume			    pr_addr(from, fromlen));
149455163Sshin		return;
149555163Sshin	}
1496168866Smtm	if (((mhdr->msg_flags & MSG_CTRUNC) != 0) &&
1497168866Smtm	    (options & F_VERBOSE) != 0)
1498168866Smtm		warnx("some control data discarded, insufficient buffer size");
149955163Sshin	icp = (struct icmp6_hdr *)buf;
150078064Sume	ni = (struct icmp6_nodeinfo *)buf;
150155163Sshin	off = 0;
150255163Sshin
150355163Sshin	if ((hoplim = get_hoplim(mhdr)) == -1) {
150455163Sshin		warnx("failed to get receiving hop limit");
150555163Sshin		return;
150655163Sshin	}
150762627Skris	if ((pktinfo = get_rcvpktinfo(mhdr)) == NULL) {
150887668Scharnier		warnx("failed to get receiving packet information");
150962627Skris		return;
151062627Skris	}
151155163Sshin
151278064Sume	if (icp->icmp6_type == ICMP6_ECHO_REPLY && myechoreply(icp)) {
151378064Sume		seq = ntohs(icp->icmp6_seq);
151455163Sshin		++nreceived;
151555163Sshin		if (timing) {
1516121472Sume			tpp = (struct tv32 *)(icp + 1);
1517121472Sume			tp.tv_sec = ntohl(tpp->tv32_sec);
1518121472Sume			tp.tv_usec = ntohl(tpp->tv32_usec);
1519121472Sume			tvsub(&tv, &tp);
152055163Sshin			triptime = ((double)tv.tv_sec) * 1000.0 +
152155163Sshin			    ((double)tv.tv_usec) / 1000.0;
152255163Sshin			tsum += triptime;
152378064Sume			tsumsq += triptime * triptime;
152455163Sshin			if (triptime < tmin)
152555163Sshin				tmin = triptime;
152655163Sshin			if (triptime > tmax)
152755163Sshin				tmax = triptime;
152855163Sshin		}
152955163Sshin
153078064Sume		if (TST(seq % mx_dup_ck)) {
153155163Sshin			++nrepeats;
153255163Sshin			--nreceived;
153355163Sshin			dupflag = 1;
153455163Sshin		} else {
153578064Sume			SET(seq % mx_dup_ck);
153655163Sshin			dupflag = 0;
153755163Sshin		}
153855163Sshin
153955163Sshin		if (options & F_QUIET)
154055163Sshin			return;
154155163Sshin
1542285820Shrs		if (options & F_WAITTIME && triptime > waittime) {
1543285820Shrs			++nrcvtimeout;
1544285820Shrs			return;
1545285820Shrs		}
1546285820Shrs
154755163Sshin		if (options & F_FLOOD)
154855163Sshin			(void)write(STDOUT_FILENO, &BSPACE, 1);
154955163Sshin		else {
1550182195Smatteo			if (options & F_AUDIBLE)
1551182195Smatteo				(void)write(STDOUT_FILENO, &BBELL, 1);
155255163Sshin			(void)printf("%d bytes from %s, icmp_seq=%u", cc,
155378064Sume			    pr_addr(from, fromlen), seq);
155455163Sshin			(void)printf(" hlim=%d", hoplim);
155562627Skris			if ((options & F_VERBOSE) != 0) {
155662627Skris				struct sockaddr_in6 dstsa;
155762627Skris
155862627Skris				memset(&dstsa, 0, sizeof(dstsa));
155962627Skris				dstsa.sin6_family = AF_INET6;
156062627Skris				dstsa.sin6_len = sizeof(dstsa);
156162627Skris				dstsa.sin6_scope_id = pktinfo->ipi6_ifindex;
156262627Skris				dstsa.sin6_addr = pktinfo->ipi6_addr;
156378064Sume				(void)printf(" dst=%s",
156478064Sume				    pr_addr((struct sockaddr *)&dstsa,
156578064Sume				    sizeof(dstsa)));
156662627Skris			}
156755163Sshin			if (timing)
1568117824Smaxim				(void)printf(" time=%.3f ms", triptime);
156955163Sshin			if (dupflag)
157055163Sshin				(void)printf("(DUP!)");
157155163Sshin			/* check the data */
157255163Sshin			cp = buf + off + ICMP6ECHOLEN + ICMP6ECHOTMLEN;
157355163Sshin			dp = outpack + ICMP6ECHOLEN + ICMP6ECHOTMLEN;
157455163Sshin			for (i = 8; cp < end; ++i, ++cp, ++dp) {
157555163Sshin				if (*cp != *dp) {
157655163Sshin					(void)printf("\nwrong data byte #%d should be 0x%x but was 0x%x", i, *dp, *cp);
157755163Sshin					break;
157855163Sshin				}
157955163Sshin			}
158055163Sshin		}
158178064Sume	} else if (icp->icmp6_type == ICMP6_NI_REPLY && mynireply(ni)) {
158278064Sume		seq = ntohs(*(u_int16_t *)ni->icmp6_ni_nonce);
158378064Sume		++nreceived;
158478064Sume		if (TST(seq % mx_dup_ck)) {
158578064Sume			++nrepeats;
158678064Sume			--nreceived;
158778064Sume			dupflag = 1;
158878064Sume		} else {
158978064Sume			SET(seq % mx_dup_ck);
159078064Sume			dupflag = 0;
159178064Sume		}
159255163Sshin
159378064Sume		if (options & F_QUIET)
159478064Sume			return;
159555163Sshin
159678064Sume		(void)printf("%d bytes from %s: ", cc, pr_addr(from, fromlen));
159778064Sume
159878064Sume		switch (ntohs(ni->ni_code)) {
159978064Sume		case ICMP6_NI_SUCCESS:
160078064Sume			break;
160178064Sume		case ICMP6_NI_REFUSED:
160278064Sume			printf("refused, type 0x%x", ntohs(ni->ni_type));
160378064Sume			goto fqdnend;
160478064Sume		case ICMP6_NI_UNKNOWN:
160578064Sume			printf("unknown, type 0x%x", ntohs(ni->ni_type));
160678064Sume			goto fqdnend;
160778064Sume		default:
160878064Sume			printf("unknown code 0x%x, type 0x%x",
160978064Sume			    ntohs(ni->ni_code), ntohs(ni->ni_type));
161078064Sume			goto fqdnend;
161178064Sume		}
161278064Sume
161378064Sume		switch (ntohs(ni->ni_qtype)) {
161478064Sume		case NI_QTYPE_NOOP:
161578064Sume			printf("NodeInfo NOOP");
161678064Sume			break;
161778064Sume		case NI_QTYPE_SUPTYPES:
161878064Sume			pr_suptypes(ni, end - (u_char *)ni);
161978064Sume			break;
162078064Sume		case NI_QTYPE_NODEADDR:
162178064Sume			pr_nodeaddr(ni, end - (u_char *)ni);
162278064Sume			break;
162378064Sume		case NI_QTYPE_FQDN:
162478064Sume		default:	/* XXX: for backward compatibility */
162562627Skris			cp = (u_char *)ni + ICMP6_NIRLEN;
162662627Skris			if (buf[off + ICMP6_NIRLEN] ==
162762627Skris			    cc - off - ICMP6_NIRLEN - 1)
162862627Skris				oldfqdn = 1;
162962627Skris			else
163062627Skris				oldfqdn = 0;
163162627Skris			if (oldfqdn) {
163278064Sume				cp++;	/* skip length */
163362627Skris				while (cp < end) {
163462627Skris					safeputc(*cp & 0xff);
163562627Skris					cp++;
163662627Skris				}
163762627Skris			} else {
163878064Sume				i = 0;
163962627Skris				while (cp < end) {
164078064Sume					if (dnsdecode((const u_char **)&cp, end,
164178064Sume					    (const u_char *)(ni + 1), dnsname,
164278064Sume					    sizeof(dnsname)) == NULL) {
164378064Sume						printf("???");
164462627Skris						break;
164562627Skris					}
164678064Sume					/*
164778064Sume					 * name-lookup special handling for
164878064Sume					 * truncated name
164978064Sume					 */
165078064Sume					if (cp + 1 <= end && !*cp &&
165178064Sume					    strlen(dnsname) > 0) {
165278064Sume						dnsname[strlen(dnsname) - 1] = '\0';
165378064Sume						cp++;
165478064Sume					}
165578064Sume					printf("%s%s", i > 0 ? "," : "",
165678064Sume					    dnsname);
165762627Skris				}
165862627Skris			}
165962627Skris			if (options & F_VERBOSE) {
166062627Skris				int32_t ttl;
166162627Skris				int comma = 0;
166255163Sshin
166362627Skris				(void)printf(" (");	/*)*/
166455163Sshin
166578064Sume				switch (ni->ni_code) {
166662627Skris				case ICMP6_NI_REFUSED:
166762627Skris					(void)printf("refused");
166862627Skris					comma++;
166962627Skris					break;
167062627Skris				case ICMP6_NI_UNKNOWN:
167187668Scharnier					(void)printf("unknown qtype");
167262627Skris					comma++;
167362627Skris					break;
167462627Skris				}
167555163Sshin
167662627Skris				if ((end - (u_char *)ni) < ICMP6_NIRLEN) {
167778064Sume					/* case of refusion, unknown */
167878064Sume					/*(*/
167978064Sume					putchar(')');
168062627Skris					goto fqdnend;
168162627Skris				}
168262627Skris				ttl = (int32_t)ntohl(*(u_long *)&buf[off+ICMP6ECHOLEN+8]);
168362627Skris				if (comma)
168462627Skris					printf(",");
168578064Sume				if (!(ni->ni_flags & NI_FQDN_FLAG_VALIDTTL)) {
168662627Skris					(void)printf("TTL=%d:meaningless",
168778064Sume					    (int)ttl);
168878064Sume				} else {
168962627Skris					if (ttl < 0) {
169062627Skris						(void)printf("TTL=%d:invalid",
169178064Sume						   ttl);
169262627Skris					} else
169362627Skris						(void)printf("TTL=%d", ttl);
169462627Skris				}
169562627Skris				comma++;
169655163Sshin
169762627Skris				if (oldfqdn) {
169862627Skris					if (comma)
169962627Skris						printf(",");
170062627Skris					printf("03 draft");
170162627Skris					comma++;
170262627Skris				} else {
170362627Skris					cp = (u_char *)ni + ICMP6_NIRLEN;
170462627Skris					if (cp == end) {
170562627Skris						if (comma)
170662627Skris							printf(",");
170762627Skris						printf("no name");
170862627Skris						comma++;
170962627Skris					}
171062627Skris				}
171162627Skris
171262627Skris				if (buf[off + ICMP6_NIRLEN] !=
171362627Skris				    cc - off - ICMP6_NIRLEN - 1 && oldfqdn) {
171462627Skris					if (comma)
171562627Skris						printf(",");
171662627Skris					(void)printf("invalid namelen:%d/%lu",
171778064Sume					    buf[off + ICMP6_NIRLEN],
171878064Sume					    (u_long)cc - off - ICMP6_NIRLEN - 1);
171962627Skris					comma++;
172062627Skris				}
172162627Skris				/*(*/
172262627Skris				putchar(')');
172362627Skris			}
172478064Sume		fqdnend:
172562627Skris			;
172655163Sshin		}
172755163Sshin	} else {
172855163Sshin		/* We've got something other than an ECHOREPLY */
172955163Sshin		if (!(options & F_VERBOSE))
173055163Sshin			return;
173178064Sume		(void)printf("%d bytes from %s: ", cc, pr_addr(from, fromlen));
173255163Sshin		pr_icmph(icp, end);
173355163Sshin	}
173455163Sshin
173555163Sshin	if (!(options & F_FLOOD)) {
173655163Sshin		(void)putchar('\n');
173762627Skris		if (options & F_VERBOSE)
173862627Skris			pr_exthdrs(mhdr);
173955163Sshin		(void)fflush(stdout);
174055163Sshin	}
174162627Skris#undef safeputc
174255163Sshin}
174355163Sshin
174455163Sshinvoid
1745216561Scharnierpr_exthdrs(struct msghdr *mhdr)
174662627Skris{
1747168866Smtm	ssize_t	bufsize;
1748168866Smtm	void	*bufp;
174962627Skris	struct cmsghdr *cm;
175062627Skris
1751168866Smtm	bufsize = 0;
1752168866Smtm	bufp = mhdr->msg_control;
175362627Skris	for (cm = (struct cmsghdr *)CMSG_FIRSTHDR(mhdr); cm;
175462627Skris	     cm = (struct cmsghdr *)CMSG_NXTHDR(mhdr, cm)) {
175562627Skris		if (cm->cmsg_level != IPPROTO_IPV6)
175662627Skris			continue;
175762627Skris
1758168866Smtm		bufsize = CONTROLLEN - ((caddr_t)CMSG_DATA(cm) - (caddr_t)bufp);
1759168866Smtm		if (bufsize <= 0)
1760168866Smtm			continue;
176178064Sume		switch (cm->cmsg_type) {
176262627Skris		case IPV6_HOPOPTS:
176362627Skris			printf("  HbH Options: ");
1764168866Smtm			pr_ip6opt(CMSG_DATA(cm), (size_t)bufsize);
176562627Skris			break;
176662627Skris		case IPV6_DSTOPTS:
176762627Skris#ifdef IPV6_RTHDRDSTOPTS
176862627Skris		case IPV6_RTHDRDSTOPTS:
176962627Skris#endif
177062627Skris			printf("  Dst Options: ");
1771168866Smtm			pr_ip6opt(CMSG_DATA(cm), (size_t)bufsize);
177262627Skris			break;
177362627Skris		case IPV6_RTHDR:
177462627Skris			printf("  Routing: ");
1775168866Smtm			pr_rthdr(CMSG_DATA(cm), (size_t)bufsize);
177662627Skris			break;
177762627Skris		}
177862627Skris	}
177962627Skris}
178062627Skris
178162627Skris#ifdef USE_RFC2292BIS
178262627Skrisvoid
1783168866Smtmpr_ip6opt(void *extbuf, size_t bufsize)
178462627Skris{
178562627Skris	struct ip6_hbh *ext;
178662627Skris	int currentlen;
178762627Skris	u_int8_t type;
1788229912Seadler	socklen_t extlen, len;
178962627Skris	void *databuf;
179062627Skris	size_t offset;
179162627Skris	u_int16_t value2;
179262627Skris	u_int32_t value4;
179362627Skris
179462627Skris	ext = (struct ip6_hbh *)extbuf;
179562627Skris	extlen = (ext->ip6h_len + 1) * 8;
179678064Sume	printf("nxt %u, len %u (%lu bytes)\n", ext->ip6h_nxt,
179778064Sume	    (unsigned int)ext->ip6h_len, (unsigned long)extlen);
179862627Skris
1799168866Smtm	/*
1800168866Smtm	 * Bounds checking on the ancillary data buffer:
1801168866Smtm	 *     subtract the size of a cmsg structure from the buffer size.
1802168866Smtm	 */
1803168866Smtm	if (bufsize < (extlen  + CMSG_SPACE(0))) {
1804168866Smtm		extlen = bufsize - CMSG_SPACE(0);
1805168866Smtm		warnx("options truncated, showing only %u (total=%u)",
1806168866Smtm		    (unsigned int)(extlen / 8 - 1),
1807168866Smtm		    (unsigned int)(ext->ip6h_len));
1808168866Smtm	}
1809168866Smtm
181062627Skris	currentlen = 0;
181162627Skris	while (1) {
181262627Skris		currentlen = inet6_opt_next(extbuf, extlen, currentlen,
181378064Sume		    &type, &len, &databuf);
181462627Skris		if (currentlen == -1)
181562627Skris			break;
181662627Skris		switch (type) {
181762627Skris		/*
181862627Skris		 * Note that inet6_opt_next automatically skips any padding
181962627Skris		 * optins.
182062627Skris		 */
182162627Skris		case IP6OPT_JUMBO:
182262627Skris			offset = 0;
182362627Skris			offset = inet6_opt_get_val(databuf, offset,
182478064Sume			    &value4, sizeof(value4));
182562627Skris			printf("    Jumbo Payload Opt: Length %u\n",
182678064Sume			    (u_int32_t)ntohl(value4));
182762627Skris			break;
182862627Skris		case IP6OPT_ROUTER_ALERT:
182962627Skris			offset = 0;
183062627Skris			offset = inet6_opt_get_val(databuf, offset,
183162627Skris						   &value2, sizeof(value2));
183262627Skris			printf("    Router Alert Opt: Type %u\n",
183378064Sume			    ntohs(value2));
183462627Skris			break;
183562627Skris		default:
183678064Sume			printf("    Received Opt %u len %lu\n",
183778064Sume			    type, (unsigned long)len);
183862627Skris			break;
183962627Skris		}
184062627Skris	}
184162627Skris	return;
184262627Skris}
184362627Skris#else  /* !USE_RFC2292BIS */
184462627Skris/* ARGSUSED */
184562627Skrisvoid
1846168866Smtmpr_ip6opt(void *extbuf, size_t bufsize __unused)
184762627Skris{
184862627Skris	putchar('\n');
184962627Skris	return;
185062627Skris}
185162627Skris#endif /* USE_RFC2292BIS */
185262627Skris
185362627Skris#ifdef USE_RFC2292BIS
185462627Skrisvoid
1855168866Smtmpr_rthdr(void *extbuf, size_t bufsize)
185662627Skris{
185762627Skris	struct in6_addr *in6;
185862627Skris	char ntopbuf[INET6_ADDRSTRLEN];
185962627Skris	struct ip6_rthdr *rh = (struct ip6_rthdr *)extbuf;
1860168866Smtm	int i, segments, origsegs, rthsize, size0, size1;
186162627Skris
186262627Skris	/* print fixed part of the header */
186362627Skris	printf("nxt %u, len %u (%d bytes), type %u, ", rh->ip6r_nxt,
186478064Sume	    rh->ip6r_len, (rh->ip6r_len + 1) << 3, rh->ip6r_type);
1865168866Smtm	if ((segments = inet6_rth_segments(extbuf)) >= 0) {
186662627Skris		printf("%d segments, ", segments);
1867168866Smtm		printf("%d left\n", rh->ip6r_segleft);
1868168866Smtm	} else {
186962627Skris		printf("segments unknown, ");
1870168866Smtm		printf("%d left\n", rh->ip6r_segleft);
1871168866Smtm		return;
1872168866Smtm	}
187362627Skris
1874168866Smtm	/*
1875168866Smtm	 * Bounds checking on the ancillary data buffer. When calculating
1876168866Smtm	 * the number of items to show keep in mind:
1877168866Smtm	 *	- The size of the cmsg structure
1878168866Smtm	 *	- The size of one segment (the size of a Type 0 routing header)
1879168866Smtm	 *	- When dividing add a fudge factor of one in case the
1880168866Smtm	 *	  dividend is not evenly divisible by the divisor
1881168866Smtm	 */
1882168866Smtm	rthsize = (rh->ip6r_len + 1) * 8;
1883168866Smtm	if (bufsize < (rthsize + CMSG_SPACE(0))) {
1884168866Smtm		origsegs = segments;
1885168866Smtm		size0 = inet6_rth_space(IPV6_RTHDR_TYPE_0, 0);
1886168866Smtm		size1 = inet6_rth_space(IPV6_RTHDR_TYPE_0, 1);
1887168866Smtm		segments -= (rthsize - (bufsize - CMSG_SPACE(0))) /
1888168866Smtm		    (size1 - size0) + 1;
1889168866Smtm		warnx("segments truncated, showing only %d (total=%d)",
1890168866Smtm		    segments, origsegs);
1891168866Smtm	}
1892168866Smtm
189362627Skris	for (i = 0; i < segments; i++) {
189462627Skris		in6 = inet6_rth_getaddr(extbuf, i);
189562627Skris		if (in6 == NULL)
189662627Skris			printf("   [%d]<NULL>\n", i);
189778064Sume		else {
189878064Sume			if (!inet_ntop(AF_INET6, in6, ntopbuf,
189978064Sume			    sizeof(ntopbuf)))
1900121472Sume				strlcpy(ntopbuf, "?", sizeof(ntopbuf));
190178064Sume			printf("   [%d]%s\n", i, ntopbuf);
190278064Sume		}
190362627Skris	}
190462627Skris
190562627Skris	return;
190678064Sume
190762627Skris}
190878064Sume
190962627Skris#else  /* !USE_RFC2292BIS */
191062627Skris/* ARGSUSED */
191162627Skrisvoid
1912168866Smtmpr_rthdr(void *extbuf, size_t bufsize __unused)
191362627Skris{
191462627Skris	putchar('\n');
191562627Skris	return;
191662627Skris}
191762627Skris#endif /* USE_RFC2292BIS */
191862627Skris
191978064Sumeint
1920216561Scharnierpr_bitrange(u_int32_t v, int soff, int ii)
192178064Sume{
192278064Sume	int off;
192378064Sume	int i;
192462627Skris
192578064Sume	off = 0;
192678064Sume	while (off < 32) {
192778064Sume		/* shift till we have 0x01 */
192878064Sume		if ((v & 0x01) == 0) {
192978064Sume			if (ii > 1)
1930121472Sume				printf("-%u", soff + off - 1);
193178064Sume			ii = 0;
193278064Sume			switch (v & 0x0f) {
193378064Sume			case 0x00:
193478064Sume				v >>= 4;
193578064Sume				off += 4;
193678064Sume				continue;
193778064Sume			case 0x08:
193878064Sume				v >>= 3;
193978064Sume				off += 3;
194078064Sume				continue;
194178064Sume			case 0x04: case 0x0c:
194278064Sume				v >>= 2;
194378064Sume				off += 2;
194478064Sume				continue;
194578064Sume			default:
194678064Sume				v >>= 1;
194778064Sume				off += 1;
194878064Sume				continue;
194978064Sume			}
195078064Sume		}
195178064Sume
195278064Sume		/* we have 0x01 with us */
195378064Sume		for (i = 0; i < 32 - off; i++) {
195478064Sume			if ((v & (0x01 << i)) == 0)
195578064Sume				break;
195678064Sume		}
195778064Sume		if (!ii)
1958121472Sume			printf(" %u", soff + off);
195978064Sume		ii += i;
196078064Sume		v >>= i; off += i;
196178064Sume	}
196278064Sume	return ii;
196378064Sume}
196478064Sume
196562627Skrisvoid
1966216561Scharnierpr_suptypes(struct icmp6_nodeinfo *ni, size_t nilen)
1967216561Scharnier	/* ni->qtype must be SUPTYPES */
196878064Sume{
196978064Sume	size_t clen;
197078064Sume	u_int32_t v;
197178064Sume	const u_char *cp, *end;
197278064Sume	u_int16_t cur;
197378064Sume	struct cbit {
197478064Sume		u_int16_t words;	/*32bit count*/
197578064Sume		u_int16_t skip;
197678064Sume	} cbit;
197778064Sume#define MAXQTYPES	(1 << 16)
197878064Sume	size_t off;
197978064Sume	int b;
198078064Sume
198178064Sume	cp = (u_char *)(ni + 1);
198278064Sume	end = ((u_char *)ni) + nilen;
198378064Sume	cur = 0;
198478064Sume	b = 0;
198578064Sume
198678064Sume	printf("NodeInfo Supported Qtypes");
198778064Sume	if (options & F_VERBOSE) {
198878064Sume		if (ni->ni_flags & NI_SUPTYPE_FLAG_COMPRESS)
198978064Sume			printf(", compressed bitmap");
199078064Sume		else
199178064Sume			printf(", raw bitmap");
199278064Sume	}
199378064Sume
199478064Sume	while (cp < end) {
199578064Sume		clen = (size_t)(end - cp);
199678064Sume		if ((ni->ni_flags & NI_SUPTYPE_FLAG_COMPRESS) == 0) {
199778064Sume			if (clen == 0 || clen > MAXQTYPES / 8 ||
199878064Sume			    clen % sizeof(v)) {
199978064Sume				printf("???");
200078064Sume				return;
200178064Sume			}
200278064Sume		} else {
200378064Sume			if (clen < sizeof(cbit) || clen % sizeof(v))
200478064Sume				return;
200578064Sume			memcpy(&cbit, cp, sizeof(cbit));
200678064Sume			if (sizeof(cbit) + ntohs(cbit.words) * sizeof(v) >
200778064Sume			    clen)
200878064Sume				return;
200978064Sume			cp += sizeof(cbit);
201078064Sume			clen = ntohs(cbit.words) * sizeof(v);
201178064Sume			if (cur + clen * 8 + (u_long)ntohs(cbit.skip) * 32 >
201278064Sume			    MAXQTYPES)
201378064Sume				return;
201478064Sume		}
201578064Sume
201678064Sume		for (off = 0; off < clen; off += sizeof(v)) {
201778064Sume			memcpy(&v, cp + off, sizeof(v));
201878064Sume			v = (u_int32_t)ntohl(v);
201978064Sume			b = pr_bitrange(v, (int)(cur + off * 8), b);
202078064Sume		}
202178064Sume		/* flush the remaining bits */
202278064Sume		b = pr_bitrange(0, (int)(cur + off * 8), b);
202378064Sume
202478064Sume		cp += clen;
202578064Sume		cur += clen * 8;
202678064Sume		if ((ni->ni_flags & NI_SUPTYPE_FLAG_COMPRESS) != 0)
202778064Sume			cur += ntohs(cbit.skip) * 32;
202878064Sume	}
202978064Sume}
203078064Sume
203178064Sumevoid
2032216561Scharnierpr_nodeaddr(struct icmp6_nodeinfo *ni, int nilen)
2033216561Scharnier	/* ni->qtype must be NODEADDR */
203455163Sshin{
203578064Sume	u_char *cp = (u_char *)(ni + 1);
203662627Skris	char ntop_buf[INET6_ADDRSTRLEN];
203778064Sume	int withttl = 0;
203855163Sshin
203955163Sshin	nilen -= sizeof(struct icmp6_nodeinfo);
204055163Sshin
204155163Sshin	if (options & F_VERBOSE) {
204278064Sume		switch (ni->ni_code) {
204378064Sume		case ICMP6_NI_REFUSED:
204478064Sume			(void)printf("refused");
204578064Sume			break;
204678064Sume		case ICMP6_NI_UNKNOWN:
204778064Sume			(void)printf("unknown qtype");
204878064Sume			break;
204955163Sshin		}
205078064Sume		if (ni->ni_flags & NI_NODEADDR_FLAG_TRUNCATE)
205155163Sshin			(void)printf(" truncated");
205255163Sshin	}
205355163Sshin	putchar('\n');
205455163Sshin	if (nilen <= 0)
205555163Sshin		printf("  no address\n");
205678064Sume
205778064Sume	/*
205878064Sume	 * In icmp-name-lookups 05 and later, TTL of each returned address
205978064Sume	 * is contained in the resposne. We try to detect the version
206078064Sume	 * by the length of the data, but note that the detection algorithm
206178064Sume	 * is incomplete. We assume the latest draft by default.
206278064Sume	 */
206378064Sume	if (nilen % (sizeof(u_int32_t) + sizeof(struct in6_addr)) == 0)
206478064Sume		withttl = 1;
206578064Sume	while (nilen > 0) {
206678064Sume		u_int32_t ttl;
206778064Sume
206878064Sume		if (withttl) {
206978064Sume			/* XXX: alignment? */
207078064Sume			ttl = (u_int32_t)ntohl(*(u_int32_t *)cp);
207178064Sume			cp += sizeof(u_int32_t);
207278064Sume			nilen -= sizeof(u_int32_t);
207378064Sume		}
207478064Sume
207578064Sume		if (inet_ntop(AF_INET6, cp, ntop_buf, sizeof(ntop_buf)) ==
207678064Sume		    NULL)
2077121472Sume			strlcpy(ntop_buf, "?", sizeof(ntop_buf));
207878064Sume		printf("  %s", ntop_buf);
207978064Sume		if (withttl) {
208078064Sume			if (ttl == 0xffffffff) {
208178064Sume				/*
208278064Sume				 * XXX: can this convention be applied to all
208378064Sume				 * type of TTL (i.e. non-ND TTL)?
208478064Sume				 */
208578064Sume				printf("(TTL=infty)");
208678064Sume			}
208778064Sume			else
208878064Sume				printf("(TTL=%u)", ttl);
208978064Sume		}
209078064Sume		putchar('\n');
209178064Sume
209278064Sume		nilen -= sizeof(struct in6_addr);
209378064Sume		cp += sizeof(struct in6_addr);
209455163Sshin	}
209555163Sshin}
209655163Sshin
209755163Sshinint
2098216561Scharnierget_hoplim(struct msghdr *mhdr)
209955163Sshin{
210055163Sshin	struct cmsghdr *cm;
210155163Sshin
210255163Sshin	for (cm = (struct cmsghdr *)CMSG_FIRSTHDR(mhdr); cm;
210355163Sshin	     cm = (struct cmsghdr *)CMSG_NXTHDR(mhdr, cm)) {
210478064Sume		if (cm->cmsg_len == 0)
210578064Sume			return(-1);
210678064Sume
210755163Sshin		if (cm->cmsg_level == IPPROTO_IPV6 &&
210855163Sshin		    cm->cmsg_type == IPV6_HOPLIMIT &&
210955163Sshin		    cm->cmsg_len == CMSG_LEN(sizeof(int)))
211055163Sshin			return(*(int *)CMSG_DATA(cm));
211155163Sshin	}
211255163Sshin
211355163Sshin	return(-1);
211455163Sshin}
211555163Sshin
211662627Skrisstruct in6_pktinfo *
2117216561Scharnierget_rcvpktinfo(struct msghdr *mhdr)
211862627Skris{
211962627Skris	struct cmsghdr *cm;
212062627Skris
212162627Skris	for (cm = (struct cmsghdr *)CMSG_FIRSTHDR(mhdr); cm;
212262627Skris	     cm = (struct cmsghdr *)CMSG_NXTHDR(mhdr, cm)) {
212378064Sume		if (cm->cmsg_len == 0)
212478064Sume			return(NULL);
212578064Sume
212662627Skris		if (cm->cmsg_level == IPPROTO_IPV6 &&
212762627Skris		    cm->cmsg_type == IPV6_PKTINFO &&
212862627Skris		    cm->cmsg_len == CMSG_LEN(sizeof(struct in6_pktinfo)))
212962627Skris			return((struct in6_pktinfo *)CMSG_DATA(cm));
213062627Skris	}
213162627Skris
213262627Skris	return(NULL);
213362627Skris}
213462627Skris
213578064Sumeint
2136216561Scharnierget_pathmtu(struct msghdr *mhdr)
213778064Sume{
213878064Sume#ifdef IPV6_RECVPATHMTU
213978064Sume	struct cmsghdr *cm;
214078064Sume	struct ip6_mtuinfo *mtuctl = NULL;
214178064Sume
214278064Sume	for (cm = (struct cmsghdr *)CMSG_FIRSTHDR(mhdr); cm;
214378064Sume	     cm = (struct cmsghdr *)CMSG_NXTHDR(mhdr, cm)) {
214478064Sume		if (cm->cmsg_len == 0)
214578064Sume			return(0);
214678064Sume
214778064Sume		if (cm->cmsg_level == IPPROTO_IPV6 &&
214878064Sume		    cm->cmsg_type == IPV6_PATHMTU &&
214978064Sume		    cm->cmsg_len == CMSG_LEN(sizeof(struct ip6_mtuinfo))) {
215078064Sume			mtuctl = (struct ip6_mtuinfo *)CMSG_DATA(cm);
215178064Sume
215278064Sume			/*
215378064Sume			 * If the notified destination is different from
215478064Sume			 * the one we are pinging, just ignore the info.
215578064Sume			 * We check the scope ID only when both notified value
215678064Sume			 * and our own value have non-0 values, because we may
215778064Sume			 * have used the default scope zone ID for sending,
215878064Sume			 * in which case the scope ID value is 0.
215978064Sume			 */
216078064Sume			if (!IN6_ARE_ADDR_EQUAL(&mtuctl->ip6m_addr.sin6_addr,
216178064Sume						&dst.sin6_addr) ||
216278064Sume			    (mtuctl->ip6m_addr.sin6_scope_id &&
216378064Sume			     dst.sin6_scope_id &&
216478064Sume			     mtuctl->ip6m_addr.sin6_scope_id !=
216578064Sume			     dst.sin6_scope_id)) {
216678064Sume				if ((options & F_VERBOSE) != 0) {
216778064Sume					printf("path MTU for %s is notified. "
216878064Sume					       "(ignored)\n",
216978064Sume					   pr_addr((struct sockaddr *)&mtuctl->ip6m_addr,
217078064Sume					   sizeof(mtuctl->ip6m_addr)));
217178064Sume				}
217278064Sume				return(0);
217378064Sume			}
217478064Sume
217578064Sume			/*
217678064Sume			 * Ignore an invalid MTU. XXX: can we just believe
217778064Sume			 * the kernel check?
217878064Sume			 */
217978064Sume			if (mtuctl->ip6m_mtu < IPV6_MMTU)
218078064Sume				return(0);
218178064Sume
218278064Sume			/* notification for our destination. return the MTU. */
218378064Sume			return((int)mtuctl->ip6m_mtu);
218478064Sume		}
218578064Sume	}
218678064Sume#endif
218778064Sume	return(0);
218878064Sume}
218978064Sume
219055163Sshin/*
219155163Sshin * tvsub --
219255163Sshin *	Subtract 2 timeval structs:  out = out - in.  Out is assumed to
219355163Sshin * be >= in.
219455163Sshin */
219555163Sshinvoid
2196216561Scharniertvsub(struct timeval *out, struct timeval *in)
219755163Sshin{
219855163Sshin	if ((out->tv_usec -= in->tv_usec) < 0) {
219955163Sshin		--out->tv_sec;
220055163Sshin		out->tv_usec += 1000000;
220155163Sshin	}
220255163Sshin	out->tv_sec -= in->tv_sec;
220355163Sshin}
220455163Sshin
220555163Sshin/*
220655163Sshin * onint --
220755163Sshin *	SIGINT handler.
220855163Sshin */
220962627Skris/* ARGSUSED */
221055163Sshinvoid
2211216561Scharnieronint(int notused __unused)
221255163Sshin{
2213285820Shrs	/*
2214285820Shrs	 * When doing reverse DNS lookups, the seenint flag might not
2215285820Shrs	 * be noticed for a while.  Just exit if we get a second SIGINT.
2216285820Shrs	 */
2217285820Shrs	if ((options & F_HOSTNAME) && seenint != 0)
2218285820Shrs		_exit(nreceived ? 0 : 2);
221955163Sshin}
222055163Sshin
222155163Sshin/*
222255163Sshin * summary --
222355163Sshin *	Print out statistics.
222455163Sshin */
222555163Sshinvoid
2226216561Scharniersummary(void)
222755163Sshin{
222855163Sshin
222955163Sshin	(void)printf("\n--- %s ping6 statistics ---\n", hostname);
223055163Sshin	(void)printf("%ld packets transmitted, ", ntransmitted);
223155163Sshin	(void)printf("%ld packets received, ", nreceived);
223255163Sshin	if (nrepeats)
223355163Sshin		(void)printf("+%ld duplicates, ", nrepeats);
223455163Sshin	if (ntransmitted) {
223555163Sshin		if (nreceived > ntransmitted)
2236121472Sume			(void)printf("-- somebody's duplicating packets!");
223755163Sshin		else
2238121472Sume			(void)printf("%.1f%% packet loss",
2239121472Sume			    ((((double)ntransmitted - nreceived) * 100.0) /
224055163Sshin			    ntransmitted));
224155163Sshin	}
2242285820Shrs	if (nrcvtimeout)
2243285820Shrs		printf(", %ld packets out of wait time", nrcvtimeout);
224455163Sshin	(void)putchar('\n');
224555163Sshin	if (nreceived && timing) {
224655163Sshin		/* Only display average to microseconds */
224778064Sume		double num = nreceived + nrepeats;
224878064Sume		double avg = tsum / num;
224978064Sume		double dev = sqrt(tsumsq / num - avg * avg);
225078064Sume		(void)printf(
225178064Sume		    "round-trip min/avg/max/std-dev = %.3f/%.3f/%.3f/%.3f ms\n",
225278064Sume		    tmin, avg, tmax, dev);
225355163Sshin		(void)fflush(stdout);
225455163Sshin	}
225578064Sume	(void)fflush(stdout);
225655163Sshin}
225755163Sshin
225878064Sume/*subject type*/
2259121472Sumestatic const char *niqcode[] = {
226078064Sume	"IPv6 address",
226178064Sume	"DNS label",	/*or empty*/
226278064Sume	"IPv4 address",
226355163Sshin};
226455163Sshin
226578064Sume/*result code*/
2266121472Sumestatic const char *nircode[] = {
226778064Sume	"Success", "Refused", "Unknown",
226878064Sume};
226978064Sume
227078064Sume
227155163Sshin/*
227255163Sshin * pr_icmph --
227355163Sshin *	Print a descriptive string about an ICMP header.
227455163Sshin */
227555163Sshinvoid
2276216561Scharnierpr_icmph(struct icmp6_hdr *icp, u_char *end)
227755163Sshin{
227862627Skris	char ntop_buf[INET6_ADDRSTRLEN];
227978064Sume	struct nd_redirect *red;
228078064Sume	struct icmp6_nodeinfo *ni;
228178064Sume	char dnsname[MAXDNAME + 1];
228278064Sume	const u_char *cp;
228378064Sume	size_t l;
228462627Skris
228578064Sume	switch (icp->icmp6_type) {
228655163Sshin	case ICMP6_DST_UNREACH:
228778064Sume		switch (icp->icmp6_code) {
228855163Sshin		case ICMP6_DST_UNREACH_NOROUTE:
228955163Sshin			(void)printf("No Route to Destination\n");
229055163Sshin			break;
229155163Sshin		case ICMP6_DST_UNREACH_ADMIN:
229255163Sshin			(void)printf("Destination Administratively "
229378064Sume			    "Unreachable\n");
229455163Sshin			break;
229555163Sshin		case ICMP6_DST_UNREACH_BEYONDSCOPE:
229655163Sshin			(void)printf("Destination Unreachable Beyond Scope\n");
229755163Sshin			break;
229855163Sshin		case ICMP6_DST_UNREACH_ADDR:
229955163Sshin			(void)printf("Destination Host Unreachable\n");
230055163Sshin			break;
230155163Sshin		case ICMP6_DST_UNREACH_NOPORT:
230255163Sshin			(void)printf("Destination Port Unreachable\n");
230355163Sshin			break;
230455163Sshin		default:
230555163Sshin			(void)printf("Destination Unreachable, Bad Code: %d\n",
230655163Sshin			    icp->icmp6_code);
230755163Sshin			break;
230855163Sshin		}
230955163Sshin		/* Print returned IP header information */
231055163Sshin		pr_retip((struct ip6_hdr *)(icp + 1), end);
231155163Sshin		break;
231255163Sshin	case ICMP6_PACKET_TOO_BIG:
231355163Sshin		(void)printf("Packet too big mtu = %d\n",
231478064Sume		    (int)ntohl(icp->icmp6_mtu));
231562627Skris		pr_retip((struct ip6_hdr *)(icp + 1), end);
231655163Sshin		break;
231755163Sshin	case ICMP6_TIME_EXCEEDED:
231878064Sume		switch (icp->icmp6_code) {
231955163Sshin		case ICMP6_TIME_EXCEED_TRANSIT:
232055163Sshin			(void)printf("Time to live exceeded\n");
232155163Sshin			break;
232255163Sshin		case ICMP6_TIME_EXCEED_REASSEMBLY:
232355163Sshin			(void)printf("Frag reassembly time exceeded\n");
232455163Sshin			break;
232555163Sshin		default:
232655163Sshin			(void)printf("Time exceeded, Bad Code: %d\n",
232755163Sshin			    icp->icmp6_code);
232855163Sshin			break;
232955163Sshin		}
233055163Sshin		pr_retip((struct ip6_hdr *)(icp + 1), end);
233155163Sshin		break;
233255163Sshin	case ICMP6_PARAM_PROB:
233355163Sshin		(void)printf("Parameter problem: ");
233478064Sume		switch (icp->icmp6_code) {
233578064Sume		case ICMP6_PARAMPROB_HEADER:
233678064Sume			(void)printf("Erroneous Header ");
233778064Sume			break;
233878064Sume		case ICMP6_PARAMPROB_NEXTHEADER:
233978064Sume			(void)printf("Unknown Nextheader ");
234078064Sume			break;
234178064Sume		case ICMP6_PARAMPROB_OPTION:
234278064Sume			(void)printf("Unrecognized Option ");
234378064Sume			break;
234478064Sume		default:
234578064Sume			(void)printf("Bad code(%d) ", icp->icmp6_code);
234678064Sume			break;
234755163Sshin		}
234855163Sshin		(void)printf("pointer = 0x%02x\n",
234978064Sume		    (u_int32_t)ntohl(icp->icmp6_pptr));
235055163Sshin		pr_retip((struct ip6_hdr *)(icp + 1), end);
235155163Sshin		break;
235255163Sshin	case ICMP6_ECHO_REQUEST:
235362627Skris		(void)printf("Echo Request");
235455163Sshin		/* XXX ID + Seq + Data */
235555163Sshin		break;
235655163Sshin	case ICMP6_ECHO_REPLY:
235762627Skris		(void)printf("Echo Reply");
235855163Sshin		/* XXX ID + Seq + Data */
235955163Sshin		break;
236055163Sshin	case ICMP6_MEMBERSHIP_QUERY:
236162627Skris		(void)printf("Listener Query");
236255163Sshin		break;
236355163Sshin	case ICMP6_MEMBERSHIP_REPORT:
236462627Skris		(void)printf("Listener Report");
236555163Sshin		break;
236655163Sshin	case ICMP6_MEMBERSHIP_REDUCTION:
236762627Skris		(void)printf("Listener Done");
236855163Sshin		break;
236955163Sshin	case ND_ROUTER_SOLICIT:
237062627Skris		(void)printf("Router Solicitation");
237155163Sshin		break;
237255163Sshin	case ND_ROUTER_ADVERT:
237362627Skris		(void)printf("Router Advertisement");
237455163Sshin		break;
237555163Sshin	case ND_NEIGHBOR_SOLICIT:
237662627Skris		(void)printf("Neighbor Solicitation");
237755163Sshin		break;
237855163Sshin	case ND_NEIGHBOR_ADVERT:
237962627Skris		(void)printf("Neighbor Advertisement");
238055163Sshin		break;
238155163Sshin	case ND_REDIRECT:
238278064Sume		red = (struct nd_redirect *)icp;
238355163Sshin		(void)printf("Redirect\n");
238478064Sume		if (!inet_ntop(AF_INET6, &red->nd_rd_dst, ntop_buf,
238578064Sume		    sizeof(ntop_buf)))
2386121472Sume			strlcpy(ntop_buf, "?", sizeof(ntop_buf));
238778064Sume		(void)printf("Destination: %s", ntop_buf);
238878064Sume		if (!inet_ntop(AF_INET6, &red->nd_rd_target, ntop_buf,
238978064Sume		    sizeof(ntop_buf)))
2390121472Sume			strlcpy(ntop_buf, "?", sizeof(ntop_buf));
239178064Sume		(void)printf(" New Target: %s", ntop_buf);
239255163Sshin		break;
239355163Sshin	case ICMP6_NI_QUERY:
239462627Skris		(void)printf("Node Information Query");
239555163Sshin		/* XXX ID + Seq + Data */
239678064Sume		ni = (struct icmp6_nodeinfo *)icp;
239778064Sume		l = end - (u_char *)(ni + 1);
239878064Sume		printf(", ");
239978064Sume		switch (ntohs(ni->ni_qtype)) {
240078064Sume		case NI_QTYPE_NOOP:
240178064Sume			(void)printf("NOOP");
240278064Sume			break;
240378064Sume		case NI_QTYPE_SUPTYPES:
240478064Sume			(void)printf("Supported qtypes");
240578064Sume			break;
240678064Sume		case NI_QTYPE_FQDN:
240778064Sume			(void)printf("DNS name");
240878064Sume			break;
240978064Sume		case NI_QTYPE_NODEADDR:
241078064Sume			(void)printf("nodeaddr");
241178064Sume			break;
241278064Sume		case NI_QTYPE_IPV4ADDR:
241378064Sume			(void)printf("IPv4 nodeaddr");
241478064Sume			break;
241578064Sume		default:
241678064Sume			(void)printf("unknown qtype");
241778064Sume			break;
241878064Sume		}
241978064Sume		if (options & F_VERBOSE) {
242078064Sume			switch (ni->ni_code) {
242178064Sume			case ICMP6_NI_SUBJ_IPV6:
242278064Sume				if (l == sizeof(struct in6_addr) &&
242378064Sume				    inet_ntop(AF_INET6, ni + 1, ntop_buf,
242478064Sume				    sizeof(ntop_buf)) != NULL) {
242578064Sume					(void)printf(", subject=%s(%s)",
242678064Sume					    niqcode[ni->ni_code], ntop_buf);
242778064Sume				} else {
242878064Sume#if 1
242978064Sume					/* backward compat to -W */
243078064Sume					(void)printf(", oldfqdn");
243178064Sume#else
243278064Sume					(void)printf(", invalid");
243378064Sume#endif
243478064Sume				}
243578064Sume				break;
243678064Sume			case ICMP6_NI_SUBJ_FQDN:
243778064Sume				if (end == (u_char *)(ni + 1)) {
243878064Sume					(void)printf(", no subject");
243978064Sume					break;
244078064Sume				}
244178064Sume				printf(", subject=%s", niqcode[ni->ni_code]);
244278064Sume				cp = (const u_char *)(ni + 1);
244378064Sume				if (dnsdecode(&cp, end, NULL, dnsname,
244478064Sume				    sizeof(dnsname)) != NULL)
244578064Sume					printf("(%s)", dnsname);
244678064Sume				else
244778064Sume					printf("(invalid)");
244878064Sume				break;
244978064Sume			case ICMP6_NI_SUBJ_IPV4:
245078064Sume				if (l == sizeof(struct in_addr) &&
245178064Sume				    inet_ntop(AF_INET, ni + 1, ntop_buf,
245278064Sume				    sizeof(ntop_buf)) != NULL) {
245378064Sume					(void)printf(", subject=%s(%s)",
245478064Sume					    niqcode[ni->ni_code], ntop_buf);
245578064Sume				} else
245678064Sume					(void)printf(", invalid");
245778064Sume				break;
245878064Sume			default:
245978064Sume				(void)printf(", invalid");
246078064Sume				break;
246178064Sume			}
246278064Sume		}
246355163Sshin		break;
246455163Sshin	case ICMP6_NI_REPLY:
246562627Skris		(void)printf("Node Information Reply");
246655163Sshin		/* XXX ID + Seq + Data */
246778064Sume		ni = (struct icmp6_nodeinfo *)icp;
246878064Sume		printf(", ");
246978064Sume		switch (ntohs(ni->ni_qtype)) {
247078064Sume		case NI_QTYPE_NOOP:
247178064Sume			(void)printf("NOOP");
247278064Sume			break;
247378064Sume		case NI_QTYPE_SUPTYPES:
247478064Sume			(void)printf("Supported qtypes");
247578064Sume			break;
247678064Sume		case NI_QTYPE_FQDN:
247778064Sume			(void)printf("DNS name");
247878064Sume			break;
247978064Sume		case NI_QTYPE_NODEADDR:
248078064Sume			(void)printf("nodeaddr");
248178064Sume			break;
248278064Sume		case NI_QTYPE_IPV4ADDR:
248378064Sume			(void)printf("IPv4 nodeaddr");
248478064Sume			break;
248578064Sume		default:
248678064Sume			(void)printf("unknown qtype");
248778064Sume			break;
248878064Sume		}
248978064Sume		if (options & F_VERBOSE) {
249078064Sume			if (ni->ni_code > sizeof(nircode) / sizeof(nircode[0]))
249178064Sume				printf(", invalid");
249278064Sume			else
249378064Sume				printf(", %s", nircode[ni->ni_code]);
249478064Sume		}
249555163Sshin		break;
249655163Sshin	default:
249762627Skris		(void)printf("Bad ICMP type: %d", icp->icmp6_type);
249855163Sshin	}
249955163Sshin}
250055163Sshin
250155163Sshin/*
250255163Sshin * pr_iph --
250355163Sshin *	Print an IP6 header.
250455163Sshin */
250555163Sshinvoid
2506216561Scharnierpr_iph(struct ip6_hdr *ip6)
250755163Sshin{
250855163Sshin	u_int32_t flow = ip6->ip6_flow & IPV6_FLOWLABEL_MASK;
250955163Sshin	u_int8_t tc;
251062627Skris	char ntop_buf[INET6_ADDRSTRLEN];
251155163Sshin
251255163Sshin	tc = *(&ip6->ip6_vfc + 1); /* XXX */
251355163Sshin	tc = (tc >> 4) & 0x0f;
251455163Sshin	tc |= (ip6->ip6_vfc << 4);
251555163Sshin
251655163Sshin	printf("Vr TC  Flow Plen Nxt Hlim\n");
251755163Sshin	printf(" %1x %02x %05x %04x  %02x   %02x\n",
251878064Sume	    (ip6->ip6_vfc & IPV6_VERSION_MASK) >> 4, tc, (u_int32_t)ntohl(flow),
251978064Sume	    ntohs(ip6->ip6_plen), ip6->ip6_nxt, ip6->ip6_hlim);
252078064Sume	if (!inet_ntop(AF_INET6, &ip6->ip6_src, ntop_buf, sizeof(ntop_buf)))
2521121472Sume		strlcpy(ntop_buf, "?", sizeof(ntop_buf));
252278064Sume	printf("%s->", ntop_buf);
252378064Sume	if (!inet_ntop(AF_INET6, &ip6->ip6_dst, ntop_buf, sizeof(ntop_buf)))
2524121472Sume		strlcpy(ntop_buf, "?", sizeof(ntop_buf));
252578064Sume	printf("%s\n", ntop_buf);
252655163Sshin}
252755163Sshin
252855163Sshin/*
252955163Sshin * pr_addr --
253055163Sshin *	Return an ascii host address as a dotted quad and optionally with
253155163Sshin * a hostname.
253255163Sshin */
253362627Skrisconst char *
2534216561Scharnierpr_addr(struct sockaddr *addr, int addrlen)
253555163Sshin{
253678064Sume	static char buf[NI_MAXHOST];
2537121316Sume	int flag = 0;
253855163Sshin
253962627Skris	if ((options & F_HOSTNAME) == 0)
254055163Sshin		flag |= NI_NUMERICHOST;
254155163Sshin
254278064Sume	if (getnameinfo(addr, addrlen, buf, sizeof(buf), NULL, 0, flag) == 0)
254378064Sume		return (buf);
254478064Sume	else
254578064Sume		return "?";
254655163Sshin}
254755163Sshin
254855163Sshin/*
254955163Sshin * pr_retip --
255055163Sshin *	Dump some info on a returned (via ICMPv6) IPv6 packet.
255155163Sshin */
255255163Sshinvoid
2553216561Scharnierpr_retip(struct ip6_hdr *ip6, u_char *end)
255455163Sshin{
255555163Sshin	u_char *cp = (u_char *)ip6, nh;
255655163Sshin	int hlen;
255755163Sshin
255855163Sshin	if (end - (u_char *)ip6 < sizeof(*ip6)) {
255955163Sshin		printf("IP6");
256055163Sshin		goto trunc;
256155163Sshin	}
256255163Sshin	pr_iph(ip6);
256355163Sshin	hlen = sizeof(*ip6);
256455163Sshin
256555163Sshin	nh = ip6->ip6_nxt;
256655163Sshin	cp += hlen;
256755163Sshin	while (end - cp >= 8) {
256855163Sshin		switch (nh) {
256978064Sume		case IPPROTO_HOPOPTS:
257078064Sume			printf("HBH ");
257178064Sume			hlen = (((struct ip6_hbh *)cp)->ip6h_len+1) << 3;
257278064Sume			nh = ((struct ip6_hbh *)cp)->ip6h_nxt;
257378064Sume			break;
257478064Sume		case IPPROTO_DSTOPTS:
257578064Sume			printf("DSTOPT ");
257678064Sume			hlen = (((struct ip6_dest *)cp)->ip6d_len+1) << 3;
257778064Sume			nh = ((struct ip6_dest *)cp)->ip6d_nxt;
257878064Sume			break;
257978064Sume		case IPPROTO_FRAGMENT:
258078064Sume			printf("FRAG ");
258178064Sume			hlen = sizeof(struct ip6_frag);
258278064Sume			nh = ((struct ip6_frag *)cp)->ip6f_nxt;
258378064Sume			break;
258478064Sume		case IPPROTO_ROUTING:
258578064Sume			printf("RTHDR ");
258678064Sume			hlen = (((struct ip6_rthdr *)cp)->ip6r_len+1) << 3;
258778064Sume			nh = ((struct ip6_rthdr *)cp)->ip6r_nxt;
258878064Sume			break;
258955163Sshin#ifdef IPSEC
259078064Sume		case IPPROTO_AH:
259178064Sume			printf("AH ");
259278064Sume			hlen = (((struct ah *)cp)->ah_len+2) << 2;
259378064Sume			nh = ((struct ah *)cp)->ah_nxt;
259478064Sume			break;
259555163Sshin#endif
259678064Sume		case IPPROTO_ICMPV6:
259778064Sume			printf("ICMP6: type = %d, code = %d\n",
259878064Sume			    *cp, *(cp + 1));
259978064Sume			return;
260078064Sume		case IPPROTO_ESP:
260178064Sume			printf("ESP\n");
260278064Sume			return;
260378064Sume		case IPPROTO_TCP:
260478064Sume			printf("TCP: from port %u, to port %u (decimal)\n",
260578064Sume			    (*cp * 256 + *(cp + 1)),
260678064Sume			    (*(cp + 2) * 256 + *(cp + 3)));
260778064Sume			return;
260878064Sume		case IPPROTO_UDP:
260978064Sume			printf("UDP: from port %u, to port %u (decimal)\n",
261078064Sume			    (*cp * 256 + *(cp + 1)),
261178064Sume			    (*(cp + 2) * 256 + *(cp + 3)));
261278064Sume			return;
261378064Sume		default:
261478064Sume			printf("Unknown Header(%d)\n", nh);
261578064Sume			return;
261655163Sshin		}
261755163Sshin
261855163Sshin		if ((cp += hlen) >= end)
261955163Sshin			goto trunc;
262055163Sshin	}
262155163Sshin	if (end - cp < 8)
262255163Sshin		goto trunc;
262355163Sshin
262455163Sshin	putchar('\n');
262555163Sshin	return;
262655163Sshin
262755163Sshin  trunc:
262855163Sshin	printf("...\n");
262955163Sshin	return;
263055163Sshin}
263155163Sshin
263255163Sshinvoid
2633216561Scharnierfill(char *bp, char *patp)
263455163Sshin{
263592806Sobrien	int ii, jj, kk;
263655163Sshin	int pat[16];
263755163Sshin	char *cp;
263855163Sshin
263955163Sshin	for (cp = patp; *cp; cp++)
264055163Sshin		if (!isxdigit(*cp))
264155163Sshin			errx(1, "patterns must be specified as hex digits");
264255163Sshin	ii = sscanf(patp,
264355163Sshin	    "%2x%2x%2x%2x%2x%2x%2x%2x%2x%2x%2x%2x%2x%2x%2x%2x",
264455163Sshin	    &pat[0], &pat[1], &pat[2], &pat[3], &pat[4], &pat[5], &pat[6],
264555163Sshin	    &pat[7], &pat[8], &pat[9], &pat[10], &pat[11], &pat[12],
264655163Sshin	    &pat[13], &pat[14], &pat[15]);
264755163Sshin
264878064Sume/* xxx */
264955163Sshin	if (ii > 0)
265055163Sshin		for (kk = 0;
2651121472Sume		    kk <= MAXDATALEN - (8 + sizeof(struct tv32) + ii);
265255163Sshin		    kk += ii)
265355163Sshin			for (jj = 0; jj < ii; ++jj)
265455163Sshin				bp[jj + kk] = pat[jj];
265555163Sshin	if (!(options & F_QUIET)) {
265655163Sshin		(void)printf("PATTERN: 0x");
265755163Sshin		for (jj = 0; jj < ii; ++jj)
265855163Sshin			(void)printf("%02x", bp[jj] & 0xFF);
265955163Sshin		(void)printf("\n");
266055163Sshin	}
266155163Sshin}
266255163Sshin
266355163Sshin#ifdef IPSEC
266455163Sshin#ifdef IPSEC_POLICY_IPSEC
266555163Sshinint
2666216561Scharniersetpolicy(int so __unused, char *policy)
266755163Sshin{
266855163Sshin	char *buf;
266955163Sshin
267055163Sshin	if (policy == NULL)
267155163Sshin		return 0;	/* ignore */
267255163Sshin
267355163Sshin	buf = ipsec_set_policy(policy, strlen(policy));
267455163Sshin	if (buf == NULL)
267564277Skris		errx(1, "%s", ipsec_strerror());
267678064Sume	if (setsockopt(s, IPPROTO_IPV6, IPV6_IPSEC_POLICY, buf,
267778064Sume	    ipsec_get_policylen(buf)) < 0)
2678121472Sume		warnx("Unable to set IPsec policy");
267955163Sshin	free(buf);
268055163Sshin
268155163Sshin	return 0;
268255163Sshin}
268355163Sshin#endif
268455163Sshin#endif
268555163Sshin
268662627Skrischar *
2687250251Shrsnigroup(char *name, int nig_oldmcprefix)
268862627Skris{
268962627Skris	char *p;
2690121472Sume	char *q;
269162627Skris	MD5_CTX ctxt;
269262627Skris	u_int8_t digest[16];
269378064Sume	u_int8_t c;
269478064Sume	size_t l;
269562627Skris	char hbuf[NI_MAXHOST];
269662627Skris	struct in6_addr in6;
2697250251Shrs	int valid;
269862627Skris
269978064Sume	p = strchr(name, '.');
270078064Sume	if (!p)
270178064Sume		p = name + strlen(name);
270278064Sume	l = p - name;
270378064Sume	if (l > 63 || l > sizeof(hbuf) - 1)
270462627Skris		return NULL;	/*label too long*/
270578064Sume	strncpy(hbuf, name, l);
270678064Sume	hbuf[(int)l] = '\0';
270762627Skris
270878064Sume	for (q = name; *q; q++) {
2709121472Sume		if (isupper(*(unsigned char *)q))
2710121472Sume			*q = tolower(*(unsigned char *)q);
271178064Sume	}
271278064Sume
2713250251Shrs	/* generate 16 bytes of pseudo-random value. */
2714121472Sume	memset(&ctxt, 0, sizeof(ctxt));
271562627Skris	MD5Init(&ctxt);
271678064Sume	c = l & 0xff;
271778064Sume	MD5Update(&ctxt, &c, sizeof(c));
2718121472Sume	MD5Update(&ctxt, (unsigned char *)name, l);
271962627Skris	MD5Final(digest, &ctxt);
272062627Skris
2721250251Shrs	if (nig_oldmcprefix) {
2722250251Shrs		/* draft-ietf-ipngwg-icmp-name-lookup */
2723250251Shrs		valid = inet_pton(AF_INET6, "ff02::2:0000:0000", &in6);
2724250251Shrs	} else {
2725250251Shrs		/* RFC 4620 */
2726250251Shrs		valid = inet_pton(AF_INET6, "ff02::2:ff00:0000", &in6);
2727250251Shrs	}
2728250251Shrs	if (valid != 1)
272978064Sume		return NULL;	/*XXX*/
2730250251Shrs
2731250251Shrs	if (nig_oldmcprefix) {
2732250251Shrs		/* draft-ietf-ipngwg-icmp-name-lookup */
2733250251Shrs		bcopy(digest, &in6.s6_addr[12], 4);
2734250251Shrs	} else {
2735250251Shrs		/* RFC 4620 */
2736250251Shrs		bcopy(digest, &in6.s6_addr[13], 3);
2737250251Shrs	}
273862627Skris
273962627Skris	if (inet_ntop(AF_INET6, &in6, hbuf, sizeof(hbuf)) == NULL)
274062627Skris		return NULL;
274162627Skris
274262627Skris	return strdup(hbuf);
274362627Skris}
274462627Skris
274555163Sshinvoid
2746216561Scharnierusage(void)
274755163Sshin{
274855163Sshin	(void)fprintf(stderr,
2749141611Sru#if defined(IPSEC) && !defined(IPSEC_POLICY_IPSEC)
2750141611Sru	    "A"
2751141611Sru#endif
2752141611Sru	    "usage: ping6 [-"
2753206889Smaxim	    "Dd"
2754141611Sru#if defined(IPSEC) && !defined(IPSEC_POLICY_IPSEC)
2755141611Sru	    "E"
2756141611Sru#endif
2757141611Sru	    "fH"
275878990Sume#ifdef IPV6_USE_MIN_MTU
275978990Sume	    "m"
276078990Sume#endif
2761182276Smatteo	    "nNoqrRtvwW] "
2762141611Sru	    "[-a addrtype] [-b bufsiz] [-c count] [-g gateway]\n"
2763141611Sru	    "             [-h hoplimit] [-I interface] [-i wait] [-l preload]"
2764141611Sru#if defined(IPSEC) && defined(IPSEC_POLICY_IPSEC)
2765141611Sru	    " [-P policy]"
276655163Sshin#endif
2767141611Sru	    "\n"
2768141611Sru	    "             [-p pattern] [-S sourceaddr] [-s packetsize] "
2769285820Shrs	    "[-x waittime]\n"
2770285820Shrs	    "             [-X timeout] [hops ...] host\n");
277155163Sshin	exit(1);
277255163Sshin}
2773