1121472Sume/*	$KAME: ping6.c,v 1.169 2003/07/25 06:01:47 itojun Exp $	*/
262627Skris
355163Sshin/*
455163Sshin * Copyright (C) 1995, 1996, 1997, and 1998 WIDE Project.
555163Sshin * All rights reserved.
655163Sshin *
755163Sshin * Redistribution and use in source and binary forms, with or without
855163Sshin * modification, are permitted provided that the following conditions
955163Sshin * are met:
1055163Sshin * 1. Redistributions of source code must retain the above copyright
1155163Sshin *    notice, this list of conditions and the following disclaimer.
1255163Sshin * 2. Redistributions in binary form must reproduce the above copyright
1355163Sshin *    notice, this list of conditions and the following disclaimer in the
1455163Sshin *    documentation and/or other materials provided with the distribution.
1555163Sshin * 3. Neither the name of the project nor the names of its contributors
1655163Sshin *    may be used to endorse or promote products derived from this software
1755163Sshin *    without specific prior written permission.
1855163Sshin *
1955163Sshin * THIS SOFTWARE IS PROVIDED BY THE PROJECT AND CONTRIBUTORS ``AS IS'' AND
2055163Sshin * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
2155163Sshin * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
2255163Sshin * ARE DISCLAIMED.  IN NO EVENT SHALL THE PROJECT OR CONTRIBUTORS BE LIABLE
2355163Sshin * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
2455163Sshin * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
2555163Sshin * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
2655163Sshin * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
2755163Sshin * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
2855163Sshin * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
2955163Sshin * SUCH DAMAGE.
3055163Sshin */
3155163Sshin
3255163Sshin/*	BSDI	ping.c,v 2.3 1996/01/21 17:56:50 jch Exp	*/
3355163Sshin
3455163Sshin/*
3555163Sshin * Copyright (c) 1989, 1993
3655163Sshin *	The Regents of the University of California.  All rights reserved.
3755163Sshin *
3855163Sshin * This code is derived from software contributed to Berkeley by
3955163Sshin * Mike Muuss.
4055163Sshin *
4155163Sshin * Redistribution and use in source and binary forms, with or without
4255163Sshin * modification, are permitted provided that the following conditions
4355163Sshin * are met:
4455163Sshin * 1. Redistributions of source code must retain the above copyright
4555163Sshin *    notice, this list of conditions and the following disclaimer.
4655163Sshin * 2. Redistributions in binary form must reproduce the above copyright
4755163Sshin *    notice, this list of conditions and the following disclaimer in the
4855163Sshin *    documentation and/or other materials provided with the distribution.
4955163Sshin * 4. Neither the name of the University nor the names of its contributors
5055163Sshin *    may be used to endorse or promote products derived from this software
5155163Sshin *    without specific prior written permission.
5255163Sshin *
5355163Sshin * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
5455163Sshin * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
5555163Sshin * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
5655163Sshin * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
5755163Sshin * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
5855163Sshin * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
5955163Sshin * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
6055163Sshin * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
6155163Sshin * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
6255163Sshin * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
6355163Sshin * SUCH DAMAGE.
6455163Sshin */
6555163Sshin
6655163Sshin#ifndef lint
67139920Strhodesstatic const char copyright[] =
6855163Sshin"@(#) Copyright (c) 1989, 1993\n\
6955163Sshin	The Regents of the University of California.  All rights reserved.\n";
7055163Sshin#endif /* not lint */
7155163Sshin
7255163Sshin#ifndef lint
7355163Sshin#if 0
7455163Sshinstatic char sccsid[] = "@(#)ping.c	8.1 (Berkeley) 6/5/93";
7555163Sshin#endif
7655163Sshin#endif /* not lint */
7755163Sshin
78216561Scharnier#include <sys/cdefs.h>
79216561Scharnier__FBSDID("$FreeBSD$");
80216561Scharnier
8155163Sshin/*
8255163Sshin * Using the InterNet Control Message Protocol (ICMP) "ECHO" facility,
8355163Sshin * measure round-trip-delays and packet loss across network paths.
8455163Sshin *
8555163Sshin * Author -
8655163Sshin *	Mike Muuss
8755163Sshin *	U. S. Army Ballistic Research Laboratory
8855163Sshin *	December, 1983
8955163Sshin *
9055163Sshin * Status -
9155163Sshin *	Public Domain.  Distribution Unlimited.
9255163Sshin * Bugs -
9355163Sshin *	More statistics could always be gathered.
9455163Sshin *	This program has to run SUID to ROOT to access the ICMP socket.
9555163Sshin */
9655163Sshin/*
9755163Sshin * NOTE:
9855163Sshin * USE_SIN6_SCOPE_ID assumes that sin6_scope_id has the same semantics
9978064Sume * as IPV6_PKTINFO.  Some people object it (sin6_scope_id specifies *link*
10078064Sume * while IPV6_PKTINFO specifies *interface*.  Link is defined as collection of
10155163Sshin * network attached to 1 or more interfaces)
10255163Sshin */
10355163Sshin
10455163Sshin#include <sys/param.h>
10555163Sshin#include <sys/uio.h>
10655163Sshin#include <sys/socket.h>
10755163Sshin#include <sys/time.h>
10855163Sshin
10955163Sshin#include <net/if.h>
11055163Sshin#include <net/route.h>
11155163Sshin
11255163Sshin#include <netinet/in.h>
11355163Sshin#include <netinet/ip6.h>
11455163Sshin#include <netinet/icmp6.h>
11555163Sshin#include <arpa/inet.h>
11678064Sume#include <arpa/nameser.h>
11755163Sshin#include <netdb.h>
11855163Sshin
11955163Sshin#include <ctype.h>
12055163Sshin#include <err.h>
12155163Sshin#include <errno.h>
12255163Sshin#include <fcntl.h>
12378064Sume#include <math.h>
12455163Sshin#include <signal.h>
12555163Sshin#include <stdio.h>
12655163Sshin#include <stdlib.h>
12755163Sshin#include <string.h>
12855163Sshin#include <unistd.h>
129121472Sume#ifdef HAVE_POLL_H
130121472Sume#include <poll.h>
131121472Sume#endif
13255163Sshin
13355163Sshin#ifdef IPSEC
134171135Sgnn#include <netipsec/ah.h>
135171135Sgnn#include <netipsec/ipsec.h>
13655163Sshin#endif
13755163Sshin
13862627Skris#include <md5.h>
13962627Skris
140121472Sumestruct tv32 {
141121472Sume	u_int32_t tv32_sec;
142121472Sume	u_int32_t tv32_usec;
143121472Sume};
144121472Sume
14562627Skris#define MAXPACKETLEN	131072
14655163Sshin#define	IP6LEN		40
147121472Sume#define ICMP6ECHOLEN	8	/* icmp echo header len excluding time */
148121472Sume#define ICMP6ECHOTMLEN sizeof(struct tv32)
14962627Skris#define ICMP6_NIQLEN	(ICMP6ECHOLEN + 8)
150168866Smtm# define CONTROLLEN	10240	/* ancillary data buffer size RFC3542 20.1 */
15178064Sume/* FQDN case, 64 bits of nonce + 32 bits ttl */
15278064Sume#define ICMP6_NIRLEN	(ICMP6ECHOLEN + 12)
15355163Sshin#define	EXTRA		256	/* for AH and various other headers. weird. */
15455163Sshin#define	DEFDATALEN	ICMP6ECHOTMLEN
15562627Skris#define MAXDATALEN	MAXPACKETLEN - IP6LEN - ICMP6ECHOLEN
15655163Sshin#define	NROUTES		9		/* number of record route slots */
15755163Sshin
15855163Sshin#define	A(bit)		rcvd_tbl[(bit)>>3]	/* identify byte in array */
15955163Sshin#define	B(bit)		(1 << ((bit) & 0x07))	/* identify bit in byte */
16055163Sshin#define	SET(bit)	(A(bit) |= B(bit))
16155163Sshin#define	CLR(bit)	(A(bit) &= (~B(bit)))
16255163Sshin#define	TST(bit)	(A(bit) & B(bit))
16355163Sshin
16455163Sshin#define	F_FLOOD		0x0001
16555163Sshin#define	F_INTERVAL	0x0002
16655163Sshin#define	F_PINGFILLED	0x0008
16755163Sshin#define	F_QUIET		0x0010
16855163Sshin#define	F_RROUTE	0x0020
16955163Sshin#define	F_SO_DEBUG	0x0040
17055163Sshin#define	F_VERBOSE	0x0100
17155163Sshin#ifdef IPSEC
17255163Sshin#ifdef IPSEC_POLICY_IPSEC
17362627Skris#define	F_POLICY	0x0400
17455163Sshin#else
17562627Skris#define F_AUTHHDR	0x0200
17662627Skris#define F_ENCRYPT	0x0400
17755163Sshin#endif /*IPSEC_POLICY_IPSEC*/
17855163Sshin#endif /*IPSEC*/
17962627Skris#define F_NODEADDR	0x0800
18062627Skris#define F_FQDN		0x1000
18162627Skris#define F_INTERFACE	0x2000
18262627Skris#define F_SRCADDR	0x4000
18362627Skris#define F_HOSTNAME	0x10000
18462627Skris#define F_FQDNOLD	0x20000
18562627Skris#define F_NIGROUP	0x40000
18678064Sume#define F_SUPTYPES	0x80000
18778064Sume#define F_NOMINMTU	0x100000
188173765Sdd#define F_ONCE		0x200000
189182195Smatteo#define F_AUDIBLE	0x400000
190182276Smatteo#define F_MISSED	0x800000
191206889Smaxim#define F_DONTFRAG	0x1000000
19278064Sume#define F_NOUSERDATA	(F_NODEADDR | F_FQDN | F_FQDNOLD | F_SUPTYPES)
19355163Sshinu_int options;
19455163Sshin
19562627Skris#define IN6LEN		sizeof(struct in6_addr)
19662627Skris#define SA6LEN		sizeof(struct sockaddr_in6)
19778064Sume#define DUMMY_PORT	10101
19855163Sshin
19978064Sume#define SIN6(s)	((struct sockaddr_in6 *)(s))
20055163Sshin
20155163Sshin/*
20255163Sshin * MAX_DUP_CHK is the number of bits in received table, i.e. the maximum
20355163Sshin * number of received sequence numbers we can keep track of.  Change 128
20455163Sshin * to 8192 for complete accuracy...
20555163Sshin */
20655163Sshin#define	MAX_DUP_CHK	(8 * 8192)
20755163Sshinint mx_dup_ck = MAX_DUP_CHK;
20855163Sshinchar rcvd_tbl[MAX_DUP_CHK / 8];
20955163Sshin
210209236Sbrucecstruct addrinfo *res = NULL;
21178064Sumestruct sockaddr_in6 dst;	/* who to ping6 */
21278064Sumestruct sockaddr_in6 src;	/* src addr of this packet */
213121472Sumesocklen_t srclen;
21455163Sshinint datalen = DEFDATALEN;
21555163Sshinint s;				/* socket file descriptor */
21655163Sshinu_char outpack[MAXPACKETLEN];
21755163Sshinchar BSPACE = '\b';		/* characters written for flood */
218182195Smatteochar BBELL = '\a';		/* characters written for AUDIBLE */
21955163Sshinchar DOT = '.';
22055163Sshinchar *hostname;
22155163Sshinint ident;			/* process id to identify our packets */
22278064Sumeu_int8_t nonce[8];		/* nonce field for node information */
22378064Sumeint hoplimit = -1;		/* hoplimit */
22478064Sumeint pathmtu = 0;		/* path MTU for the destination.  0 = unspec. */
225209236Sbrucecu_char *packet = NULL;
226209236Sbrucec#ifdef HAVE_POLL_H
227209236Sbrucecstruct pollfd fdmaskp[1];
228209236Sbrucec#else
229209236Sbrucecfd_set *fdmaskp = NULL;
230209236Sbrucecint fdmasks;
231209236Sbrucec#endif
23255163Sshin
23355163Sshin/* counters */
234182276Smatteolong nmissedmax;		/* max value of ntransmitted - nreceived - 1 */
23555163Sshinlong npackets;			/* max packets to transmit */
23655163Sshinlong nreceived;			/* # of packets we got back */
23755163Sshinlong nrepeats;			/* number of duplicates */
23855163Sshinlong ntransmitted;		/* sequence # for outbound packets = #sent */
23978064Sumestruct timeval interval = {1, 0}; /* interval between packets */
24055163Sshin
24155163Sshin/* timing */
24255163Sshinint timing;			/* flag to do timing */
24355163Sshindouble tmin = 999999999.0;	/* minimum round trip time */
24455163Sshindouble tmax = 0.0;		/* maximum round trip time */
24555163Sshindouble tsum = 0.0;		/* sum of all times, for doing average */
24678064Sumedouble tsumsq = 0.0;		/* sum of all times squared, for std. dev. */
24755163Sshin
24855163Sshin/* for node addresses */
24955163Sshinu_short naflags;
25055163Sshin
25155163Sshin/* for ancillary data(advanced API) */
25255163Sshinstruct msghdr smsghdr;
25355163Sshinstruct iovec smsgiov;
25455163Sshinchar *scmsg = 0;
25555163Sshin
25678064Sumevolatile sig_atomic_t seenalrm;
25778064Sumevolatile sig_atomic_t seenint;
25878064Sume#ifdef SIGINFO
25978064Sumevolatile sig_atomic_t seeninfo;
26078064Sume#endif
26178064Sume
262121472Sumeint	 main(int, char *[]);
26392883Simpvoid	 fill(char *, char *);
26492883Simpint	 get_hoplim(struct msghdr *);
26592883Simpint	 get_pathmtu(struct msghdr *);
26692883Simpstruct in6_pktinfo *get_rcvpktinfo(struct msghdr *);
26792883Simpvoid	 onsignal(int);
26892883Simpvoid	 retransmit(void);
26992883Simpvoid	 onint(int);
27092883Simpsize_t	 pingerlen(void);
27192883Simpint	 pinger(void);
27292883Simpconst char *pr_addr(struct sockaddr *, int);
27392883Simpvoid	 pr_icmph(struct icmp6_hdr *, u_char *);
27492883Simpvoid	 pr_iph(struct ip6_hdr *);
27592883Simpvoid	 pr_suptypes(struct icmp6_nodeinfo *, size_t);
27692883Simpvoid	 pr_nodeaddr(struct icmp6_nodeinfo *, int);
27792883Simpint	 myechoreply(const struct icmp6_hdr *);
27892883Simpint	 mynireply(const struct icmp6_nodeinfo *);
27992883Simpchar *dnsdecode(const u_char **, const u_char *, const u_char *,
280121472Sume	char *, size_t);
28192883Simpvoid	 pr_pack(u_char *, int, struct msghdr *);
28292883Simpvoid	 pr_exthdrs(struct msghdr *);
283168866Smtmvoid	 pr_ip6opt(void *, size_t);
284168866Smtmvoid	 pr_rthdr(void *, size_t);
28592883Simpint	 pr_bitrange(u_int32_t, int, int);
28692883Simpvoid	 pr_retip(struct ip6_hdr *, u_char *);
28792883Simpvoid	 summary(void);
28892883Simpvoid	 tvsub(struct timeval *, struct timeval *);
28992883Simpint	 setpolicy(int, char *);
290252021Shrschar	*nigroup(char *, int);
29192883Simpvoid	 usage(void);
29255163Sshin
29355163Sshinint
294216561Scharniermain(int argc, char *argv[])
29555163Sshin{
29655163Sshin	struct itimerval itimer;
29755163Sshin	struct sockaddr_in6 from;
298121472Sume#ifndef HAVE_ARC4RANDOM
299121472Sume	struct timeval seed;
300121472Sume#endif
301121472Sume#ifdef HAVE_POLL_H
302121472Sume	int timeout;
303121472Sume#else
30478064Sume	struct timeval timeout, *tv;
305121472Sume#endif
30655163Sshin	struct addrinfo hints;
30792806Sobrien	int cc, i;
308121472Sume	int ch, hold, packlen, preload, optval, ret_ga;
309252021Shrs	int nig_oldmcprefix = -1;
310209236Sbrucec	u_char *datap;
311121472Sume	char *e, *target, *ifname = NULL, *gateway = NULL;
31255163Sshin	int ip6optlen = 0;
31355163Sshin	struct cmsghdr *scmsgp = NULL;
314209236Sbrucec	/* For control (ancillary) data received from recvmsg() */
315209236Sbrucec	struct cmsghdr cm[CONTROLLEN];
316121472Sume#if defined(SO_SNDBUF) && defined(SO_RCVBUF)
317121472Sume	u_long lsockbufsize;
31855163Sshin	int sockbufsize = 0;
319121472Sume#endif
32062627Skris	int usepktinfo = 0;
32162627Skris	struct in6_pktinfo *pktinfo = NULL;
32262627Skris#ifdef USE_RFC2292BIS
32362627Skris	struct ip6_rthdr *rthdr = NULL;
32462627Skris#endif
32555163Sshin#ifdef IPSEC_POLICY_IPSEC
32655163Sshin	char *policy_in = NULL;
32755163Sshin	char *policy_out = NULL;
32855163Sshin#endif
32978064Sume	double intval;
33078064Sume	size_t rthlen;
331121472Sume#ifdef IPV6_USE_MIN_MTU
332121472Sume	int mflag = 0;
333121472Sume#endif
33455163Sshin
33555163Sshin	/* just to be sure */
336107652Ssuz	memset(&smsghdr, 0, sizeof(smsghdr));
337107652Ssuz	memset(&smsgiov, 0, sizeof(smsgiov));
33855163Sshin
33955163Sshin	preload = 0;
34055163Sshin	datap = &outpack[ICMP6ECHOLEN + ICMP6ECHOTMLEN];
34155163Sshin#ifndef IPSEC
34278064Sume#define ADDOPTS
34355163Sshin#else
34455163Sshin#ifdef IPSEC_POLICY_IPSEC
34578064Sume#define ADDOPTS	"P:"
34655163Sshin#else
34778064Sume#define ADDOPTS	"AE"
34855163Sshin#endif /*IPSEC_POLICY_IPSEC*/
34955163Sshin#endif
35078064Sume	while ((ch = getopt(argc, argv,
351206889Smaxim	    "a:b:c:DdfHg:h:I:i:l:mnNop:qrRS:s:tvwW" ADDOPTS)) != -1) {
35278064Sume#undef ADDOPTS
35378064Sume		switch (ch) {
35478064Sume		case 'a':
35578064Sume		{
35678064Sume			char *cp;
35755163Sshin
35878064Sume			options &= ~F_NOUSERDATA;
35978064Sume			options |= F_NODEADDR;
36078064Sume			for (cp = optarg; *cp != '\0'; cp++) {
36178064Sume				switch (*cp) {
36278064Sume				case 'a':
36378064Sume					naflags |= NI_NODEADDR_FLAG_ALL;
36478064Sume					break;
36578064Sume				case 'c':
36678064Sume				case 'C':
36778064Sume					naflags |= NI_NODEADDR_FLAG_COMPAT;
36878064Sume					break;
36978064Sume				case 'l':
37078064Sume				case 'L':
37178064Sume					naflags |= NI_NODEADDR_FLAG_LINKLOCAL;
37278064Sume					break;
37378064Sume				case 's':
37478064Sume				case 'S':
37578064Sume					naflags |= NI_NODEADDR_FLAG_SITELOCAL;
37678064Sume					break;
37778064Sume				case 'g':
37878064Sume				case 'G':
37978064Sume					naflags |= NI_NODEADDR_FLAG_GLOBAL;
38078064Sume					break;
38178064Sume				case 'A': /* experimental. not in the spec */
38278064Sume#ifdef NI_NODEADDR_FLAG_ANYCAST
38378064Sume					naflags |= NI_NODEADDR_FLAG_ANYCAST;
38478064Sume					break;
38578064Sume#else
38678064Sume					errx(1,
38778064Sume"-a A is not supported on the platform");
38878064Sume					/*NOTREACHED*/
38978064Sume#endif
39078064Sume				default:
39178064Sume					usage();
39278064Sume					/*NOTREACHED*/
39378064Sume				}
39478064Sume			}
39578064Sume			break;
39678064Sume		}
39778064Sume		case 'b':
39855163Sshin#if defined(SO_SNDBUF) && defined(SO_RCVBUF)
399121472Sume			errno = 0;
400121472Sume			e = NULL;
401121472Sume			lsockbufsize = strtoul(optarg, &e, 10);
402121472Sume			sockbufsize = lsockbufsize;
403121472Sume			if (errno || !*optarg || *e ||
404121472Sume			    sockbufsize != lsockbufsize)
405121472Sume				errx(1, "invalid socket buffer size");
40655163Sshin#else
40787668Scharnier			errx(1,
40855163Sshin"-b option ignored: SO_SNDBUF/SO_RCVBUF socket options not supported");
40955163Sshin#endif
41055163Sshin			break;
41155163Sshin		case 'c':
41255163Sshin			npackets = strtol(optarg, &e, 10);
41355163Sshin			if (npackets <= 0 || *optarg == '\0' || *e != '\0')
41455163Sshin				errx(1,
41555163Sshin				    "illegal number of packets -- %s", optarg);
41655163Sshin			break;
417206889Smaxim		case 'D':
418206889Smaxim			options |= F_DONTFRAG;
419206889Smaxim			break;
42055163Sshin		case 'd':
42155163Sshin			options |= F_SO_DEBUG;
42255163Sshin			break;
42355163Sshin		case 'f':
424121472Sume			if (getuid()) {
425121472Sume				errno = EPERM;
426121472Sume				errx(1, "Must be superuser to flood ping");
427121472Sume			}
42855163Sshin			options |= F_FLOOD;
42955163Sshin			setbuf(stdout, (char *)NULL);
43055163Sshin			break;
431121472Sume		case 'g':
432121472Sume			gateway = optarg;
433121472Sume			break;
43462627Skris		case 'H':
43562627Skris			options |= F_HOSTNAME;
43662627Skris			break;
43755163Sshin		case 'h':		/* hoplimit */
43855163Sshin			hoplimit = strtol(optarg, &e, 10);
439121472Sume			if (*optarg == '\0' || *e != '\0')
440121472Sume				errx(1, "illegal hoplimit %s", optarg);
44155163Sshin			if (255 < hoplimit || hoplimit < -1)
44255163Sshin				errx(1,
44355163Sshin				    "illegal hoplimit -- %s", optarg);
44455163Sshin			break;
44555163Sshin		case 'I':
44655163Sshin			ifname = optarg;
44755163Sshin			options |= F_INTERFACE;
44862627Skris#ifndef USE_SIN6_SCOPE_ID
44962627Skris			usepktinfo++;
45062627Skris#endif
45155163Sshin			break;
45255163Sshin		case 'i':		/* wait between sending packets */
45378064Sume			intval = strtod(optarg, &e);
45478064Sume			if (*optarg == '\0' || *e != '\0')
45578064Sume				errx(1, "illegal timing interval %s", optarg);
45678064Sume			if (intval < 1 && getuid()) {
45778064Sume				errx(1, "%s: only root may use interval < 1s",
45878064Sume				    strerror(EPERM));
45978064Sume			}
46078064Sume			interval.tv_sec = (long)intval;
46178064Sume			interval.tv_usec =
46278064Sume			    (long)((intval - interval.tv_sec) * 1000000);
46378064Sume			if (interval.tv_sec < 0)
46478064Sume				errx(1, "illegal timing interval %s", optarg);
46578064Sume			/* less than 1/hz does not make sense */
466176549Ssilby			if (interval.tv_sec == 0 && interval.tv_usec < 1) {
467176549Ssilby				warnx("too small interval, raised to .000001");
468176549Ssilby				interval.tv_usec = 1;
46978064Sume			}
47055163Sshin			options |= F_INTERVAL;
47155163Sshin			break;
47255163Sshin		case 'l':
473121472Sume			if (getuid()) {
474121472Sume				errno = EPERM;
475121472Sume				errx(1, "Must be superuser to preload");
476121472Sume			}
47755163Sshin			preload = strtol(optarg, &e, 10);
47855163Sshin			if (preload < 0 || *optarg == '\0' || *e != '\0')
47955163Sshin				errx(1, "illegal preload value -- %s", optarg);
48055163Sshin			break;
48178064Sume		case 'm':
48278064Sume#ifdef IPV6_USE_MIN_MTU
483121472Sume			mflag++;
48478064Sume			break;
48578064Sume#else
48678064Sume			errx(1, "-%c is not supported on this platform", ch);
48778064Sume			/*NOTREACHED*/
48878064Sume#endif
48955163Sshin		case 'n':
49078064Sume			options &= ~F_HOSTNAME;
49155163Sshin			break;
49262627Skris		case 'N':
49362627Skris			options |= F_NIGROUP;
494252021Shrs			nig_oldmcprefix++;
49562627Skris			break;
496173765Sdd		case 'o':
497173765Sdd			options |= F_ONCE;
498173765Sdd			break;
49955163Sshin		case 'p':		/* fill buffer with user pattern */
50055163Sshin			options |= F_PINGFILLED;
50155163Sshin			fill((char *)datap, optarg);
50255163Sshin				break;
50355163Sshin		case 'q':
50455163Sshin			options |= F_QUIET;
50555163Sshin			break;
506182276Smatteo		case 'r':
507182276Smatteo			options |= F_AUDIBLE;
508182276Smatteo			break;
509182276Smatteo		case 'R':
510182276Smatteo			options |= F_MISSED;
511182276Smatteo			break;
51262627Skris		case 'S':
513121472Sume			memset(&hints, 0, sizeof(struct addrinfo));
514121472Sume			hints.ai_flags = AI_NUMERICHOST; /* allow hostname? */
515121472Sume			hints.ai_family = AF_INET6;
516121472Sume			hints.ai_socktype = SOCK_RAW;
517121472Sume			hints.ai_protocol = IPPROTO_ICMPV6;
518121472Sume
519121472Sume			ret_ga = getaddrinfo(optarg, NULL, &hints, &res);
520121472Sume			if (ret_ga) {
521121472Sume				errx(1, "invalid source address: %s",
522121472Sume				     gai_strerror(ret_ga));
523121472Sume			}
524121472Sume			/*
525121472Sume			 * res->ai_family must be AF_INET6 and res->ai_addrlen
526121472Sume			 * must be sizeof(src).
527121472Sume			 */
528121472Sume			memcpy(&src, res->ai_addr, res->ai_addrlen);
529121472Sume			srclen = res->ai_addrlen;
530121472Sume			freeaddrinfo(res);
531209236Sbrucec			res = NULL;
53262627Skris			options |= F_SRCADDR;
53362627Skris			break;
53455163Sshin		case 's':		/* size of packet to send */
53555163Sshin			datalen = strtol(optarg, &e, 10);
53655163Sshin			if (datalen <= 0 || *optarg == '\0' || *e != '\0')
53755163Sshin				errx(1, "illegal datalen value -- %s", optarg);
53878064Sume			if (datalen > MAXDATALEN) {
53955163Sshin				errx(1,
54055163Sshin				    "datalen value too large, maximum is %d",
54155163Sshin				    MAXDATALEN);
54278064Sume			}
54355163Sshin			break;
54478064Sume		case 't':
54578064Sume			options &= ~F_NOUSERDATA;
54678064Sume			options |= F_SUPTYPES;
54778064Sume			break;
54855163Sshin		case 'v':
54955163Sshin			options |= F_VERBOSE;
55055163Sshin			break;
55155163Sshin		case 'w':
55278064Sume			options &= ~F_NOUSERDATA;
55355163Sshin			options |= F_FQDN;
55455163Sshin			break;
55562627Skris		case 'W':
55678064Sume			options &= ~F_NOUSERDATA;
55762627Skris			options |= F_FQDNOLD;
55862627Skris			break;
55955163Sshin#ifdef IPSEC
56055163Sshin#ifdef IPSEC_POLICY_IPSEC
56155163Sshin		case 'P':
56255163Sshin			options |= F_POLICY;
56369571Sume			if (!strncmp("in", optarg, 2)) {
56469571Sume				if ((policy_in = strdup(optarg)) == NULL)
56569571Sume					errx(1, "strdup");
56669571Sume			} else if (!strncmp("out", optarg, 3)) {
56769571Sume				if ((policy_out = strdup(optarg)) == NULL)
56869571Sume					errx(1, "strdup");
56969571Sume			} else
57055163Sshin				errx(1, "invalid security policy");
57155163Sshin			break;
57255163Sshin#else
57355163Sshin		case 'A':
57455163Sshin			options |= F_AUTHHDR;
57555163Sshin			break;
57655163Sshin		case 'E':
57755163Sshin			options |= F_ENCRYPT;
57855163Sshin			break;
57955163Sshin#endif /*IPSEC_POLICY_IPSEC*/
58055163Sshin#endif /*IPSEC*/
58155163Sshin		default:
58255163Sshin			usage();
58362627Skris			/*NOTREACHED*/
58455163Sshin		}
58562627Skris	}
586121472Sume
58755163Sshin	argc -= optind;
58855163Sshin	argv += optind;
58955163Sshin
59062627Skris	if (argc < 1) {
59155163Sshin		usage();
59262627Skris		/*NOTREACHED*/
59362627Skris	}
59455163Sshin
59562627Skris	if (argc > 1) {
59678064Sume#ifdef IPV6_RECVRTHDR	/* 2292bis */
59778064Sume		rthlen = CMSG_SPACE(inet6_rth_space(IPV6_RTHDR_TYPE_0,
59878064Sume		    argc - 1));
59978064Sume#else  /* RFC2292 */
60078064Sume		rthlen = inet6_rthdr_space(IPV6_RTHDR_TYPE_0, argc - 1);
60162627Skris#endif
60278064Sume		if (rthlen == 0) {
60378064Sume			errx(1, "too many intermediate hops");
60478064Sume			/*NOTREACHED*/
60578064Sume		}
60678064Sume		ip6optlen += rthlen;
60762627Skris	}
60855163Sshin
60962627Skris	if (options & F_NIGROUP) {
610252021Shrs		target = nigroup(argv[argc - 1], nig_oldmcprefix);
61162627Skris		if (target == NULL) {
61262627Skris			usage();
61362627Skris			/*NOTREACHED*/
61462627Skris		}
61562627Skris	} else
61662627Skris		target = argv[argc - 1];
61755163Sshin
61855163Sshin	/* getaddrinfo */
619121472Sume	memset(&hints, 0, sizeof(struct addrinfo));
62078064Sume	hints.ai_flags = AI_CANONNAME;
62155163Sshin	hints.ai_family = AF_INET6;
62255163Sshin	hints.ai_socktype = SOCK_RAW;
62355163Sshin	hints.ai_protocol = IPPROTO_ICMPV6;
62455163Sshin
62555163Sshin	ret_ga = getaddrinfo(target, NULL, &hints, &res);
62687668Scharnier	if (ret_ga)
62787668Scharnier		errx(1, "%s", gai_strerror(ret_ga));
62855163Sshin	if (res->ai_canonname)
62955163Sshin		hostname = res->ai_canonname;
63055163Sshin	else
63155163Sshin		hostname = target;
63278064Sume
63355163Sshin	if (!res->ai_addr)
63455163Sshin		errx(1, "getaddrinfo failed");
63555163Sshin
63655163Sshin	(void)memcpy(&dst, res->ai_addr, res->ai_addrlen);
63755163Sshin
63878064Sume	if ((s = socket(res->ai_family, res->ai_socktype,
63978064Sume	    res->ai_protocol)) < 0)
64078064Sume		err(1, "socket");
64178064Sume
642121472Sume	/* set the source address if specified. */
643121472Sume	if ((options & F_SRCADDR) &&
644121472Sume	    bind(s, (struct sockaddr *)&src, srclen) != 0) {
645121472Sume		err(1, "bind");
646121472Sume	}
647121472Sume
648121472Sume	/* set the gateway (next hop) if specified */
649121472Sume	if (gateway) {
650121472Sume		struct addrinfo ghints, *gres;
651121472Sume		int error;
652121472Sume
653121472Sume		memset(&ghints, 0, sizeof(ghints));
654121472Sume		ghints.ai_family = AF_INET6;
655121472Sume		ghints.ai_socktype = SOCK_RAW;
656121472Sume		ghints.ai_protocol = IPPROTO_ICMPV6;
657121472Sume
658121472Sume		error = getaddrinfo(gateway, NULL, &hints, &gres);
659121472Sume		if (error) {
660121472Sume			errx(1, "getaddrinfo for the gateway %s: %s",
661121472Sume			     gateway, gai_strerror(error));
662121472Sume		}
663121472Sume		if (gres->ai_next && (options & F_VERBOSE))
664121472Sume			warnx("gateway resolves to multiple addresses");
665121472Sume
666121472Sume		if (setsockopt(s, IPPROTO_IPV6, IPV6_NEXTHOP,
667121472Sume			       gres->ai_addr, gres->ai_addrlen)) {
668121472Sume			err(1, "setsockopt(IPV6_NEXTHOP)");
669121472Sume		}
670121472Sume
671121472Sume		freeaddrinfo(gres);
672121472Sume	}
673121472Sume
67478064Sume	/*
67578064Sume	 * let the kerel pass extension headers of incoming packets,
67678064Sume	 * for privileged socket options
67778064Sume	 */
67878064Sume	if ((options & F_VERBOSE) != 0) {
67978064Sume		int opton = 1;
68078064Sume
68178064Sume#ifdef IPV6_RECVHOPOPTS
68278064Sume		if (setsockopt(s, IPPROTO_IPV6, IPV6_RECVHOPOPTS, &opton,
68378064Sume		    sizeof(opton)))
68478064Sume			err(1, "setsockopt(IPV6_RECVHOPOPTS)");
68578064Sume#else  /* old adv. API */
68678064Sume		if (setsockopt(s, IPPROTO_IPV6, IPV6_HOPOPTS, &opton,
68778064Sume		    sizeof(opton)))
68878064Sume			err(1, "setsockopt(IPV6_HOPOPTS)");
68978064Sume#endif
69078064Sume#ifdef IPV6_RECVDSTOPTS
69178064Sume		if (setsockopt(s, IPPROTO_IPV6, IPV6_RECVDSTOPTS, &opton,
69278064Sume		    sizeof(opton)))
69378064Sume			err(1, "setsockopt(IPV6_RECVDSTOPTS)");
69478064Sume#else  /* old adv. API */
69578064Sume		if (setsockopt(s, IPPROTO_IPV6, IPV6_DSTOPTS, &opton,
69678064Sume		    sizeof(opton)))
69778064Sume			err(1, "setsockopt(IPV6_DSTOPTS)");
69878064Sume#endif
69978064Sume#ifdef IPV6_RECVRTHDRDSTOPTS
70078064Sume		if (setsockopt(s, IPPROTO_IPV6, IPV6_RECVRTHDRDSTOPTS, &opton,
70178064Sume		    sizeof(opton)))
70278064Sume			err(1, "setsockopt(IPV6_RECVRTHDRDSTOPTS)");
70378064Sume#endif
70478064Sume	}
70578064Sume
70678064Sume	/* revoke root privilege */
70778064Sume	seteuid(getuid());
70878064Sume	setuid(getuid());
70978064Sume
710121472Sume	if ((options & F_FLOOD) && (options & F_INTERVAL))
71155163Sshin		errx(1, "-f and -i incompatible options");
71255163Sshin
71378064Sume	if ((options & F_NOUSERDATA) == 0) {
714121472Sume		if (datalen >= sizeof(struct tv32)) {
71578064Sume			/* we can time transfer */
71678064Sume			timing = 1;
71778064Sume		} else
71878064Sume			timing = 0;
71978064Sume		/* in F_VERBOSE case, we may get non-echoreply packets*/
72078064Sume		if (options & F_VERBOSE)
72178064Sume			packlen = 2048 + IP6LEN + ICMP6ECHOLEN + EXTRA;
72278064Sume		else
72378064Sume			packlen = datalen + IP6LEN + ICMP6ECHOLEN + EXTRA;
72478064Sume	} else {
72578064Sume		/* suppress timing for node information query */
72678064Sume		timing = 0;
72778064Sume		datalen = 2048;
72878064Sume		packlen = 2048 + IP6LEN + ICMP6ECHOLEN + EXTRA;
72978064Sume	}
73078064Sume
73155163Sshin	if (!(packet = (u_char *)malloc((u_int)packlen)))
732121472Sume		err(1, "Unable to allocate packet");
73355163Sshin	if (!(options & F_PINGFILLED))
73478064Sume		for (i = ICMP6ECHOLEN; i < packlen; ++i)
73555163Sshin			*datap++ = i;
73655163Sshin
73755163Sshin	ident = getpid() & 0xFFFF;
738121472Sume#ifndef HAVE_ARC4RANDOM
739121472Sume	gettimeofday(&seed, NULL);
740121472Sume	srand((unsigned int)(seed.tv_sec ^ seed.tv_usec ^ (long)ident));
74178064Sume	memset(nonce, 0, sizeof(nonce));
74278064Sume	for (i = 0; i < sizeof(nonce); i += sizeof(int))
74378064Sume		*((int *)&nonce[i]) = rand();
74478064Sume#else
74578064Sume	memset(nonce, 0, sizeof(nonce));
74678064Sume	for (i = 0; i < sizeof(nonce); i += sizeof(u_int32_t))
74778064Sume		*((u_int32_t *)&nonce[i]) = arc4random();
74878064Sume#endif
749206889Smaxim	optval = 1;
750206889Smaxim	if (options & F_DONTFRAG)
751206889Smaxim		if (setsockopt(s, IPPROTO_IPV6, IPV6_DONTFRAG,
752206889Smaxim		    &optval, sizeof(optval)) == -1)
753206889Smaxim			err(1, "IPV6_DONTFRAG");
75455163Sshin	hold = 1;
75555163Sshin
75655163Sshin	if (options & F_SO_DEBUG)
75755163Sshin		(void)setsockopt(s, SOL_SOCKET, SO_DEBUG, (char *)&hold,
75855163Sshin		    sizeof(hold));
75955163Sshin	optval = IPV6_DEFHLIM;
76055163Sshin	if (IN6_IS_ADDR_MULTICAST(&dst.sin6_addr))
76155163Sshin		if (setsockopt(s, IPPROTO_IPV6, IPV6_MULTICAST_HOPS,
76278064Sume		    &optval, sizeof(optval)) == -1)
76355163Sshin			err(1, "IPV6_MULTICAST_HOPS");
76478064Sume#ifdef IPV6_USE_MIN_MTU
765121472Sume	if (mflag != 1) {
766121472Sume		optval = mflag > 1 ? 0 : 1;
767121472Sume
76878064Sume		if (setsockopt(s, IPPROTO_IPV6, IPV6_USE_MIN_MTU,
76978064Sume		    &optval, sizeof(optval)) == -1)
77078064Sume			err(1, "setsockopt(IPV6_USE_MIN_MTU)");
77178064Sume	}
77278064Sume#ifdef IPV6_RECVPATHMTU
77378064Sume	else {
77478064Sume		optval = 1;
77578064Sume		if (setsockopt(s, IPPROTO_IPV6, IPV6_RECVPATHMTU,
77678064Sume		    &optval, sizeof(optval)) == -1)
77778064Sume			err(1, "setsockopt(IPV6_RECVPATHMTU)");
77878064Sume	}
77978064Sume#endif /* IPV6_RECVPATHMTU */
78078064Sume#endif /* IPV6_USE_MIN_MTU */
78155163Sshin
78255163Sshin#ifdef IPSEC
78355163Sshin#ifdef IPSEC_POLICY_IPSEC
78455163Sshin	if (options & F_POLICY) {
78555163Sshin		if (setpolicy(s, policy_in) < 0)
78664277Skris			errx(1, "%s", ipsec_strerror());
78755163Sshin		if (setpolicy(s, policy_out) < 0)
78864277Skris			errx(1, "%s", ipsec_strerror());
78955163Sshin	}
79055163Sshin#else
79155163Sshin	if (options & F_AUTHHDR) {
79255163Sshin		optval = IPSEC_LEVEL_REQUIRE;
79355163Sshin#ifdef IPV6_AUTH_TRANS_LEVEL
79455163Sshin		if (setsockopt(s, IPPROTO_IPV6, IPV6_AUTH_TRANS_LEVEL,
79578064Sume		    &optval, sizeof(optval)) == -1)
79655163Sshin			err(1, "setsockopt(IPV6_AUTH_TRANS_LEVEL)");
79755163Sshin#else /* old def */
79855163Sshin		if (setsockopt(s, IPPROTO_IPV6, IPV6_AUTH_LEVEL,
79978064Sume		    &optval, sizeof(optval)) == -1)
80055163Sshin			err(1, "setsockopt(IPV6_AUTH_LEVEL)");
80155163Sshin#endif
80255163Sshin	}
80355163Sshin	if (options & F_ENCRYPT) {
80455163Sshin		optval = IPSEC_LEVEL_REQUIRE;
80555163Sshin		if (setsockopt(s, IPPROTO_IPV6, IPV6_ESP_TRANS_LEVEL,
80678064Sume		    &optval, sizeof(optval)) == -1)
80755163Sshin			err(1, "setsockopt(IPV6_ESP_TRANS_LEVEL)");
80855163Sshin	}
80955163Sshin#endif /*IPSEC_POLICY_IPSEC*/
81055163Sshin#endif
81155163Sshin
81255163Sshin#ifdef ICMP6_FILTER
81355163Sshin    {
81455163Sshin	struct icmp6_filter filt;
81555163Sshin	if (!(options & F_VERBOSE)) {
81655163Sshin		ICMP6_FILTER_SETBLOCKALL(&filt);
81762627Skris		if ((options & F_FQDN) || (options & F_FQDNOLD) ||
81878064Sume		    (options & F_NODEADDR) || (options & F_SUPTYPES))
81955163Sshin			ICMP6_FILTER_SETPASS(ICMP6_NI_REPLY, &filt);
82055163Sshin		else
82155163Sshin			ICMP6_FILTER_SETPASS(ICMP6_ECHO_REPLY, &filt);
82255163Sshin	} else {
82355163Sshin		ICMP6_FILTER_SETPASSALL(&filt);
82455163Sshin	}
82555163Sshin	if (setsockopt(s, IPPROTO_ICMPV6, ICMP6_FILTER, &filt,
82678064Sume	    sizeof(filt)) < 0)
82755163Sshin		err(1, "setsockopt(ICMP6_FILTER)");
82855163Sshin    }
82955163Sshin#endif /*ICMP6_FILTER*/
83055163Sshin
83162627Skris	/* let the kerel pass extension headers of incoming packets */
83262627Skris	if ((options & F_VERBOSE) != 0) {
83362627Skris		int opton = 1;
83462627Skris
83562627Skris#ifdef IPV6_RECVRTHDR
83662627Skris		if (setsockopt(s, IPPROTO_IPV6, IPV6_RECVRTHDR, &opton,
83778064Sume		    sizeof(opton)))
83862627Skris			err(1, "setsockopt(IPV6_RECVRTHDR)");
83962627Skris#else  /* old adv. API */
84062627Skris		if (setsockopt(s, IPPROTO_IPV6, IPV6_RTHDR, &opton,
84178064Sume		    sizeof(opton)))
84262627Skris			err(1, "setsockopt(IPV6_RTHDR)");
84362627Skris#endif
84462627Skris	}
84562627Skris
84655163Sshin/*
84755163Sshin	optval = 1;
84855163Sshin	if (IN6_IS_ADDR_MULTICAST(&dst.sin6_addr))
84955163Sshin		if (setsockopt(s, IPPROTO_IPV6, IPV6_MULTICAST_LOOP,
85078064Sume		    &optval, sizeof(optval)) == -1)
85155163Sshin			err(1, "IPV6_MULTICAST_LOOP");
85255163Sshin*/
85355163Sshin
85462627Skris	/* Specify the outgoing interface and/or the source address */
85562627Skris	if (usepktinfo)
85655163Sshin		ip6optlen += CMSG_SPACE(sizeof(struct in6_pktinfo));
85755163Sshin
85855163Sshin	if (hoplimit != -1)
85955163Sshin		ip6optlen += CMSG_SPACE(sizeof(int));
86055163Sshin
86155163Sshin	/* set IP6 packet options */
86255163Sshin	if (ip6optlen) {
86355163Sshin		if ((scmsg = (char *)malloc(ip6optlen)) == 0)
86455163Sshin			errx(1, "can't allocate enough memory");
86555163Sshin		smsghdr.msg_control = (caddr_t)scmsg;
86655163Sshin		smsghdr.msg_controllen = ip6optlen;
86755163Sshin		scmsgp = (struct cmsghdr *)scmsg;
86855163Sshin	}
86962627Skris	if (usepktinfo) {
87062627Skris		pktinfo = (struct in6_pktinfo *)(CMSG_DATA(scmsgp));
87162627Skris		memset(pktinfo, 0, sizeof(*pktinfo));
87255163Sshin		scmsgp->cmsg_len = CMSG_LEN(sizeof(struct in6_pktinfo));
87355163Sshin		scmsgp->cmsg_level = IPPROTO_IPV6;
87455163Sshin		scmsgp->cmsg_type = IPV6_PKTINFO;
87562627Skris		scmsgp = CMSG_NXTHDR(&smsghdr, scmsgp);
87662627Skris	}
87755163Sshin
87862627Skris	/* set the outgoing interface */
87962627Skris	if (ifname) {
88062627Skris#ifndef USE_SIN6_SCOPE_ID
88162627Skris		/* pktinfo must have already been allocated */
88262627Skris		if ((pktinfo->ipi6_ifindex = if_nametoindex(ifname)) == 0)
88378064Sume			errx(1, "%s: invalid interface name", ifname);
88455163Sshin#else
88555163Sshin		if ((dst.sin6_scope_id = if_nametoindex(ifname)) == 0)
88655163Sshin			errx(1, "%s: invalid interface name", ifname);
88755163Sshin#endif
88855163Sshin	}
88955163Sshin	if (hoplimit != -1) {
89055163Sshin		scmsgp->cmsg_len = CMSG_LEN(sizeof(int));
89155163Sshin		scmsgp->cmsg_level = IPPROTO_IPV6;
89255163Sshin		scmsgp->cmsg_type = IPV6_HOPLIMIT;
89355163Sshin		*(int *)(CMSG_DATA(scmsgp)) = hoplimit;
89455163Sshin
89555163Sshin		scmsgp = CMSG_NXTHDR(&smsghdr, scmsgp);
89655163Sshin	}
89762627Skris
89855163Sshin	if (argc > 1) {	/* some intermediate addrs are specified */
89955163Sshin		int hops, error;
90062627Skris#ifdef USE_RFC2292BIS
90162627Skris		int rthdrlen;
90262627Skris#endif
90355163Sshin
90462627Skris#ifdef USE_RFC2292BIS
90562627Skris		rthdrlen = inet6_rth_space(IPV6_RTHDR_TYPE_0, argc - 1);
90662627Skris		scmsgp->cmsg_len = CMSG_LEN(rthdrlen);
90762627Skris		scmsgp->cmsg_level = IPPROTO_IPV6;
90862627Skris		scmsgp->cmsg_type = IPV6_RTHDR;
90962627Skris		rthdr = (struct ip6_rthdr *)CMSG_DATA(scmsgp);
91062627Skris		rthdr = inet6_rth_init((void *)rthdr, rthdrlen,
91178064Sume		    IPV6_RTHDR_TYPE_0, argc - 1);
91262627Skris		if (rthdr == NULL)
91362627Skris			errx(1, "can't initialize rthdr");
91462627Skris#else  /* old advanced API */
91555163Sshin		if ((scmsgp = (struct cmsghdr *)inet6_rthdr_init(scmsgp,
91678064Sume		    IPV6_RTHDR_TYPE_0)) == 0)
91755163Sshin			errx(1, "can't initialize rthdr");
91862627Skris#endif /* USE_RFC2292BIS */
91955163Sshin
92055163Sshin		for (hops = 0; hops < argc - 1; hops++) {
92155163Sshin			struct addrinfo *iaip;
92255163Sshin
92378064Sume			if ((error = getaddrinfo(argv[hops], NULL, &hints,
92478064Sume			    &iaip)))
92564277Skris				errx(1, "%s", gai_strerror(error));
92678064Sume			if (SIN6(iaip->ai_addr)->sin6_family != AF_INET6)
92755163Sshin				errx(1,
92878064Sume				    "bad addr family of an intermediate addr");
92955163Sshin
93062627Skris#ifdef USE_RFC2292BIS
93162627Skris			if (inet6_rth_add(rthdr,
93278064Sume			    &(SIN6(iaip->ai_addr))->sin6_addr))
93362627Skris				errx(1, "can't add an intermediate node");
93462627Skris#else  /* old advanced API */
93555163Sshin			if (inet6_rthdr_add(scmsgp,
93678064Sume			    &(SIN6(iaip->ai_addr))->sin6_addr,
93778064Sume			    IPV6_RTHDR_LOOSE))
93855163Sshin				errx(1, "can't add an intermediate node");
93962627Skris#endif /* USE_RFC2292BIS */
94078064Sume			freeaddrinfo(iaip);
94155163Sshin		}
94255163Sshin
94362627Skris#ifndef USE_RFC2292BIS
94455163Sshin		if (inet6_rthdr_lasthop(scmsgp, IPV6_RTHDR_LOOSE))
94555163Sshin			errx(1, "can't set the last flag");
94662627Skris#endif
94755163Sshin
94855163Sshin		scmsgp = CMSG_NXTHDR(&smsghdr, scmsgp);
94955163Sshin	}
95055163Sshin
951121472Sume	if (!(options & F_SRCADDR)) {
95255163Sshin		/*
953121472Sume		 * get the source address. XXX since we revoked the root
954121472Sume		 * privilege, we cannot use a raw socket for this.
95555163Sshin		 */
956121472Sume		int dummy;
957121472Sume		socklen_t len = sizeof(src);
95878064Sume
95955163Sshin		if ((dummy = socket(AF_INET6, SOCK_DGRAM, 0)) < 0)
96055163Sshin			err(1, "UDP socket");
96155163Sshin
96255163Sshin		src.sin6_family = AF_INET6;
96355163Sshin		src.sin6_addr = dst.sin6_addr;
96455163Sshin		src.sin6_port = ntohs(DUMMY_PORT);
96555163Sshin		src.sin6_scope_id = dst.sin6_scope_id;
96655163Sshin
96762627Skris#ifdef USE_RFC2292BIS
96862627Skris		if (pktinfo &&
96962627Skris		    setsockopt(dummy, IPPROTO_IPV6, IPV6_PKTINFO,
97078064Sume		    (void *)pktinfo, sizeof(*pktinfo)))
97162627Skris			err(1, "UDP setsockopt(IPV6_PKTINFO)");
97262627Skris
97362627Skris		if (hoplimit != -1 &&
974121472Sume		    setsockopt(dummy, IPPROTO_IPV6, IPV6_UNICAST_HOPS,
97578064Sume		    (void *)&hoplimit, sizeof(hoplimit)))
976121472Sume			err(1, "UDP setsockopt(IPV6_UNICAST_HOPS)");
97762627Skris
978121472Sume		if (hoplimit != -1 &&
979121472Sume		    setsockopt(dummy, IPPROTO_IPV6, IPV6_MULTICAST_HOPS,
980121472Sume		    (void *)&hoplimit, sizeof(hoplimit)))
981121472Sume			err(1, "UDP setsockopt(IPV6_MULTICAST_HOPS)");
982121472Sume
98362627Skris		if (rthdr &&
98462627Skris		    setsockopt(dummy, IPPROTO_IPV6, IPV6_RTHDR,
98578064Sume		    (void *)rthdr, (rthdr->ip6r_len + 1) << 3))
98662627Skris			err(1, "UDP setsockopt(IPV6_RTHDR)");
98762627Skris#else  /* old advanced API */
98862627Skris		if (smsghdr.msg_control &&
98962627Skris		    setsockopt(dummy, IPPROTO_IPV6, IPV6_PKTOPTIONS,
99078064Sume		    (void *)smsghdr.msg_control, smsghdr.msg_controllen))
99162627Skris			err(1, "UDP setsockopt(IPV6_PKTOPTIONS)");
99255163Sshin#endif
99378064Sume
99455163Sshin		if (connect(dummy, (struct sockaddr *)&src, len) < 0)
99555163Sshin			err(1, "UDP connect");
99678064Sume
99755163Sshin		if (getsockname(dummy, (struct sockaddr *)&src, &len) < 0)
99855163Sshin			err(1, "getsockname");
99955163Sshin
100055163Sshin		close(dummy);
100155163Sshin	}
100255163Sshin
100355163Sshin#if defined(SO_SNDBUF) && defined(SO_RCVBUF)
100455163Sshin	if (sockbufsize) {
100555163Sshin		if (datalen > sockbufsize)
100662627Skris			warnx("you need -b to increase socket buffer size");
100755163Sshin		if (setsockopt(s, SOL_SOCKET, SO_SNDBUF, &sockbufsize,
100878064Sume		    sizeof(sockbufsize)) < 0)
100955163Sshin			err(1, "setsockopt(SO_SNDBUF)");
101055163Sshin		if (setsockopt(s, SOL_SOCKET, SO_RCVBUF, &sockbufsize,
101178064Sume		    sizeof(sockbufsize)) < 0)
101255163Sshin			err(1, "setsockopt(SO_RCVBUF)");
101355163Sshin	}
101455163Sshin	else {
101555163Sshin		if (datalen > 8 * 1024)	/*XXX*/
101655163Sshin			warnx("you need -b to increase socket buffer size");
101755163Sshin		/*
101855163Sshin		 * When pinging the broadcast address, you can get a lot of
101955163Sshin		 * answers. Doing something so evil is useful if you are trying
102055163Sshin		 * to stress the ethernet, or just want to fill the arp cache
102155163Sshin		 * to get some stuff for /etc/ethers.
102255163Sshin		 */
102355163Sshin		hold = 48 * 1024;
102478064Sume		setsockopt(s, SOL_SOCKET, SO_RCVBUF, (char *)&hold,
102578064Sume		    sizeof(hold));
102655163Sshin	}
102755163Sshin#endif
102855163Sshin
102955163Sshin	optval = 1;
103055163Sshin#ifndef USE_SIN6_SCOPE_ID
103162627Skris#ifdef IPV6_RECVPKTINFO
103262627Skris	if (setsockopt(s, IPPROTO_IPV6, IPV6_RECVPKTINFO, &optval,
103378064Sume	    sizeof(optval)) < 0)
103462627Skris		warn("setsockopt(IPV6_RECVPKTINFO)"); /* XXX err? */
103562627Skris#else  /* old adv. API */
103662627Skris	if (setsockopt(s, IPPROTO_IPV6, IPV6_PKTINFO, &optval,
103778064Sume	    sizeof(optval)) < 0)
103862627Skris		warn("setsockopt(IPV6_PKTINFO)"); /* XXX err? */
103955163Sshin#endif
104062627Skris#endif /* USE_SIN6_SCOPE_ID */
104162627Skris#ifdef IPV6_RECVHOPLIMIT
104262627Skris	if (setsockopt(s, IPPROTO_IPV6, IPV6_RECVHOPLIMIT, &optval,
104378064Sume	    sizeof(optval)) < 0)
104462627Skris		warn("setsockopt(IPV6_RECVHOPLIMIT)"); /* XXX err? */
104562627Skris#else  /* old adv. API */
104662627Skris	if (setsockopt(s, IPPROTO_IPV6, IPV6_HOPLIMIT, &optval,
104778064Sume	    sizeof(optval)) < 0)
104862627Skris		warn("setsockopt(IPV6_HOPLIMIT)"); /* XXX err? */
104962627Skris#endif
105055163Sshin
105178064Sume	printf("PING6(%lu=40+8+%lu bytes) ", (unsigned long)(40 + pingerlen()),
105278064Sume	    (unsigned long)(pingerlen() - 8));
105378064Sume	printf("%s --> ", pr_addr((struct sockaddr *)&src, sizeof(src)));
105478064Sume	printf("%s\n", pr_addr((struct sockaddr *)&dst, sizeof(dst)));
105555163Sshin
105655163Sshin	while (preload--)		/* Fire off them quickies. */
105778984Sume		(void)pinger();
105855163Sshin
105978064Sume	(void)signal(SIGINT, onsignal);
106078064Sume#ifdef SIGINFO
106178064Sume	(void)signal(SIGINFO, onsignal);
106278064Sume#endif
106355163Sshin
106455163Sshin	if ((options & F_FLOOD) == 0) {
106578064Sume		(void)signal(SIGALRM, onsignal);
106678064Sume		itimer.it_interval = interval;
106778064Sume		itimer.it_value = interval;
106855163Sshin		(void)setitimer(ITIMER_REAL, &itimer, NULL);
1069132656Ssuz		if (ntransmitted == 0)
107089394Sru			retransmit();
107155163Sshin	}
107255163Sshin
1073121472Sume#ifndef HAVE_POLL_H
107478064Sume	fdmasks = howmany(s + 1, NFDBITS) * sizeof(fd_mask);
107566811Skris	if ((fdmaskp = malloc(fdmasks)) == NULL)
107666811Skris		err(1, "malloc");
1077121472Sume#endif
107866811Skris
1079121472Sume	seenalrm = seenint = 0;
108078064Sume#ifdef SIGINFO
108178064Sume	seeninfo = 0;
108278064Sume#endif
108378064Sume
108455163Sshin	for (;;) {
108555163Sshin		struct msghdr m;
108655163Sshin		struct iovec iov[2];
108755163Sshin
108878064Sume		/* signal handling */
108978064Sume		if (seenalrm) {
1090179356Sbz			/* last packet sent, timeout reached? */
1091179356Sbz			if (npackets && ntransmitted >= npackets)
1092179356Sbz				break;
109378064Sume			retransmit();
109478064Sume			seenalrm = 0;
109578064Sume			continue;
109678064Sume		}
109778064Sume		if (seenint) {
109878064Sume			onint(SIGINT);
109978064Sume			seenint = 0;
110078064Sume			continue;
110178064Sume		}
110278064Sume#ifdef SIGINFO
110378064Sume		if (seeninfo) {
110478064Sume			summary();
110578064Sume			seeninfo = 0;
110678064Sume			continue;
110778064Sume		}
110878064Sume#endif
110978064Sume
111055163Sshin		if (options & F_FLOOD) {
111178984Sume			(void)pinger();
1112121472Sume#ifdef HAVE_POLL_H
1113121472Sume			timeout = 10;
1114121472Sume#else
111566811Skris			timeout.tv_sec = 0;
111666811Skris			timeout.tv_usec = 10000;
111778064Sume			tv = &timeout;
1118121472Sume#endif
1119121472Sume		} else {
1120121472Sume#ifdef HAVE_POLL_H
1121121472Sume			timeout = INFTIM;
1122121472Sume#else
112378064Sume			tv = NULL;
1124121472Sume#endif
1125121472Sume		}
1126121472Sume#ifdef HAVE_POLL_H
1127121472Sume		fdmaskp[0].fd = s;
1128121472Sume		fdmaskp[0].events = POLLIN;
1129121472Sume		cc = poll(fdmaskp, 1, timeout);
1130121472Sume#else
113178064Sume		memset(fdmaskp, 0, fdmasks);
113278064Sume		FD_SET(s, fdmaskp);
113378064Sume		cc = select(s + 1, fdmaskp, NULL, NULL, tv);
1134121472Sume#endif
113578064Sume		if (cc < 0) {
113678064Sume			if (errno != EINTR) {
1137121472Sume#ifdef HAVE_POLL_H
1138121472Sume				warn("poll");
1139121472Sume#else
114078064Sume				warn("select");
1141121472Sume#endif
114278064Sume				sleep(1);
114378064Sume			}
114478064Sume			continue;
114578064Sume		} else if (cc == 0)
114678064Sume			continue;
114778064Sume
114855163Sshin		m.msg_name = (caddr_t)&from;
114955163Sshin		m.msg_namelen = sizeof(from);
115055163Sshin		memset(&iov, 0, sizeof(iov));
115155163Sshin		iov[0].iov_base = (caddr_t)packet;
115255163Sshin		iov[0].iov_len = packlen;
115355163Sshin		m.msg_iov = iov;
115455163Sshin		m.msg_iovlen = 1;
1155168866Smtm		memset(cm, 0, CONTROLLEN);
1156168866Smtm		m.msg_control = (void *)cm;
1157168866Smtm		m.msg_controllen = CONTROLLEN;
115855163Sshin
115978064Sume		cc = recvmsg(s, &m, 0);
116078064Sume		if (cc < 0) {
116178064Sume			if (errno != EINTR) {
116278064Sume				warn("recvmsg");
116378064Sume				sleep(1);
116478064Sume			}
116555163Sshin			continue;
116678064Sume		} else if (cc == 0) {
116778064Sume			int mtu;
116878064Sume
116978064Sume			/*
117078064Sume			 * receive control messages only. Process the
117178064Sume			 * exceptions (currently the only possiblity is
117278064Sume			 * a path MTU notification.)
117378064Sume			 */
117478064Sume			if ((mtu = get_pathmtu(&m)) > 0) {
117578064Sume				if ((options & F_VERBOSE) != 0) {
117678064Sume					printf("new path MTU (%d) is "
117778064Sume					    "notified\n", mtu);
117878064Sume				}
117978064Sume			}
118078064Sume			continue;
118178064Sume		} else {
118278064Sume			/*
118378064Sume			 * an ICMPv6 message (probably an echoreply) arrived.
118478064Sume			 */
118578064Sume			pr_pack(packet, cc, &m);
118655163Sshin		}
1187173765Sdd		if (((options & F_ONCE) != 0 && nreceived > 0) ||
1188173765Sdd		    (npackets > 0 && nreceived >= npackets))
118955163Sshin			break;
1190182276Smatteo		if (ntransmitted - nreceived - 1 > nmissedmax) {
1191182276Smatteo			nmissedmax = ntransmitted - nreceived - 1;
1192182276Smatteo			if (options & F_MISSED)
1193182276Smatteo				(void)write(STDOUT_FILENO, &BBELL, 1);
1194182276Smatteo		}
119555163Sshin	}
119655163Sshin	summary();
1197209236Sbrucec
1198209236Sbrucec	if (res != NULL)
1199209236Sbrucec		freeaddrinfo(res);
1200209236Sbrucec
1201209236Sbrucec        if(packet != NULL)
1202209236Sbrucec                free(packet);
1203209236Sbrucec
1204209236Sbrucec#ifndef HAVE_POLL_H
1205209236Sbrucec        if(fdmaskp != NULL)
1206209236Sbrucec                free(fdmaskp);
1207209236Sbrucec#endif
1208209236Sbrucec
1209179356Sbz	exit(nreceived == 0 ? 2 : 0);
121055163Sshin}
121155163Sshin
121278064Sumevoid
1213216561Scharnieronsignal(int sig)
121478064Sume{
1215121472Sume
121678064Sume	switch (sig) {
121778064Sume	case SIGALRM:
121878064Sume		seenalrm++;
121978064Sume		break;
122078064Sume	case SIGINT:
122178064Sume		seenint++;
122278064Sume		break;
122378064Sume#ifdef SIGINFO
122478064Sume	case SIGINFO:
122578064Sume		seeninfo++;
122678064Sume		break;
122778064Sume#endif
122878064Sume	}
122978064Sume}
123078064Sume
123155163Sshin/*
123278064Sume * retransmit --
123355163Sshin *	This routine transmits another ping6.
123455163Sshin */
123555163Sshinvoid
1236216561Scharnierretransmit(void)
123755163Sshin{
123855163Sshin	struct itimerval itimer;
123955163Sshin
124078984Sume	if (pinger() == 0)
124155163Sshin		return;
124255163Sshin
124355163Sshin	/*
124455163Sshin	 * If we're not transmitting any more packets, change the timer
124555163Sshin	 * to wait two round-trip times if we've received any packets or
124655163Sshin	 * ten seconds if we haven't.
124755163Sshin	 */
124855163Sshin#define	MAXWAIT		10
124955163Sshin	if (nreceived) {
125055163Sshin		itimer.it_value.tv_sec =  2 * tmax / 1000;
125155163Sshin		if (itimer.it_value.tv_sec == 0)
125255163Sshin			itimer.it_value.tv_sec = 1;
125355163Sshin	} else
125455163Sshin		itimer.it_value.tv_sec = MAXWAIT;
125555163Sshin	itimer.it_interval.tv_sec = 0;
125655163Sshin	itimer.it_interval.tv_usec = 0;
125755163Sshin	itimer.it_value.tv_usec = 0;
125855163Sshin
1259179356Sbz	(void)signal(SIGALRM, onsignal);
126055163Sshin	(void)setitimer(ITIMER_REAL, &itimer, NULL);
126155163Sshin}
126255163Sshin
126355163Sshin/*
126455163Sshin * pinger --
126555163Sshin *	Compose and transmit an ICMP ECHO REQUEST packet.  The IP packet
126655163Sshin * will be added on by the kernel.  The ID field is our UNIX process ID,
126755163Sshin * and the sequence number is an ascending integer.  The first 8 bytes
126855163Sshin * of the data portion are used to hold a UNIX "timeval" struct in VAX
126955163Sshin * byte-order, to compute the round-trip time.
127055163Sshin */
127178064Sumesize_t
1272216561Scharnierpingerlen(void)
127378064Sume{
127478064Sume	size_t l;
127578064Sume
127678064Sume	if (options & F_FQDN)
127778064Sume		l = ICMP6_NIQLEN + sizeof(dst.sin6_addr);
127878064Sume	else if (options & F_FQDNOLD)
127978064Sume		l = ICMP6_NIQLEN;
128078064Sume	else if (options & F_NODEADDR)
128178064Sume		l = ICMP6_NIQLEN + sizeof(dst.sin6_addr);
128278064Sume	else if (options & F_SUPTYPES)
128378064Sume		l = ICMP6_NIQLEN;
128478064Sume	else
128578064Sume		l = ICMP6ECHOLEN + datalen;
128678064Sume
128778064Sume	return l;
128878064Sume}
128978064Sume
129078984Sumeint
1291216561Scharnierpinger(void)
129255163Sshin{
129355163Sshin	struct icmp6_hdr *icp;
129455163Sshin	struct iovec iov[2];
129555163Sshin	int i, cc;
129678064Sume	struct icmp6_nodeinfo *nip;
129778064Sume	int seq;
129855163Sshin
129978984Sume	if (npackets && ntransmitted >= npackets)
130078984Sume		return(-1);	/* no more transmission */
130178984Sume
130255163Sshin	icp = (struct icmp6_hdr *)outpack;
130378064Sume	nip = (struct icmp6_nodeinfo *)outpack;
130462627Skris	memset(icp, 0, sizeof(*icp));
130555163Sshin	icp->icmp6_cksum = 0;
130678064Sume	seq = ntransmitted++;
130778064Sume	CLR(seq % mx_dup_ck);
130855163Sshin
130955163Sshin	if (options & F_FQDN) {
131055163Sshin		icp->icmp6_type = ICMP6_NI_QUERY;
131162627Skris		icp->icmp6_code = ICMP6_NI_SUBJ_IPV6;
131278064Sume		nip->ni_qtype = htons(NI_QTYPE_FQDN);
131378064Sume		nip->ni_flags = htons(0);
131478064Sume
131578064Sume		memcpy(nip->icmp6_ni_nonce, nonce,
131678064Sume		    sizeof(nip->icmp6_ni_nonce));
131778064Sume		*(u_int16_t *)nip->icmp6_ni_nonce = ntohs(seq);
131878064Sume
131962627Skris		memcpy(&outpack[ICMP6_NIQLEN], &dst.sin6_addr,
132062627Skris		    sizeof(dst.sin6_addr));
132162627Skris		cc = ICMP6_NIQLEN + sizeof(dst.sin6_addr);
132262627Skris		datalen = 0;
132362627Skris	} else if (options & F_FQDNOLD) {
132462627Skris		/* packet format in 03 draft - no Subject data on queries */
132562627Skris		icp->icmp6_type = ICMP6_NI_QUERY;
132678064Sume		icp->icmp6_code = 0;	/* code field is always 0 */
132778064Sume		nip->ni_qtype = htons(NI_QTYPE_FQDN);
132878064Sume		nip->ni_flags = htons(0);
132978064Sume
133078064Sume		memcpy(nip->icmp6_ni_nonce, nonce,
133178064Sume		    sizeof(nip->icmp6_ni_nonce));
133278064Sume		*(u_int16_t *)nip->icmp6_ni_nonce = ntohs(seq);
133378064Sume
133455163Sshin		cc = ICMP6_NIQLEN;
133555163Sshin		datalen = 0;
133655163Sshin	} else if (options & F_NODEADDR) {
133755163Sshin		icp->icmp6_type = ICMP6_NI_QUERY;
133862627Skris		icp->icmp6_code = ICMP6_NI_SUBJ_IPV6;
133978064Sume		nip->ni_qtype = htons(NI_QTYPE_NODEADDR);
134078064Sume		nip->ni_flags = naflags;
134178064Sume
134278064Sume		memcpy(nip->icmp6_ni_nonce, nonce,
134378064Sume		    sizeof(nip->icmp6_ni_nonce));
134478064Sume		*(u_int16_t *)nip->icmp6_ni_nonce = ntohs(seq);
134578064Sume
134662627Skris		memcpy(&outpack[ICMP6_NIQLEN], &dst.sin6_addr,
134762627Skris		    sizeof(dst.sin6_addr));
134862627Skris		cc = ICMP6_NIQLEN + sizeof(dst.sin6_addr);
134955163Sshin		datalen = 0;
135078064Sume	} else if (options & F_SUPTYPES) {
135178064Sume		icp->icmp6_type = ICMP6_NI_QUERY;
135278064Sume		icp->icmp6_code = ICMP6_NI_SUBJ_FQDN;	/*empty*/
135378064Sume		nip->ni_qtype = htons(NI_QTYPE_SUPTYPES);
135478064Sume		/* we support compressed bitmap */
135578064Sume		nip->ni_flags = NI_SUPTYPE_FLAG_COMPRESS;
135678064Sume
135778064Sume		memcpy(nip->icmp6_ni_nonce, nonce,
135878064Sume		    sizeof(nip->icmp6_ni_nonce));
135978064Sume		*(u_int16_t *)nip->icmp6_ni_nonce = ntohs(seq);
136078064Sume		cc = ICMP6_NIQLEN;
136178064Sume		datalen = 0;
136278064Sume	} else {
136355163Sshin		icp->icmp6_type = ICMP6_ECHO_REQUEST;
136478064Sume		icp->icmp6_code = 0;
136578064Sume		icp->icmp6_id = htons(ident);
136678064Sume		icp->icmp6_seq = ntohs(seq);
1367121472Sume		if (timing) {
1368121472Sume			struct timeval tv;
1369121472Sume			struct tv32 *tv32;
1370121472Sume			(void)gettimeofday(&tv, NULL);
1371121472Sume			tv32 = (struct tv32 *)&outpack[ICMP6ECHOLEN];
1372121472Sume			tv32->tv32_sec = htonl(tv.tv_sec);
1373121472Sume			tv32->tv32_usec = htonl(tv.tv_usec);
1374121472Sume		}
137555163Sshin		cc = ICMP6ECHOLEN + datalen;
137655163Sshin	}
137755163Sshin
137878064Sume#ifdef DIAGNOSTIC
137978064Sume	if (pingerlen() != cc)
138078064Sume		errx(1, "internal error; length mismatch");
138178064Sume#endif
138278064Sume
138355163Sshin	smsghdr.msg_name = (caddr_t)&dst;
138455163Sshin	smsghdr.msg_namelen = sizeof(dst);
138555163Sshin	memset(&iov, 0, sizeof(iov));
138655163Sshin	iov[0].iov_base = (caddr_t)outpack;
138755163Sshin	iov[0].iov_len = cc;
138855163Sshin	smsghdr.msg_iov = iov;
138955163Sshin	smsghdr.msg_iovlen = 1;
139055163Sshin
139155163Sshin	i = sendmsg(s, &smsghdr, 0);
139255163Sshin
139355163Sshin	if (i < 0 || i != cc)  {
139455163Sshin		if (i < 0)
139555163Sshin			warn("sendmsg");
139655163Sshin		(void)printf("ping6: wrote %s %d chars, ret=%d\n",
139755163Sshin		    hostname, cc, i);
139855163Sshin	}
139955163Sshin	if (!(options & F_QUIET) && options & F_FLOOD)
140055163Sshin		(void)write(STDOUT_FILENO, &DOT, 1);
140178984Sume
140278984Sume	return(0);
140355163Sshin}
140455163Sshin
140578064Sumeint
1406216561Scharniermyechoreply(const struct icmp6_hdr *icp)
140778064Sume{
140878064Sume	if (ntohs(icp->icmp6_id) == ident)
140978064Sume		return 1;
141078064Sume	else
141178064Sume		return 0;
141278064Sume}
141378064Sume
141478064Sumeint
1415216561Scharniermynireply(const struct icmp6_nodeinfo *nip)
141678064Sume{
141778064Sume	if (memcmp(nip->icmp6_ni_nonce + sizeof(u_int16_t),
141878064Sume	    nonce + sizeof(u_int16_t),
141978064Sume	    sizeof(nonce) - sizeof(u_int16_t)) == 0)
142078064Sume		return 1;
142178064Sume	else
142278064Sume		return 0;
142378064Sume}
142478064Sume
142578064Sumechar *
1426216561Scharnierdnsdecode(const u_char **sp, const u_char *ep, const u_char *base, char *buf,
1427216561Scharnier	size_t bufsiz)
1428216561Scharnier	/*base for compressed name*/
142978064Sume{
143078064Sume	int i;
143178064Sume	const u_char *cp;
143278064Sume	char cresult[MAXDNAME + 1];
143378064Sume	const u_char *comp;
143478064Sume	int l;
143578064Sume
143678064Sume	cp = *sp;
143778064Sume	*buf = '\0';
143878064Sume
143978064Sume	if (cp >= ep)
144078064Sume		return NULL;
144178064Sume	while (cp < ep) {
144278064Sume		i = *cp;
144378064Sume		if (i == 0 || cp != *sp) {
1444121472Sume			if (strlcat((char *)buf, ".", bufsiz) >= bufsiz)
144578064Sume				return NULL;	/*result overrun*/
144678064Sume		}
144778064Sume		if (i == 0)
144878064Sume			break;
144978064Sume		cp++;
145078064Sume
145178064Sume		if ((i & 0xc0) == 0xc0 && cp - base > (i & 0x3f)) {
145278064Sume			/* DNS compression */
145378064Sume			if (!base)
145478064Sume				return NULL;
145578064Sume
145678064Sume			comp = base + (i & 0x3f);
145778064Sume			if (dnsdecode(&comp, cp, base, cresult,
145878064Sume			    sizeof(cresult)) == NULL)
145978064Sume				return NULL;
146078064Sume			if (strlcat(buf, cresult, bufsiz) >= bufsiz)
146178064Sume				return NULL;	/*result overrun*/
146278064Sume			break;
146378064Sume		} else if ((i & 0x3f) == i) {
146478064Sume			if (i > ep - cp)
146578064Sume				return NULL;	/*source overrun*/
146678064Sume			while (i-- > 0 && cp < ep) {
146778064Sume				l = snprintf(cresult, sizeof(cresult),
146878064Sume				    isprint(*cp) ? "%c" : "\\%03o", *cp & 0xff);
1469121472Sume				if (l >= sizeof(cresult) || l < 0)
147078064Sume					return NULL;
147178064Sume				if (strlcat(buf, cresult, bufsiz) >= bufsiz)
147278064Sume					return NULL;	/*result overrun*/
147378064Sume				cp++;
147478064Sume			}
147578064Sume		} else
147678064Sume			return NULL;	/*invalid label*/
147778064Sume	}
147878064Sume	if (i != 0)
147978064Sume		return NULL;	/*not terminated*/
148078064Sume	cp++;
148178064Sume	*sp = cp;
148278064Sume	return buf;
148378064Sume}
148478064Sume
148555163Sshin/*
148655163Sshin * pr_pack --
148755163Sshin *	Print out the packet, if it came from us.  This logic is necessary
148855163Sshin * because ALL readers of the ICMP socket get a copy of ALL ICMP packets
148955163Sshin * which arrive ('tis only fair).  This permits multiple copies of this
149055163Sshin * program to be run without having intermingled output (or statistics!).
149155163Sshin */
149255163Sshinvoid
1493216561Scharnierpr_pack(u_char *buf, int cc, struct msghdr *mhdr)
149455163Sshin{
149562627Skris#define safeputc(c)	printf((isprint((c)) ? "%c" : "\\%03o"), c)
149655163Sshin	struct icmp6_hdr *icp;
149778064Sume	struct icmp6_nodeinfo *ni;
149855163Sshin	int i;
149955163Sshin	int hoplim;
150078064Sume	struct sockaddr *from;
150178064Sume	int fromlen;
150255163Sshin	u_char *cp = NULL, *dp, *end = buf + cc;
150362627Skris	struct in6_pktinfo *pktinfo = NULL;
1504121472Sume	struct timeval tv, tp;
1505121472Sume	struct tv32 *tpp;
150655163Sshin	double triptime = 0;
150755163Sshin	int dupflag;
150855163Sshin	size_t off;
150962627Skris	int oldfqdn;
151078064Sume	u_int16_t seq;
151178064Sume	char dnsname[MAXDNAME + 1];
151255163Sshin
151355163Sshin	(void)gettimeofday(&tv, NULL);
151455163Sshin
151578064Sume	if (!mhdr || !mhdr->msg_name ||
151678064Sume	    mhdr->msg_namelen != sizeof(struct sockaddr_in6) ||
151778064Sume	    ((struct sockaddr *)mhdr->msg_name)->sa_family != AF_INET6) {
151878064Sume		if (options & F_VERBOSE)
1519121472Sume			warnx("invalid peername");
152078064Sume		return;
152178064Sume	}
152278064Sume	from = (struct sockaddr *)mhdr->msg_name;
152378064Sume	fromlen = mhdr->msg_namelen;
152455163Sshin	if (cc < sizeof(struct icmp6_hdr)) {
152555163Sshin		if (options & F_VERBOSE)
152687668Scharnier			warnx("packet too short (%d bytes) from %s", cc,
152778064Sume			    pr_addr(from, fromlen));
152855163Sshin		return;
152955163Sshin	}
1530168866Smtm	if (((mhdr->msg_flags & MSG_CTRUNC) != 0) &&
1531168866Smtm	    (options & F_VERBOSE) != 0)
1532168866Smtm		warnx("some control data discarded, insufficient buffer size");
153355163Sshin	icp = (struct icmp6_hdr *)buf;
153478064Sume	ni = (struct icmp6_nodeinfo *)buf;
153555163Sshin	off = 0;
153655163Sshin
153755163Sshin	if ((hoplim = get_hoplim(mhdr)) == -1) {
153855163Sshin		warnx("failed to get receiving hop limit");
153955163Sshin		return;
154055163Sshin	}
154162627Skris	if ((pktinfo = get_rcvpktinfo(mhdr)) == NULL) {
154287668Scharnier		warnx("failed to get receiving packet information");
154362627Skris		return;
154462627Skris	}
154555163Sshin
154678064Sume	if (icp->icmp6_type == ICMP6_ECHO_REPLY && myechoreply(icp)) {
154778064Sume		seq = ntohs(icp->icmp6_seq);
154855163Sshin		++nreceived;
154955163Sshin		if (timing) {
1550121472Sume			tpp = (struct tv32 *)(icp + 1);
1551121472Sume			tp.tv_sec = ntohl(tpp->tv32_sec);
1552121472Sume			tp.tv_usec = ntohl(tpp->tv32_usec);
1553121472Sume			tvsub(&tv, &tp);
155455163Sshin			triptime = ((double)tv.tv_sec) * 1000.0 +
155555163Sshin			    ((double)tv.tv_usec) / 1000.0;
155655163Sshin			tsum += triptime;
155778064Sume			tsumsq += triptime * triptime;
155855163Sshin			if (triptime < tmin)
155955163Sshin				tmin = triptime;
156055163Sshin			if (triptime > tmax)
156155163Sshin				tmax = triptime;
156255163Sshin		}
156355163Sshin
156478064Sume		if (TST(seq % mx_dup_ck)) {
156555163Sshin			++nrepeats;
156655163Sshin			--nreceived;
156755163Sshin			dupflag = 1;
156855163Sshin		} else {
156978064Sume			SET(seq % mx_dup_ck);
157055163Sshin			dupflag = 0;
157155163Sshin		}
157255163Sshin
157355163Sshin		if (options & F_QUIET)
157455163Sshin			return;
157555163Sshin
157655163Sshin		if (options & F_FLOOD)
157755163Sshin			(void)write(STDOUT_FILENO, &BSPACE, 1);
157855163Sshin		else {
1579182195Smatteo			if (options & F_AUDIBLE)
1580182195Smatteo				(void)write(STDOUT_FILENO, &BBELL, 1);
158155163Sshin			(void)printf("%d bytes from %s, icmp_seq=%u", cc,
158278064Sume			    pr_addr(from, fromlen), seq);
158355163Sshin			(void)printf(" hlim=%d", hoplim);
158462627Skris			if ((options & F_VERBOSE) != 0) {
158562627Skris				struct sockaddr_in6 dstsa;
158662627Skris
158762627Skris				memset(&dstsa, 0, sizeof(dstsa));
158862627Skris				dstsa.sin6_family = AF_INET6;
158962627Skris				dstsa.sin6_len = sizeof(dstsa);
159062627Skris				dstsa.sin6_scope_id = pktinfo->ipi6_ifindex;
159162627Skris				dstsa.sin6_addr = pktinfo->ipi6_addr;
159278064Sume				(void)printf(" dst=%s",
159378064Sume				    pr_addr((struct sockaddr *)&dstsa,
159478064Sume				    sizeof(dstsa)));
159562627Skris			}
159655163Sshin			if (timing)
1597117824Smaxim				(void)printf(" time=%.3f ms", triptime);
159855163Sshin			if (dupflag)
159955163Sshin				(void)printf("(DUP!)");
160055163Sshin			/* check the data */
160155163Sshin			cp = buf + off + ICMP6ECHOLEN + ICMP6ECHOTMLEN;
160255163Sshin			dp = outpack + ICMP6ECHOLEN + ICMP6ECHOTMLEN;
160355163Sshin			for (i = 8; cp < end; ++i, ++cp, ++dp) {
160455163Sshin				if (*cp != *dp) {
160555163Sshin					(void)printf("\nwrong data byte #%d should be 0x%x but was 0x%x", i, *dp, *cp);
160655163Sshin					break;
160755163Sshin				}
160855163Sshin			}
160955163Sshin		}
161078064Sume	} else if (icp->icmp6_type == ICMP6_NI_REPLY && mynireply(ni)) {
161178064Sume		seq = ntohs(*(u_int16_t *)ni->icmp6_ni_nonce);
161278064Sume		++nreceived;
161378064Sume		if (TST(seq % mx_dup_ck)) {
161478064Sume			++nrepeats;
161578064Sume			--nreceived;
161678064Sume			dupflag = 1;
161778064Sume		} else {
161878064Sume			SET(seq % mx_dup_ck);
161978064Sume			dupflag = 0;
162078064Sume		}
162155163Sshin
162278064Sume		if (options & F_QUIET)
162378064Sume			return;
162455163Sshin
162578064Sume		(void)printf("%d bytes from %s: ", cc, pr_addr(from, fromlen));
162678064Sume
162778064Sume		switch (ntohs(ni->ni_code)) {
162878064Sume		case ICMP6_NI_SUCCESS:
162978064Sume			break;
163078064Sume		case ICMP6_NI_REFUSED:
163178064Sume			printf("refused, type 0x%x", ntohs(ni->ni_type));
163278064Sume			goto fqdnend;
163378064Sume		case ICMP6_NI_UNKNOWN:
163478064Sume			printf("unknown, type 0x%x", ntohs(ni->ni_type));
163578064Sume			goto fqdnend;
163678064Sume		default:
163778064Sume			printf("unknown code 0x%x, type 0x%x",
163878064Sume			    ntohs(ni->ni_code), ntohs(ni->ni_type));
163978064Sume			goto fqdnend;
164078064Sume		}
164178064Sume
164278064Sume		switch (ntohs(ni->ni_qtype)) {
164378064Sume		case NI_QTYPE_NOOP:
164478064Sume			printf("NodeInfo NOOP");
164578064Sume			break;
164678064Sume		case NI_QTYPE_SUPTYPES:
164778064Sume			pr_suptypes(ni, end - (u_char *)ni);
164878064Sume			break;
164978064Sume		case NI_QTYPE_NODEADDR:
165078064Sume			pr_nodeaddr(ni, end - (u_char *)ni);
165178064Sume			break;
165278064Sume		case NI_QTYPE_FQDN:
165378064Sume		default:	/* XXX: for backward compatibility */
165462627Skris			cp = (u_char *)ni + ICMP6_NIRLEN;
165562627Skris			if (buf[off + ICMP6_NIRLEN] ==
165662627Skris			    cc - off - ICMP6_NIRLEN - 1)
165762627Skris				oldfqdn = 1;
165862627Skris			else
165962627Skris				oldfqdn = 0;
166062627Skris			if (oldfqdn) {
166178064Sume				cp++;	/* skip length */
166262627Skris				while (cp < end) {
166362627Skris					safeputc(*cp & 0xff);
166462627Skris					cp++;
166562627Skris				}
166662627Skris			} else {
166778064Sume				i = 0;
166862627Skris				while (cp < end) {
166978064Sume					if (dnsdecode((const u_char **)&cp, end,
167078064Sume					    (const u_char *)(ni + 1), dnsname,
167178064Sume					    sizeof(dnsname)) == NULL) {
167278064Sume						printf("???");
167362627Skris						break;
167462627Skris					}
167578064Sume					/*
167678064Sume					 * name-lookup special handling for
167778064Sume					 * truncated name
167878064Sume					 */
167978064Sume					if (cp + 1 <= end && !*cp &&
168078064Sume					    strlen(dnsname) > 0) {
168178064Sume						dnsname[strlen(dnsname) - 1] = '\0';
168278064Sume						cp++;
168378064Sume					}
168478064Sume					printf("%s%s", i > 0 ? "," : "",
168578064Sume					    dnsname);
168662627Skris				}
168762627Skris			}
168862627Skris			if (options & F_VERBOSE) {
168962627Skris				int32_t ttl;
169062627Skris				int comma = 0;
169155163Sshin
169262627Skris				(void)printf(" (");	/*)*/
169355163Sshin
169478064Sume				switch (ni->ni_code) {
169562627Skris				case ICMP6_NI_REFUSED:
169662627Skris					(void)printf("refused");
169762627Skris					comma++;
169862627Skris					break;
169962627Skris				case ICMP6_NI_UNKNOWN:
170087668Scharnier					(void)printf("unknown qtype");
170162627Skris					comma++;
170262627Skris					break;
170362627Skris				}
170455163Sshin
170562627Skris				if ((end - (u_char *)ni) < ICMP6_NIRLEN) {
170678064Sume					/* case of refusion, unknown */
170778064Sume					/*(*/
170878064Sume					putchar(')');
170962627Skris					goto fqdnend;
171062627Skris				}
171162627Skris				ttl = (int32_t)ntohl(*(u_long *)&buf[off+ICMP6ECHOLEN+8]);
171262627Skris				if (comma)
171362627Skris					printf(",");
171478064Sume				if (!(ni->ni_flags & NI_FQDN_FLAG_VALIDTTL)) {
171562627Skris					(void)printf("TTL=%d:meaningless",
171678064Sume					    (int)ttl);
171778064Sume				} else {
171862627Skris					if (ttl < 0) {
171962627Skris						(void)printf("TTL=%d:invalid",
172078064Sume						   ttl);
172162627Skris					} else
172262627Skris						(void)printf("TTL=%d", ttl);
172362627Skris				}
172462627Skris				comma++;
172555163Sshin
172662627Skris				if (oldfqdn) {
172762627Skris					if (comma)
172862627Skris						printf(",");
172962627Skris					printf("03 draft");
173062627Skris					comma++;
173162627Skris				} else {
173262627Skris					cp = (u_char *)ni + ICMP6_NIRLEN;
173362627Skris					if (cp == end) {
173462627Skris						if (comma)
173562627Skris							printf(",");
173662627Skris						printf("no name");
173762627Skris						comma++;
173862627Skris					}
173962627Skris				}
174062627Skris
174162627Skris				if (buf[off + ICMP6_NIRLEN] !=
174262627Skris				    cc - off - ICMP6_NIRLEN - 1 && oldfqdn) {
174362627Skris					if (comma)
174462627Skris						printf(",");
174562627Skris					(void)printf("invalid namelen:%d/%lu",
174678064Sume					    buf[off + ICMP6_NIRLEN],
174778064Sume					    (u_long)cc - off - ICMP6_NIRLEN - 1);
174862627Skris					comma++;
174962627Skris				}
175062627Skris				/*(*/
175162627Skris				putchar(')');
175262627Skris			}
175378064Sume		fqdnend:
175462627Skris			;
175555163Sshin		}
175655163Sshin	} else {
175755163Sshin		/* We've got something other than an ECHOREPLY */
175855163Sshin		if (!(options & F_VERBOSE))
175955163Sshin			return;
176078064Sume		(void)printf("%d bytes from %s: ", cc, pr_addr(from, fromlen));
176155163Sshin		pr_icmph(icp, end);
176255163Sshin	}
176355163Sshin
176455163Sshin	if (!(options & F_FLOOD)) {
176555163Sshin		(void)putchar('\n');
176662627Skris		if (options & F_VERBOSE)
176762627Skris			pr_exthdrs(mhdr);
176855163Sshin		(void)fflush(stdout);
176955163Sshin	}
177062627Skris#undef safeputc
177155163Sshin}
177255163Sshin
177355163Sshinvoid
1774216561Scharnierpr_exthdrs(struct msghdr *mhdr)
177562627Skris{
1776168866Smtm	ssize_t	bufsize;
1777168866Smtm	void	*bufp;
177862627Skris	struct cmsghdr *cm;
177962627Skris
1780168866Smtm	bufsize = 0;
1781168866Smtm	bufp = mhdr->msg_control;
178262627Skris	for (cm = (struct cmsghdr *)CMSG_FIRSTHDR(mhdr); cm;
178362627Skris	     cm = (struct cmsghdr *)CMSG_NXTHDR(mhdr, cm)) {
178462627Skris		if (cm->cmsg_level != IPPROTO_IPV6)
178562627Skris			continue;
178662627Skris
1787168866Smtm		bufsize = CONTROLLEN - ((caddr_t)CMSG_DATA(cm) - (caddr_t)bufp);
1788168866Smtm		if (bufsize <= 0)
1789168866Smtm			continue;
179078064Sume		switch (cm->cmsg_type) {
179162627Skris		case IPV6_HOPOPTS:
179262627Skris			printf("  HbH Options: ");
1793168866Smtm			pr_ip6opt(CMSG_DATA(cm), (size_t)bufsize);
179462627Skris			break;
179562627Skris		case IPV6_DSTOPTS:
179662627Skris#ifdef IPV6_RTHDRDSTOPTS
179762627Skris		case IPV6_RTHDRDSTOPTS:
179862627Skris#endif
179962627Skris			printf("  Dst Options: ");
1800168866Smtm			pr_ip6opt(CMSG_DATA(cm), (size_t)bufsize);
180162627Skris			break;
180262627Skris		case IPV6_RTHDR:
180362627Skris			printf("  Routing: ");
1804168866Smtm			pr_rthdr(CMSG_DATA(cm), (size_t)bufsize);
180562627Skris			break;
180662627Skris		}
180762627Skris	}
180862627Skris}
180962627Skris
181062627Skris#ifdef USE_RFC2292BIS
181162627Skrisvoid
1812168866Smtmpr_ip6opt(void *extbuf, size_t bufsize)
181362627Skris{
181462627Skris	struct ip6_hbh *ext;
181562627Skris	int currentlen;
181662627Skris	u_int8_t type;
1817231812Seadler	socklen_t extlen, len;
181862627Skris	void *databuf;
181962627Skris	size_t offset;
182062627Skris	u_int16_t value2;
182162627Skris	u_int32_t value4;
182262627Skris
182362627Skris	ext = (struct ip6_hbh *)extbuf;
182462627Skris	extlen = (ext->ip6h_len + 1) * 8;
182578064Sume	printf("nxt %u, len %u (%lu bytes)\n", ext->ip6h_nxt,
182678064Sume	    (unsigned int)ext->ip6h_len, (unsigned long)extlen);
182762627Skris
1828168866Smtm	/*
1829168866Smtm	 * Bounds checking on the ancillary data buffer:
1830168866Smtm	 *     subtract the size of a cmsg structure from the buffer size.
1831168866Smtm	 */
1832168866Smtm	if (bufsize < (extlen  + CMSG_SPACE(0))) {
1833168866Smtm		extlen = bufsize - CMSG_SPACE(0);
1834168866Smtm		warnx("options truncated, showing only %u (total=%u)",
1835168866Smtm		    (unsigned int)(extlen / 8 - 1),
1836168866Smtm		    (unsigned int)(ext->ip6h_len));
1837168866Smtm	}
1838168866Smtm
183962627Skris	currentlen = 0;
184062627Skris	while (1) {
184162627Skris		currentlen = inet6_opt_next(extbuf, extlen, currentlen,
184278064Sume		    &type, &len, &databuf);
184362627Skris		if (currentlen == -1)
184462627Skris			break;
184562627Skris		switch (type) {
184662627Skris		/*
184762627Skris		 * Note that inet6_opt_next automatically skips any padding
184862627Skris		 * optins.
184962627Skris		 */
185062627Skris		case IP6OPT_JUMBO:
185162627Skris			offset = 0;
185262627Skris			offset = inet6_opt_get_val(databuf, offset,
185378064Sume			    &value4, sizeof(value4));
185462627Skris			printf("    Jumbo Payload Opt: Length %u\n",
185578064Sume			    (u_int32_t)ntohl(value4));
185662627Skris			break;
185762627Skris		case IP6OPT_ROUTER_ALERT:
185862627Skris			offset = 0;
185962627Skris			offset = inet6_opt_get_val(databuf, offset,
186062627Skris						   &value2, sizeof(value2));
186162627Skris			printf("    Router Alert Opt: Type %u\n",
186278064Sume			    ntohs(value2));
186362627Skris			break;
186462627Skris		default:
186578064Sume			printf("    Received Opt %u len %lu\n",
186678064Sume			    type, (unsigned long)len);
186762627Skris			break;
186862627Skris		}
186962627Skris	}
187062627Skris	return;
187162627Skris}
187262627Skris#else  /* !USE_RFC2292BIS */
187362627Skris/* ARGSUSED */
187462627Skrisvoid
1875168866Smtmpr_ip6opt(void *extbuf, size_t bufsize __unused)
187662627Skris{
187762627Skris	putchar('\n');
187862627Skris	return;
187962627Skris}
188062627Skris#endif /* USE_RFC2292BIS */
188162627Skris
188262627Skris#ifdef USE_RFC2292BIS
188362627Skrisvoid
1884168866Smtmpr_rthdr(void *extbuf, size_t bufsize)
188562627Skris{
188662627Skris	struct in6_addr *in6;
188762627Skris	char ntopbuf[INET6_ADDRSTRLEN];
188862627Skris	struct ip6_rthdr *rh = (struct ip6_rthdr *)extbuf;
1889168866Smtm	int i, segments, origsegs, rthsize, size0, size1;
189062627Skris
189162627Skris	/* print fixed part of the header */
189262627Skris	printf("nxt %u, len %u (%d bytes), type %u, ", rh->ip6r_nxt,
189378064Sume	    rh->ip6r_len, (rh->ip6r_len + 1) << 3, rh->ip6r_type);
1894168866Smtm	if ((segments = inet6_rth_segments(extbuf)) >= 0) {
189562627Skris		printf("%d segments, ", segments);
1896168866Smtm		printf("%d left\n", rh->ip6r_segleft);
1897168866Smtm	} else {
189862627Skris		printf("segments unknown, ");
1899168866Smtm		printf("%d left\n", rh->ip6r_segleft);
1900168866Smtm		return;
1901168866Smtm	}
190262627Skris
1903168866Smtm	/*
1904168866Smtm	 * Bounds checking on the ancillary data buffer. When calculating
1905168866Smtm	 * the number of items to show keep in mind:
1906168866Smtm	 *	- The size of the cmsg structure
1907168866Smtm	 *	- The size of one segment (the size of a Type 0 routing header)
1908168866Smtm	 *	- When dividing add a fudge factor of one in case the
1909168866Smtm	 *	  dividend is not evenly divisible by the divisor
1910168866Smtm	 */
1911168866Smtm	rthsize = (rh->ip6r_len + 1) * 8;
1912168866Smtm	if (bufsize < (rthsize + CMSG_SPACE(0))) {
1913168866Smtm		origsegs = segments;
1914168866Smtm		size0 = inet6_rth_space(IPV6_RTHDR_TYPE_0, 0);
1915168866Smtm		size1 = inet6_rth_space(IPV6_RTHDR_TYPE_0, 1);
1916168866Smtm		segments -= (rthsize - (bufsize - CMSG_SPACE(0))) /
1917168866Smtm		    (size1 - size0) + 1;
1918168866Smtm		warnx("segments truncated, showing only %d (total=%d)",
1919168866Smtm		    segments, origsegs);
1920168866Smtm	}
1921168866Smtm
192262627Skris	for (i = 0; i < segments; i++) {
192362627Skris		in6 = inet6_rth_getaddr(extbuf, i);
192462627Skris		if (in6 == NULL)
192562627Skris			printf("   [%d]<NULL>\n", i);
192678064Sume		else {
192778064Sume			if (!inet_ntop(AF_INET6, in6, ntopbuf,
192878064Sume			    sizeof(ntopbuf)))
1929121472Sume				strlcpy(ntopbuf, "?", sizeof(ntopbuf));
193078064Sume			printf("   [%d]%s\n", i, ntopbuf);
193178064Sume		}
193262627Skris	}
193362627Skris
193462627Skris	return;
193578064Sume
193662627Skris}
193778064Sume
193862627Skris#else  /* !USE_RFC2292BIS */
193962627Skris/* ARGSUSED */
194062627Skrisvoid
1941168866Smtmpr_rthdr(void *extbuf, size_t bufsize __unused)
194262627Skris{
194362627Skris	putchar('\n');
194462627Skris	return;
194562627Skris}
194662627Skris#endif /* USE_RFC2292BIS */
194762627Skris
194878064Sumeint
1949216561Scharnierpr_bitrange(u_int32_t v, int soff, int ii)
195078064Sume{
195178064Sume	int off;
195278064Sume	int i;
195362627Skris
195478064Sume	off = 0;
195578064Sume	while (off < 32) {
195678064Sume		/* shift till we have 0x01 */
195778064Sume		if ((v & 0x01) == 0) {
195878064Sume			if (ii > 1)
1959121472Sume				printf("-%u", soff + off - 1);
196078064Sume			ii = 0;
196178064Sume			switch (v & 0x0f) {
196278064Sume			case 0x00:
196378064Sume				v >>= 4;
196478064Sume				off += 4;
196578064Sume				continue;
196678064Sume			case 0x08:
196778064Sume				v >>= 3;
196878064Sume				off += 3;
196978064Sume				continue;
197078064Sume			case 0x04: case 0x0c:
197178064Sume				v >>= 2;
197278064Sume				off += 2;
197378064Sume				continue;
197478064Sume			default:
197578064Sume				v >>= 1;
197678064Sume				off += 1;
197778064Sume				continue;
197878064Sume			}
197978064Sume		}
198078064Sume
198178064Sume		/* we have 0x01 with us */
198278064Sume		for (i = 0; i < 32 - off; i++) {
198378064Sume			if ((v & (0x01 << i)) == 0)
198478064Sume				break;
198578064Sume		}
198678064Sume		if (!ii)
1987121472Sume			printf(" %u", soff + off);
198878064Sume		ii += i;
198978064Sume		v >>= i; off += i;
199078064Sume	}
199178064Sume	return ii;
199278064Sume}
199378064Sume
199462627Skrisvoid
1995216561Scharnierpr_suptypes(struct icmp6_nodeinfo *ni, size_t nilen)
1996216561Scharnier	/* ni->qtype must be SUPTYPES */
199778064Sume{
199878064Sume	size_t clen;
199978064Sume	u_int32_t v;
200078064Sume	const u_char *cp, *end;
200178064Sume	u_int16_t cur;
200278064Sume	struct cbit {
200378064Sume		u_int16_t words;	/*32bit count*/
200478064Sume		u_int16_t skip;
200578064Sume	} cbit;
200678064Sume#define MAXQTYPES	(1 << 16)
200778064Sume	size_t off;
200878064Sume	int b;
200978064Sume
201078064Sume	cp = (u_char *)(ni + 1);
201178064Sume	end = ((u_char *)ni) + nilen;
201278064Sume	cur = 0;
201378064Sume	b = 0;
201478064Sume
201578064Sume	printf("NodeInfo Supported Qtypes");
201678064Sume	if (options & F_VERBOSE) {
201778064Sume		if (ni->ni_flags & NI_SUPTYPE_FLAG_COMPRESS)
201878064Sume			printf(", compressed bitmap");
201978064Sume		else
202078064Sume			printf(", raw bitmap");
202178064Sume	}
202278064Sume
202378064Sume	while (cp < end) {
202478064Sume		clen = (size_t)(end - cp);
202578064Sume		if ((ni->ni_flags & NI_SUPTYPE_FLAG_COMPRESS) == 0) {
202678064Sume			if (clen == 0 || clen > MAXQTYPES / 8 ||
202778064Sume			    clen % sizeof(v)) {
202878064Sume				printf("???");
202978064Sume				return;
203078064Sume			}
203178064Sume		} else {
203278064Sume			if (clen < sizeof(cbit) || clen % sizeof(v))
203378064Sume				return;
203478064Sume			memcpy(&cbit, cp, sizeof(cbit));
203578064Sume			if (sizeof(cbit) + ntohs(cbit.words) * sizeof(v) >
203678064Sume			    clen)
203778064Sume				return;
203878064Sume			cp += sizeof(cbit);
203978064Sume			clen = ntohs(cbit.words) * sizeof(v);
204078064Sume			if (cur + clen * 8 + (u_long)ntohs(cbit.skip) * 32 >
204178064Sume			    MAXQTYPES)
204278064Sume				return;
204378064Sume		}
204478064Sume
204578064Sume		for (off = 0; off < clen; off += sizeof(v)) {
204678064Sume			memcpy(&v, cp + off, sizeof(v));
204778064Sume			v = (u_int32_t)ntohl(v);
204878064Sume			b = pr_bitrange(v, (int)(cur + off * 8), b);
204978064Sume		}
205078064Sume		/* flush the remaining bits */
205178064Sume		b = pr_bitrange(0, (int)(cur + off * 8), b);
205278064Sume
205378064Sume		cp += clen;
205478064Sume		cur += clen * 8;
205578064Sume		if ((ni->ni_flags & NI_SUPTYPE_FLAG_COMPRESS) != 0)
205678064Sume			cur += ntohs(cbit.skip) * 32;
205778064Sume	}
205878064Sume}
205978064Sume
206078064Sumevoid
2061216561Scharnierpr_nodeaddr(struct icmp6_nodeinfo *ni, int nilen)
2062216561Scharnier	/* ni->qtype must be NODEADDR */
206355163Sshin{
206478064Sume	u_char *cp = (u_char *)(ni + 1);
206562627Skris	char ntop_buf[INET6_ADDRSTRLEN];
206678064Sume	int withttl = 0;
206755163Sshin
206855163Sshin	nilen -= sizeof(struct icmp6_nodeinfo);
206955163Sshin
207055163Sshin	if (options & F_VERBOSE) {
207178064Sume		switch (ni->ni_code) {
207278064Sume		case ICMP6_NI_REFUSED:
207378064Sume			(void)printf("refused");
207478064Sume			break;
207578064Sume		case ICMP6_NI_UNKNOWN:
207678064Sume			(void)printf("unknown qtype");
207778064Sume			break;
207855163Sshin		}
207978064Sume		if (ni->ni_flags & NI_NODEADDR_FLAG_TRUNCATE)
208055163Sshin			(void)printf(" truncated");
208155163Sshin	}
208255163Sshin	putchar('\n');
208355163Sshin	if (nilen <= 0)
208455163Sshin		printf("  no address\n");
208578064Sume
208678064Sume	/*
208778064Sume	 * In icmp-name-lookups 05 and later, TTL of each returned address
208878064Sume	 * is contained in the resposne. We try to detect the version
208978064Sume	 * by the length of the data, but note that the detection algorithm
209078064Sume	 * is incomplete. We assume the latest draft by default.
209178064Sume	 */
209278064Sume	if (nilen % (sizeof(u_int32_t) + sizeof(struct in6_addr)) == 0)
209378064Sume		withttl = 1;
209478064Sume	while (nilen > 0) {
209578064Sume		u_int32_t ttl;
209678064Sume
209778064Sume		if (withttl) {
209878064Sume			/* XXX: alignment? */
209978064Sume			ttl = (u_int32_t)ntohl(*(u_int32_t *)cp);
210078064Sume			cp += sizeof(u_int32_t);
210178064Sume			nilen -= sizeof(u_int32_t);
210278064Sume		}
210378064Sume
210478064Sume		if (inet_ntop(AF_INET6, cp, ntop_buf, sizeof(ntop_buf)) ==
210578064Sume		    NULL)
2106121472Sume			strlcpy(ntop_buf, "?", sizeof(ntop_buf));
210778064Sume		printf("  %s", ntop_buf);
210878064Sume		if (withttl) {
210978064Sume			if (ttl == 0xffffffff) {
211078064Sume				/*
211178064Sume				 * XXX: can this convention be applied to all
211278064Sume				 * type of TTL (i.e. non-ND TTL)?
211378064Sume				 */
211478064Sume				printf("(TTL=infty)");
211578064Sume			}
211678064Sume			else
211778064Sume				printf("(TTL=%u)", ttl);
211878064Sume		}
211978064Sume		putchar('\n');
212078064Sume
212178064Sume		nilen -= sizeof(struct in6_addr);
212278064Sume		cp += sizeof(struct in6_addr);
212355163Sshin	}
212455163Sshin}
212555163Sshin
212655163Sshinint
2127216561Scharnierget_hoplim(struct msghdr *mhdr)
212855163Sshin{
212955163Sshin	struct cmsghdr *cm;
213055163Sshin
213155163Sshin	for (cm = (struct cmsghdr *)CMSG_FIRSTHDR(mhdr); cm;
213255163Sshin	     cm = (struct cmsghdr *)CMSG_NXTHDR(mhdr, cm)) {
213378064Sume		if (cm->cmsg_len == 0)
213478064Sume			return(-1);
213578064Sume
213655163Sshin		if (cm->cmsg_level == IPPROTO_IPV6 &&
213755163Sshin		    cm->cmsg_type == IPV6_HOPLIMIT &&
213855163Sshin		    cm->cmsg_len == CMSG_LEN(sizeof(int)))
213955163Sshin			return(*(int *)CMSG_DATA(cm));
214055163Sshin	}
214155163Sshin
214255163Sshin	return(-1);
214355163Sshin}
214455163Sshin
214562627Skrisstruct in6_pktinfo *
2146216561Scharnierget_rcvpktinfo(struct msghdr *mhdr)
214762627Skris{
214862627Skris	struct cmsghdr *cm;
214962627Skris
215062627Skris	for (cm = (struct cmsghdr *)CMSG_FIRSTHDR(mhdr); cm;
215162627Skris	     cm = (struct cmsghdr *)CMSG_NXTHDR(mhdr, cm)) {
215278064Sume		if (cm->cmsg_len == 0)
215378064Sume			return(NULL);
215478064Sume
215562627Skris		if (cm->cmsg_level == IPPROTO_IPV6 &&
215662627Skris		    cm->cmsg_type == IPV6_PKTINFO &&
215762627Skris		    cm->cmsg_len == CMSG_LEN(sizeof(struct in6_pktinfo)))
215862627Skris			return((struct in6_pktinfo *)CMSG_DATA(cm));
215962627Skris	}
216062627Skris
216162627Skris	return(NULL);
216262627Skris}
216362627Skris
216478064Sumeint
2165216561Scharnierget_pathmtu(struct msghdr *mhdr)
216678064Sume{
216778064Sume#ifdef IPV6_RECVPATHMTU
216878064Sume	struct cmsghdr *cm;
216978064Sume	struct ip6_mtuinfo *mtuctl = NULL;
217078064Sume
217178064Sume	for (cm = (struct cmsghdr *)CMSG_FIRSTHDR(mhdr); cm;
217278064Sume	     cm = (struct cmsghdr *)CMSG_NXTHDR(mhdr, cm)) {
217378064Sume		if (cm->cmsg_len == 0)
217478064Sume			return(0);
217578064Sume
217678064Sume		if (cm->cmsg_level == IPPROTO_IPV6 &&
217778064Sume		    cm->cmsg_type == IPV6_PATHMTU &&
217878064Sume		    cm->cmsg_len == CMSG_LEN(sizeof(struct ip6_mtuinfo))) {
217978064Sume			mtuctl = (struct ip6_mtuinfo *)CMSG_DATA(cm);
218078064Sume
218178064Sume			/*
218278064Sume			 * If the notified destination is different from
218378064Sume			 * the one we are pinging, just ignore the info.
218478064Sume			 * We check the scope ID only when both notified value
218578064Sume			 * and our own value have non-0 values, because we may
218678064Sume			 * have used the default scope zone ID for sending,
218778064Sume			 * in which case the scope ID value is 0.
218878064Sume			 */
218978064Sume			if (!IN6_ARE_ADDR_EQUAL(&mtuctl->ip6m_addr.sin6_addr,
219078064Sume						&dst.sin6_addr) ||
219178064Sume			    (mtuctl->ip6m_addr.sin6_scope_id &&
219278064Sume			     dst.sin6_scope_id &&
219378064Sume			     mtuctl->ip6m_addr.sin6_scope_id !=
219478064Sume			     dst.sin6_scope_id)) {
219578064Sume				if ((options & F_VERBOSE) != 0) {
219678064Sume					printf("path MTU for %s is notified. "
219778064Sume					       "(ignored)\n",
219878064Sume					   pr_addr((struct sockaddr *)&mtuctl->ip6m_addr,
219978064Sume					   sizeof(mtuctl->ip6m_addr)));
220078064Sume				}
220178064Sume				return(0);
220278064Sume			}
220378064Sume
220478064Sume			/*
220578064Sume			 * Ignore an invalid MTU. XXX: can we just believe
220678064Sume			 * the kernel check?
220778064Sume			 */
220878064Sume			if (mtuctl->ip6m_mtu < IPV6_MMTU)
220978064Sume				return(0);
221078064Sume
221178064Sume			/* notification for our destination. return the MTU. */
221278064Sume			return((int)mtuctl->ip6m_mtu);
221378064Sume		}
221478064Sume	}
221578064Sume#endif
221678064Sume	return(0);
221778064Sume}
221878064Sume
221955163Sshin/*
222055163Sshin * tvsub --
222155163Sshin *	Subtract 2 timeval structs:  out = out - in.  Out is assumed to
222255163Sshin * be >= in.
222355163Sshin */
222455163Sshinvoid
2225216561Scharniertvsub(struct timeval *out, struct timeval *in)
222655163Sshin{
222755163Sshin	if ((out->tv_usec -= in->tv_usec) < 0) {
222855163Sshin		--out->tv_sec;
222955163Sshin		out->tv_usec += 1000000;
223055163Sshin	}
223155163Sshin	out->tv_sec -= in->tv_sec;
223255163Sshin}
223355163Sshin
223455163Sshin/*
223555163Sshin * onint --
223655163Sshin *	SIGINT handler.
223755163Sshin */
223862627Skris/* ARGSUSED */
223955163Sshinvoid
2240216561Scharnieronint(int notused __unused)
224155163Sshin{
224255163Sshin	summary();
224355163Sshin
2244209236Sbrucec	if (res != NULL)
2245209236Sbrucec		freeaddrinfo(res);
2246209236Sbrucec
2247209236Sbrucec        if(packet != NULL)
2248209236Sbrucec                free(packet);
2249209236Sbrucec
2250209236Sbrucec#ifndef HAVE_POLL_H
2251209236Sbrucec        if(fdmaskp != NULL)
2252209236Sbrucec                free(fdmaskp);
2253209236Sbrucec#endif
2254209236Sbrucec
2255121472Sume	(void)signal(SIGINT, SIG_DFL);
2256121472Sume	(void)kill(getpid(), SIGINT);
2257121472Sume
2258121472Sume	/* NOTREACHED */
2259121472Sume	exit(1);
226055163Sshin}
226155163Sshin
226255163Sshin/*
226355163Sshin * summary --
226455163Sshin *	Print out statistics.
226555163Sshin */
226655163Sshinvoid
2267216561Scharniersummary(void)
226855163Sshin{
226955163Sshin
227055163Sshin	(void)printf("\n--- %s ping6 statistics ---\n", hostname);
227155163Sshin	(void)printf("%ld packets transmitted, ", ntransmitted);
227255163Sshin	(void)printf("%ld packets received, ", nreceived);
227355163Sshin	if (nrepeats)
227455163Sshin		(void)printf("+%ld duplicates, ", nrepeats);
227555163Sshin	if (ntransmitted) {
227655163Sshin		if (nreceived > ntransmitted)
2277121472Sume			(void)printf("-- somebody's duplicating packets!");
227855163Sshin		else
2279121472Sume			(void)printf("%.1f%% packet loss",
2280121472Sume			    ((((double)ntransmitted - nreceived) * 100.0) /
228155163Sshin			    ntransmitted));
228255163Sshin	}
228355163Sshin	(void)putchar('\n');
228455163Sshin	if (nreceived && timing) {
228555163Sshin		/* Only display average to microseconds */
228678064Sume		double num = nreceived + nrepeats;
228778064Sume		double avg = tsum / num;
228878064Sume		double dev = sqrt(tsumsq / num - avg * avg);
228978064Sume		(void)printf(
229078064Sume		    "round-trip min/avg/max/std-dev = %.3f/%.3f/%.3f/%.3f ms\n",
229178064Sume		    tmin, avg, tmax, dev);
229255163Sshin		(void)fflush(stdout);
229355163Sshin	}
229478064Sume	(void)fflush(stdout);
229555163Sshin}
229655163Sshin
229778064Sume/*subject type*/
2298121472Sumestatic const char *niqcode[] = {
229978064Sume	"IPv6 address",
230078064Sume	"DNS label",	/*or empty*/
230178064Sume	"IPv4 address",
230255163Sshin};
230355163Sshin
230478064Sume/*result code*/
2305121472Sumestatic const char *nircode[] = {
230678064Sume	"Success", "Refused", "Unknown",
230778064Sume};
230878064Sume
230978064Sume
231055163Sshin/*
231155163Sshin * pr_icmph --
231255163Sshin *	Print a descriptive string about an ICMP header.
231355163Sshin */
231455163Sshinvoid
2315216561Scharnierpr_icmph(struct icmp6_hdr *icp, u_char *end)
231655163Sshin{
231762627Skris	char ntop_buf[INET6_ADDRSTRLEN];
231878064Sume	struct nd_redirect *red;
231978064Sume	struct icmp6_nodeinfo *ni;
232078064Sume	char dnsname[MAXDNAME + 1];
232178064Sume	const u_char *cp;
232278064Sume	size_t l;
232362627Skris
232478064Sume	switch (icp->icmp6_type) {
232555163Sshin	case ICMP6_DST_UNREACH:
232678064Sume		switch (icp->icmp6_code) {
232755163Sshin		case ICMP6_DST_UNREACH_NOROUTE:
232855163Sshin			(void)printf("No Route to Destination\n");
232955163Sshin			break;
233055163Sshin		case ICMP6_DST_UNREACH_ADMIN:
233155163Sshin			(void)printf("Destination Administratively "
233278064Sume			    "Unreachable\n");
233355163Sshin			break;
233455163Sshin		case ICMP6_DST_UNREACH_BEYONDSCOPE:
233555163Sshin			(void)printf("Destination Unreachable Beyond Scope\n");
233655163Sshin			break;
233755163Sshin		case ICMP6_DST_UNREACH_ADDR:
233855163Sshin			(void)printf("Destination Host Unreachable\n");
233955163Sshin			break;
234055163Sshin		case ICMP6_DST_UNREACH_NOPORT:
234155163Sshin			(void)printf("Destination Port Unreachable\n");
234255163Sshin			break;
234355163Sshin		default:
234455163Sshin			(void)printf("Destination Unreachable, Bad Code: %d\n",
234555163Sshin			    icp->icmp6_code);
234655163Sshin			break;
234755163Sshin		}
234855163Sshin		/* Print returned IP header information */
234955163Sshin		pr_retip((struct ip6_hdr *)(icp + 1), end);
235055163Sshin		break;
235155163Sshin	case ICMP6_PACKET_TOO_BIG:
235255163Sshin		(void)printf("Packet too big mtu = %d\n",
235378064Sume		    (int)ntohl(icp->icmp6_mtu));
235462627Skris		pr_retip((struct ip6_hdr *)(icp + 1), end);
235555163Sshin		break;
235655163Sshin	case ICMP6_TIME_EXCEEDED:
235778064Sume		switch (icp->icmp6_code) {
235855163Sshin		case ICMP6_TIME_EXCEED_TRANSIT:
235955163Sshin			(void)printf("Time to live exceeded\n");
236055163Sshin			break;
236155163Sshin		case ICMP6_TIME_EXCEED_REASSEMBLY:
236255163Sshin			(void)printf("Frag reassembly time exceeded\n");
236355163Sshin			break;
236455163Sshin		default:
236555163Sshin			(void)printf("Time exceeded, Bad Code: %d\n",
236655163Sshin			    icp->icmp6_code);
236755163Sshin			break;
236855163Sshin		}
236955163Sshin		pr_retip((struct ip6_hdr *)(icp + 1), end);
237055163Sshin		break;
237155163Sshin	case ICMP6_PARAM_PROB:
237255163Sshin		(void)printf("Parameter problem: ");
237378064Sume		switch (icp->icmp6_code) {
237478064Sume		case ICMP6_PARAMPROB_HEADER:
237578064Sume			(void)printf("Erroneous Header ");
237678064Sume			break;
237778064Sume		case ICMP6_PARAMPROB_NEXTHEADER:
237878064Sume			(void)printf("Unknown Nextheader ");
237978064Sume			break;
238078064Sume		case ICMP6_PARAMPROB_OPTION:
238178064Sume			(void)printf("Unrecognized Option ");
238278064Sume			break;
238378064Sume		default:
238478064Sume			(void)printf("Bad code(%d) ", icp->icmp6_code);
238578064Sume			break;
238655163Sshin		}
238755163Sshin		(void)printf("pointer = 0x%02x\n",
238878064Sume		    (u_int32_t)ntohl(icp->icmp6_pptr));
238955163Sshin		pr_retip((struct ip6_hdr *)(icp + 1), end);
239055163Sshin		break;
239155163Sshin	case ICMP6_ECHO_REQUEST:
239262627Skris		(void)printf("Echo Request");
239355163Sshin		/* XXX ID + Seq + Data */
239455163Sshin		break;
239555163Sshin	case ICMP6_ECHO_REPLY:
239662627Skris		(void)printf("Echo Reply");
239755163Sshin		/* XXX ID + Seq + Data */
239855163Sshin		break;
239955163Sshin	case ICMP6_MEMBERSHIP_QUERY:
240062627Skris		(void)printf("Listener Query");
240155163Sshin		break;
240255163Sshin	case ICMP6_MEMBERSHIP_REPORT:
240362627Skris		(void)printf("Listener Report");
240455163Sshin		break;
240555163Sshin	case ICMP6_MEMBERSHIP_REDUCTION:
240662627Skris		(void)printf("Listener Done");
240755163Sshin		break;
240855163Sshin	case ND_ROUTER_SOLICIT:
240962627Skris		(void)printf("Router Solicitation");
241055163Sshin		break;
241155163Sshin	case ND_ROUTER_ADVERT:
241262627Skris		(void)printf("Router Advertisement");
241355163Sshin		break;
241455163Sshin	case ND_NEIGHBOR_SOLICIT:
241562627Skris		(void)printf("Neighbor Solicitation");
241655163Sshin		break;
241755163Sshin	case ND_NEIGHBOR_ADVERT:
241862627Skris		(void)printf("Neighbor Advertisement");
241955163Sshin		break;
242055163Sshin	case ND_REDIRECT:
242178064Sume		red = (struct nd_redirect *)icp;
242255163Sshin		(void)printf("Redirect\n");
242378064Sume		if (!inet_ntop(AF_INET6, &red->nd_rd_dst, ntop_buf,
242478064Sume		    sizeof(ntop_buf)))
2425121472Sume			strlcpy(ntop_buf, "?", sizeof(ntop_buf));
242678064Sume		(void)printf("Destination: %s", ntop_buf);
242778064Sume		if (!inet_ntop(AF_INET6, &red->nd_rd_target, ntop_buf,
242878064Sume		    sizeof(ntop_buf)))
2429121472Sume			strlcpy(ntop_buf, "?", sizeof(ntop_buf));
243078064Sume		(void)printf(" New Target: %s", ntop_buf);
243155163Sshin		break;
243255163Sshin	case ICMP6_NI_QUERY:
243362627Skris		(void)printf("Node Information Query");
243455163Sshin		/* XXX ID + Seq + Data */
243578064Sume		ni = (struct icmp6_nodeinfo *)icp;
243678064Sume		l = end - (u_char *)(ni + 1);
243778064Sume		printf(", ");
243878064Sume		switch (ntohs(ni->ni_qtype)) {
243978064Sume		case NI_QTYPE_NOOP:
244078064Sume			(void)printf("NOOP");
244178064Sume			break;
244278064Sume		case NI_QTYPE_SUPTYPES:
244378064Sume			(void)printf("Supported qtypes");
244478064Sume			break;
244578064Sume		case NI_QTYPE_FQDN:
244678064Sume			(void)printf("DNS name");
244778064Sume			break;
244878064Sume		case NI_QTYPE_NODEADDR:
244978064Sume			(void)printf("nodeaddr");
245078064Sume			break;
245178064Sume		case NI_QTYPE_IPV4ADDR:
245278064Sume			(void)printf("IPv4 nodeaddr");
245378064Sume			break;
245478064Sume		default:
245578064Sume			(void)printf("unknown qtype");
245678064Sume			break;
245778064Sume		}
245878064Sume		if (options & F_VERBOSE) {
245978064Sume			switch (ni->ni_code) {
246078064Sume			case ICMP6_NI_SUBJ_IPV6:
246178064Sume				if (l == sizeof(struct in6_addr) &&
246278064Sume				    inet_ntop(AF_INET6, ni + 1, ntop_buf,
246378064Sume				    sizeof(ntop_buf)) != NULL) {
246478064Sume					(void)printf(", subject=%s(%s)",
246578064Sume					    niqcode[ni->ni_code], ntop_buf);
246678064Sume				} else {
246778064Sume#if 1
246878064Sume					/* backward compat to -W */
246978064Sume					(void)printf(", oldfqdn");
247078064Sume#else
247178064Sume					(void)printf(", invalid");
247278064Sume#endif
247378064Sume				}
247478064Sume				break;
247578064Sume			case ICMP6_NI_SUBJ_FQDN:
247678064Sume				if (end == (u_char *)(ni + 1)) {
247778064Sume					(void)printf(", no subject");
247878064Sume					break;
247978064Sume				}
248078064Sume				printf(", subject=%s", niqcode[ni->ni_code]);
248178064Sume				cp = (const u_char *)(ni + 1);
248278064Sume				if (dnsdecode(&cp, end, NULL, dnsname,
248378064Sume				    sizeof(dnsname)) != NULL)
248478064Sume					printf("(%s)", dnsname);
248578064Sume				else
248678064Sume					printf("(invalid)");
248778064Sume				break;
248878064Sume			case ICMP6_NI_SUBJ_IPV4:
248978064Sume				if (l == sizeof(struct in_addr) &&
249078064Sume				    inet_ntop(AF_INET, ni + 1, ntop_buf,
249178064Sume				    sizeof(ntop_buf)) != NULL) {
249278064Sume					(void)printf(", subject=%s(%s)",
249378064Sume					    niqcode[ni->ni_code], ntop_buf);
249478064Sume				} else
249578064Sume					(void)printf(", invalid");
249678064Sume				break;
249778064Sume			default:
249878064Sume				(void)printf(", invalid");
249978064Sume				break;
250078064Sume			}
250178064Sume		}
250255163Sshin		break;
250355163Sshin	case ICMP6_NI_REPLY:
250462627Skris		(void)printf("Node Information Reply");
250555163Sshin		/* XXX ID + Seq + Data */
250678064Sume		ni = (struct icmp6_nodeinfo *)icp;
250778064Sume		printf(", ");
250878064Sume		switch (ntohs(ni->ni_qtype)) {
250978064Sume		case NI_QTYPE_NOOP:
251078064Sume			(void)printf("NOOP");
251178064Sume			break;
251278064Sume		case NI_QTYPE_SUPTYPES:
251378064Sume			(void)printf("Supported qtypes");
251478064Sume			break;
251578064Sume		case NI_QTYPE_FQDN:
251678064Sume			(void)printf("DNS name");
251778064Sume			break;
251878064Sume		case NI_QTYPE_NODEADDR:
251978064Sume			(void)printf("nodeaddr");
252078064Sume			break;
252178064Sume		case NI_QTYPE_IPV4ADDR:
252278064Sume			(void)printf("IPv4 nodeaddr");
252378064Sume			break;
252478064Sume		default:
252578064Sume			(void)printf("unknown qtype");
252678064Sume			break;
252778064Sume		}
252878064Sume		if (options & F_VERBOSE) {
252978064Sume			if (ni->ni_code > sizeof(nircode) / sizeof(nircode[0]))
253078064Sume				printf(", invalid");
253178064Sume			else
253278064Sume				printf(", %s", nircode[ni->ni_code]);
253378064Sume		}
253455163Sshin		break;
253555163Sshin	default:
253662627Skris		(void)printf("Bad ICMP type: %d", icp->icmp6_type);
253755163Sshin	}
253855163Sshin}
253955163Sshin
254055163Sshin/*
254155163Sshin * pr_iph --
254255163Sshin *	Print an IP6 header.
254355163Sshin */
254455163Sshinvoid
2545216561Scharnierpr_iph(struct ip6_hdr *ip6)
254655163Sshin{
254755163Sshin	u_int32_t flow = ip6->ip6_flow & IPV6_FLOWLABEL_MASK;
254855163Sshin	u_int8_t tc;
254962627Skris	char ntop_buf[INET6_ADDRSTRLEN];
255055163Sshin
255155163Sshin	tc = *(&ip6->ip6_vfc + 1); /* XXX */
255255163Sshin	tc = (tc >> 4) & 0x0f;
255355163Sshin	tc |= (ip6->ip6_vfc << 4);
255455163Sshin
255555163Sshin	printf("Vr TC  Flow Plen Nxt Hlim\n");
255655163Sshin	printf(" %1x %02x %05x %04x  %02x   %02x\n",
255778064Sume	    (ip6->ip6_vfc & IPV6_VERSION_MASK) >> 4, tc, (u_int32_t)ntohl(flow),
255878064Sume	    ntohs(ip6->ip6_plen), ip6->ip6_nxt, ip6->ip6_hlim);
255978064Sume	if (!inet_ntop(AF_INET6, &ip6->ip6_src, ntop_buf, sizeof(ntop_buf)))
2560121472Sume		strlcpy(ntop_buf, "?", sizeof(ntop_buf));
256178064Sume	printf("%s->", ntop_buf);
256278064Sume	if (!inet_ntop(AF_INET6, &ip6->ip6_dst, ntop_buf, sizeof(ntop_buf)))
2563121472Sume		strlcpy(ntop_buf, "?", sizeof(ntop_buf));
256478064Sume	printf("%s\n", ntop_buf);
256555163Sshin}
256655163Sshin
256755163Sshin/*
256855163Sshin * pr_addr --
256955163Sshin *	Return an ascii host address as a dotted quad and optionally with
257055163Sshin * a hostname.
257155163Sshin */
257262627Skrisconst char *
2573216561Scharnierpr_addr(struct sockaddr *addr, int addrlen)
257455163Sshin{
257578064Sume	static char buf[NI_MAXHOST];
2576121316Sume	int flag = 0;
257755163Sshin
257862627Skris	if ((options & F_HOSTNAME) == 0)
257955163Sshin		flag |= NI_NUMERICHOST;
258055163Sshin
258178064Sume	if (getnameinfo(addr, addrlen, buf, sizeof(buf), NULL, 0, flag) == 0)
258278064Sume		return (buf);
258378064Sume	else
258478064Sume		return "?";
258555163Sshin}
258655163Sshin
258755163Sshin/*
258855163Sshin * pr_retip --
258955163Sshin *	Dump some info on a returned (via ICMPv6) IPv6 packet.
259055163Sshin */
259155163Sshinvoid
2592216561Scharnierpr_retip(struct ip6_hdr *ip6, u_char *end)
259355163Sshin{
259455163Sshin	u_char *cp = (u_char *)ip6, nh;
259555163Sshin	int hlen;
259655163Sshin
259755163Sshin	if (end - (u_char *)ip6 < sizeof(*ip6)) {
259855163Sshin		printf("IP6");
259955163Sshin		goto trunc;
260055163Sshin	}
260155163Sshin	pr_iph(ip6);
260255163Sshin	hlen = sizeof(*ip6);
260355163Sshin
260455163Sshin	nh = ip6->ip6_nxt;
260555163Sshin	cp += hlen;
260655163Sshin	while (end - cp >= 8) {
260755163Sshin		switch (nh) {
260878064Sume		case IPPROTO_HOPOPTS:
260978064Sume			printf("HBH ");
261078064Sume			hlen = (((struct ip6_hbh *)cp)->ip6h_len+1) << 3;
261178064Sume			nh = ((struct ip6_hbh *)cp)->ip6h_nxt;
261278064Sume			break;
261378064Sume		case IPPROTO_DSTOPTS:
261478064Sume			printf("DSTOPT ");
261578064Sume			hlen = (((struct ip6_dest *)cp)->ip6d_len+1) << 3;
261678064Sume			nh = ((struct ip6_dest *)cp)->ip6d_nxt;
261778064Sume			break;
261878064Sume		case IPPROTO_FRAGMENT:
261978064Sume			printf("FRAG ");
262078064Sume			hlen = sizeof(struct ip6_frag);
262178064Sume			nh = ((struct ip6_frag *)cp)->ip6f_nxt;
262278064Sume			break;
262378064Sume		case IPPROTO_ROUTING:
262478064Sume			printf("RTHDR ");
262578064Sume			hlen = (((struct ip6_rthdr *)cp)->ip6r_len+1) << 3;
262678064Sume			nh = ((struct ip6_rthdr *)cp)->ip6r_nxt;
262778064Sume			break;
262855163Sshin#ifdef IPSEC
262978064Sume		case IPPROTO_AH:
263078064Sume			printf("AH ");
263178064Sume			hlen = (((struct ah *)cp)->ah_len+2) << 2;
263278064Sume			nh = ((struct ah *)cp)->ah_nxt;
263378064Sume			break;
263455163Sshin#endif
263578064Sume		case IPPROTO_ICMPV6:
263678064Sume			printf("ICMP6: type = %d, code = %d\n",
263778064Sume			    *cp, *(cp + 1));
263878064Sume			return;
263978064Sume		case IPPROTO_ESP:
264078064Sume			printf("ESP\n");
264178064Sume			return;
264278064Sume		case IPPROTO_TCP:
264378064Sume			printf("TCP: from port %u, to port %u (decimal)\n",
264478064Sume			    (*cp * 256 + *(cp + 1)),
264578064Sume			    (*(cp + 2) * 256 + *(cp + 3)));
264678064Sume			return;
264778064Sume		case IPPROTO_UDP:
264878064Sume			printf("UDP: from port %u, to port %u (decimal)\n",
264978064Sume			    (*cp * 256 + *(cp + 1)),
265078064Sume			    (*(cp + 2) * 256 + *(cp + 3)));
265178064Sume			return;
265278064Sume		default:
265378064Sume			printf("Unknown Header(%d)\n", nh);
265478064Sume			return;
265555163Sshin		}
265655163Sshin
265755163Sshin		if ((cp += hlen) >= end)
265855163Sshin			goto trunc;
265955163Sshin	}
266055163Sshin	if (end - cp < 8)
266155163Sshin		goto trunc;
266255163Sshin
266355163Sshin	putchar('\n');
266455163Sshin	return;
266555163Sshin
266655163Sshin  trunc:
266755163Sshin	printf("...\n");
266855163Sshin	return;
266955163Sshin}
267055163Sshin
267155163Sshinvoid
2672216561Scharnierfill(char *bp, char *patp)
267355163Sshin{
267492806Sobrien	int ii, jj, kk;
267555163Sshin	int pat[16];
267655163Sshin	char *cp;
267755163Sshin
267855163Sshin	for (cp = patp; *cp; cp++)
267955163Sshin		if (!isxdigit(*cp))
268055163Sshin			errx(1, "patterns must be specified as hex digits");
268155163Sshin	ii = sscanf(patp,
268255163Sshin	    "%2x%2x%2x%2x%2x%2x%2x%2x%2x%2x%2x%2x%2x%2x%2x%2x",
268355163Sshin	    &pat[0], &pat[1], &pat[2], &pat[3], &pat[4], &pat[5], &pat[6],
268455163Sshin	    &pat[7], &pat[8], &pat[9], &pat[10], &pat[11], &pat[12],
268555163Sshin	    &pat[13], &pat[14], &pat[15]);
268655163Sshin
268778064Sume/* xxx */
268855163Sshin	if (ii > 0)
268955163Sshin		for (kk = 0;
2690121472Sume		    kk <= MAXDATALEN - (8 + sizeof(struct tv32) + ii);
269155163Sshin		    kk += ii)
269255163Sshin			for (jj = 0; jj < ii; ++jj)
269355163Sshin				bp[jj + kk] = pat[jj];
269455163Sshin	if (!(options & F_QUIET)) {
269555163Sshin		(void)printf("PATTERN: 0x");
269655163Sshin		for (jj = 0; jj < ii; ++jj)
269755163Sshin			(void)printf("%02x", bp[jj] & 0xFF);
269855163Sshin		(void)printf("\n");
269955163Sshin	}
270055163Sshin}
270155163Sshin
270255163Sshin#ifdef IPSEC
270355163Sshin#ifdef IPSEC_POLICY_IPSEC
270455163Sshinint
2705216561Scharniersetpolicy(int so __unused, char *policy)
270655163Sshin{
270755163Sshin	char *buf;
270855163Sshin
270955163Sshin	if (policy == NULL)
271055163Sshin		return 0;	/* ignore */
271155163Sshin
271255163Sshin	buf = ipsec_set_policy(policy, strlen(policy));
271355163Sshin	if (buf == NULL)
271464277Skris		errx(1, "%s", ipsec_strerror());
271578064Sume	if (setsockopt(s, IPPROTO_IPV6, IPV6_IPSEC_POLICY, buf,
271678064Sume	    ipsec_get_policylen(buf)) < 0)
2717121472Sume		warnx("Unable to set IPsec policy");
271855163Sshin	free(buf);
271955163Sshin
272055163Sshin	return 0;
272155163Sshin}
272255163Sshin#endif
272355163Sshin#endif
272455163Sshin
272562627Skrischar *
2726252021Shrsnigroup(char *name, int nig_oldmcprefix)
272762627Skris{
272862627Skris	char *p;
2729121472Sume	char *q;
273062627Skris	MD5_CTX ctxt;
273162627Skris	u_int8_t digest[16];
273278064Sume	u_int8_t c;
273378064Sume	size_t l;
273462627Skris	char hbuf[NI_MAXHOST];
273562627Skris	struct in6_addr in6;
2736252021Shrs	int valid;
273762627Skris
273878064Sume	p = strchr(name, '.');
273978064Sume	if (!p)
274078064Sume		p = name + strlen(name);
274178064Sume	l = p - name;
274278064Sume	if (l > 63 || l > sizeof(hbuf) - 1)
274362627Skris		return NULL;	/*label too long*/
274478064Sume	strncpy(hbuf, name, l);
274578064Sume	hbuf[(int)l] = '\0';
274662627Skris
274778064Sume	for (q = name; *q; q++) {
2748121472Sume		if (isupper(*(unsigned char *)q))
2749121472Sume			*q = tolower(*(unsigned char *)q);
275078064Sume	}
275178064Sume
2752252021Shrs	/* generate 16 bytes of pseudo-random value. */
2753121472Sume	memset(&ctxt, 0, sizeof(ctxt));
275462627Skris	MD5Init(&ctxt);
275578064Sume	c = l & 0xff;
275678064Sume	MD5Update(&ctxt, &c, sizeof(c));
2757121472Sume	MD5Update(&ctxt, (unsigned char *)name, l);
275862627Skris	MD5Final(digest, &ctxt);
275962627Skris
2760252021Shrs	if (nig_oldmcprefix) {
2761252021Shrs		/* draft-ietf-ipngwg-icmp-name-lookup */
2762252021Shrs		valid = inet_pton(AF_INET6, "ff02::2:0000:0000", &in6);
2763252021Shrs	} else {
2764252021Shrs		/* RFC 4620 */
2765252021Shrs		valid = inet_pton(AF_INET6, "ff02::2:ff00:0000", &in6);
2766252021Shrs	}
2767252021Shrs	if (valid != 1)
276878064Sume		return NULL;	/*XXX*/
2769252021Shrs
2770252021Shrs	if (nig_oldmcprefix) {
2771252021Shrs		/* draft-ietf-ipngwg-icmp-name-lookup */
2772252021Shrs		bcopy(digest, &in6.s6_addr[12], 4);
2773252021Shrs	} else {
2774252021Shrs		/* RFC 4620 */
2775252021Shrs		bcopy(digest, &in6.s6_addr[13], 3);
2776252021Shrs	}
277762627Skris
277862627Skris	if (inet_ntop(AF_INET6, &in6, hbuf, sizeof(hbuf)) == NULL)
277962627Skris		return NULL;
278062627Skris
278162627Skris	return strdup(hbuf);
278262627Skris}
278362627Skris
278455163Sshinvoid
2785216561Scharnierusage(void)
278655163Sshin{
278755163Sshin	(void)fprintf(stderr,
2788141611Sru#if defined(IPSEC) && !defined(IPSEC_POLICY_IPSEC)
2789141611Sru	    "A"
2790141611Sru#endif
2791141611Sru	    "usage: ping6 [-"
2792206889Smaxim	    "Dd"
2793141611Sru#if defined(IPSEC) && !defined(IPSEC_POLICY_IPSEC)
2794141611Sru	    "E"
2795141611Sru#endif
2796141611Sru	    "fH"
279778990Sume#ifdef IPV6_USE_MIN_MTU
279878990Sume	    "m"
279978990Sume#endif
2800182276Smatteo	    "nNoqrRtvwW] "
2801141611Sru	    "[-a addrtype] [-b bufsiz] [-c count] [-g gateway]\n"
2802141611Sru	    "             [-h hoplimit] [-I interface] [-i wait] [-l preload]"
2803141611Sru#if defined(IPSEC) && defined(IPSEC_POLICY_IPSEC)
2804141611Sru	    " [-P policy]"
280555163Sshin#endif
2806141611Sru	    "\n"
2807141611Sru	    "             [-p pattern] [-S sourceaddr] [-s packetsize] "
2808141611Sru	    "[hops ...] host\n");
280955163Sshin	exit(1);
281055163Sshin}
2811