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