root/sbin/ping/ping.c
/*      $OpenBSD: ping.c,v 1.251 2025/12/06 10:41:07 phessler Exp $     */

/*
 * Copyright (C) 1995, 1996, 1997, and 1998 WIDE Project.
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 * 1. Redistributions of source code must retain the above copyright
 *    notice, this list of conditions and the following disclaimer.
 * 2. Redistributions in binary form must reproduce the above copyright
 *    notice, this list of conditions and the following disclaimer in the
 *    documentation and/or other materials provided with the distribution.
 * 3. Neither the name of the project nor the names of its contributors
 *    may be used to endorse or promote products derived from this software
 *    without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE PROJECT AND CONTRIBUTORS ``AS IS'' AND
 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
 * ARE DISCLAIMED.  IN NO EVENT SHALL THE PROJECT OR CONTRIBUTORS BE LIABLE
 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
 * SUCH DAMAGE.
 */

/*
 * Copyright (c) 1989, 1993
 *      The Regents of the University of California.  All rights reserved.
 *
 * This code is derived from software contributed to Berkeley by
 * Mike Muuss.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 * 1. Redistributions of source code must retain the above copyright
 *    notice, this list of conditions and the following disclaimer.
 * 2. Redistributions in binary form must reproduce the above copyright
 *    notice, this list of conditions and the following disclaimer in the
 *    documentation and/or other materials provided with the distribution.
 * 3. Neither the name of the University nor the names of its contributors
 *    may be used to endorse or promote products derived from this software
 *    without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
 * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
 * SUCH DAMAGE.
 */

/*
 * Using the InterNet Control Message Protocol (ICMP) "ECHO" facility,
 * measure round-trip-delays and packet loss across network paths.
 *
 * Author -
 *      Mike Muuss
 *      U. S. Army Ballistic Research Laboratory
 *      December, 1983
 *
 * Status -
 *      Public Domain.  Distribution Unlimited.
 * Bugs -
 *      More statistics could always be gathered.
 *      This program has to run SUID to ROOT to access the ICMP socket.
 */

#include <sys/types.h>
#include <sys/socket.h>
#include <sys/time.h>
#include <sys/uio.h>

#include <netinet/in.h>
#include <netinet/ip.h>
#include <netinet/ip_icmp.h>
#include <netinet/ip_var.h>
#include <netinet/ip6.h>
#include <netinet/icmp6.h>
#include <netinet/ip_ah.h>
#include <arpa/inet.h>
#include <netdb.h>

#include <ctype.h>
#include <err.h>
#include <errno.h>
#include <limits.h>
#include <math.h>
#include <poll.h>
#include <pwd.h>
#include <signal.h>
#include <siphash.h>
#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <time.h>
#include <unistd.h>

struct tv64 {
        u_int64_t       tv64_sec;
        u_int64_t       tv64_nsec;
};

struct payload {
        struct tv64     tv64;
        u_int8_t        mac[SIPHASH_DIGEST_LENGTH];
};

#define ECHOLEN         8       /* icmp echo header len excluding time */
#define ECHOTMLEN       sizeof(struct payload)
#define DEFDATALEN      (64 - ECHOLEN)          /* default data length */
#define MAXIPLEN        60
#define MAXICMPLEN      76
#define MAXPAYLOAD      (IP_MAXPACKET - MAXIPLEN - ECHOLEN)
#define IP6LEN          40
#define EXTRA           256     /* for AH and various other headers. weird. */
#define MAXPAYLOAD6     IPV6_MAXPACKET - IP6LEN - ECHOLEN
#define MAXWAIT_DEFAULT 10                      /* secs to wait for response */
#define NROUTES         9                       /* number of record route slots */

#define A(bit)          rcvd_tbl[(bit)>>3]      /* identify byte in array */
#define B(bit)          (1 << ((bit) & 0x07))   /* identify bit in byte */
#define SET(bit)        (A(bit) |= B(bit))
#define CLR(bit)        (A(bit) &= (~B(bit)))
#define TST(bit)        (A(bit) & B(bit))

/* various options */
int options;
#define F_FLOOD         0x0001
#define F_INTERVAL      0x0002
#define F_HOSTNAME      0x0004
#define F_PINGFILLED    0x0008
#define F_QUIET         0x0010
#define F_RROUTE        0x0020
#define F_SO_DEBUG      0x0040
#define F_SHOWCHAR      0x0080
#define F_VERBOSE       0x0100
/*                      0x0200 */
#define F_HDRINCL       0x0400
#define F_TTL           0x0800
#define F_TOS           0x1000
#define F_AUD_RECV      0x2000
#define F_AUD_MISS      0x4000

/* multicast options */
int moptions;
#define MULTICAST_NOLOOP        0x001
#define MULTICAST_TTL           0x002

#define DUMMY_PORT      10101
#define PING_USER       "_ping"

/*
 * MAX_DUP_CHK is the number of bits in received table, i.e. the maximum
 * number of received sequence numbers we can keep track of.  Change 128
 * to 8192 for complete accuracy...
 */
#define MAX_DUP_CHK     (8 * 8192)
int mx_dup_ck = MAX_DUP_CHK;
char rcvd_tbl[MAX_DUP_CHK / 8];

int datalen = DEFDATALEN;
int maxpayload = MAXPAYLOAD;
u_char outpackhdr[IP_MAXPACKET+sizeof(struct ip)];
u_char *outpack = outpackhdr+sizeof(struct ip);
char BSPACE = '\b';             /* characters written for flood */
char DOT = '.';
char *hostname;
int ident;                      /* random number to identify our packets */
int v6flag;                     /* are we ping6? */

/* counters */
int64_t npackets;               /* max packets to transmit */
int64_t nreceived;              /* # of packets we got back */
int64_t nrepeats;               /* number of duplicates */
int64_t ntransmitted;           /* sequence # for outbound packets = #sent */
int64_t nmissedmax = 1;         /* max value of ntransmitted - nreceived - 1 */
struct timeval interval = {1, 0}; /* interval between packets */

/* timing */
int timing;                     /* flag to do timing */
int timinginfo;
unsigned int maxwait = MAXWAIT_DEFAULT; /* max seconds to wait for response */
double tmin = 999999999.0;      /* minimum round trip time */
double tmax;                    /* maximum round trip time */
double tsum;                    /* sum of all times, for doing average */
double tsumsq;                  /* sum of all times squared, for std. dev. */

struct tv64 tv64_offset;
SIPHASH_KEY mac_key;

struct msghdr smsghdr;
struct iovec smsgiov;

volatile sig_atomic_t seenalrm;
volatile sig_atomic_t seenint;
volatile sig_atomic_t seeninfo;

void                     fill(char *, char *);
void                     summary(void);
void                     onsignal(int);
void                     retransmit(int);
int                      pinger(int);
const char              *pr_addr(struct sockaddr *, socklen_t);
void                     pr_pack(u_char *, int, struct msghdr *);
__dead void              usage(void);

/* IPv4 specific functions */
void                     pr_ipopt(int, u_char *);
int                      in_cksum(u_short *, int);
void                     pr_icmph(struct icmp *);
void                     pr_retip(struct ip *);
void                     pr_iph(struct ip *);
#ifndef SMALL
int                      map_tos(char *, int *);
#endif  /* SMALL */

/* IPv6 specific functions */
int                      get_hoplim(struct msghdr *);
int                      get_pathmtu(struct msghdr *, struct sockaddr_in6 *);
void                     pr_icmph6(struct icmp6_hdr *, u_char *);
void                     pr_iph6(struct ip6_hdr *);
void                     pr_exthdrs(struct msghdr *);
void                     pr_ip6opt(void *);
void                     pr_rthdr(void *);
void                     pr_retip6(struct ip6_hdr *, u_char *);

int
main(int argc, char *argv[])
{
        struct addrinfo hints, *res;
        struct itimerval itimer;
        struct sockaddr *from, *dst;
        struct sockaddr_in from4, dst4;
        struct sockaddr_in6 from6, dst6;
        struct cmsghdr *scmsg = NULL;
        struct in6_pktinfo *pktinfo = NULL;
        struct icmp6_filter filt;
        struct passwd *pw;
        socklen_t maxsizelen;
        int64_t preload;
        int ch, i, optval = 1, packlen, maxsize, error, s, flooddone = 0;
        int df = 0, tos = 0, bufspace = IP_MAXPACKET, hoplimit = -1, mflag = 0;
        u_char *datap, *packet;
        u_char ttl = MAXTTL;
        char *e, *target, hbuf[NI_MAXHOST], *source = NULL;
        char rspace[3 + 4 * NROUTES + 1];       /* record route space */
        const char *errstr;
        double fraction, integral, seconds;
        uid_t ouid, uid;
        gid_t gid;
        u_int rtableid = 0;
        extern char *__progname;

        /* Cannot pledge due to special setsockopt()s below */
        if (unveil("/", "r") == -1)
                err(1, "unveil /");
        if (unveil(NULL, NULL) == -1)
                err(1, "unveil");

        if (strcmp("ping6", __progname) == 0) {
                v6flag = 1;
                maxpayload = MAXPAYLOAD6;
                if ((s = socket(AF_INET6, SOCK_RAW, IPPROTO_ICMPV6)) == -1)
                        err(1, "socket");
        } else {
                if ((s = socket(AF_INET, SOCK_RAW, IPPROTO_ICMP)) == -1)
                        err(1, "socket");
        }

        /* revoke privs */
        ouid = getuid();
        if (ouid == 0 && (pw = getpwnam(PING_USER)) != NULL) {
                uid = pw->pw_uid;
                gid = pw->pw_gid;
        } else {
                uid = getuid();
                gid = getgid();
        }
        if (ouid && (setgroups(1, &gid) ||
            setresgid(gid, gid, gid) ||
            setresuid(uid, uid, uid)))
                err(1, "unable to revoke privs");

        preload = 0;
        datap = &outpack[ECHOLEN + ECHOTMLEN];
        while ((ch = getopt(argc, argv, v6flag ?
            "c:DdEefgHh:I:i:Ll:mNnp:qS:s:T:V:vw:" :
            "DEI:LRS:c:defgHi:l:np:qs:T:t:V:vw:")) != -1) {
                switch(ch) {
                case 'c':
                        npackets = strtonum(optarg, 0, INT64_MAX, &errstr);
                        if (errstr)
                                errx(1,
                                    "number of packets to transmit is %s: %s",
                                    errstr, optarg);
                        break;
                case 'D':
                        options |= F_HDRINCL;
                        df = 1;
                        break;
                case 'd':
                        options |= F_SO_DEBUG;
                        break;
                case 'E':
                        options |= F_AUD_MISS;
                        break;
                case 'e':
                        options |= F_AUD_RECV;
                        break;
                case 'f':
                        if (ouid)
                                errc(1, EPERM, NULL);
                        options |= F_FLOOD;
                        setvbuf(stdout, NULL, _IONBF, 0);
                        break;
                case 'g':
                        options |= F_SHOWCHAR;
                        break;
                case 'H':
                        options |= F_HOSTNAME;
                        break;
                case 'h':               /* hoplimit */
                        hoplimit = strtonum(optarg, 0, IPV6_MAXHLIM, &errstr);
                        if (errstr)
                                errx(1, "hoplimit is %s: %s", errstr, optarg);
                        break;
                case 'I':
                case 'S':       /* deprecated */
                        source = optarg;
                        break;
                case 'i':               /* interval between packets */
                        seconds = strtod(optarg, &e);
                        if (*optarg == '\0' || *e != '\0' || seconds < 0.0)
                                errx(1, "interval is invalid: %s", optarg);
                        fraction = modf(seconds, &integral);
                        if (integral > UINT_MAX)
                                errx(1, "interval is too large: %s", optarg);
                        interval.tv_sec = integral;
                        interval.tv_usec = fraction * 1000000.0;
                        if (!timerisset(&interval))
                                errx(1, "interval is too small: %s", optarg);
                        if (interval.tv_sec < 1 && ouid != 0) {
                                errx(1, "only root may use an interval smaller"
                                    " than one second");
                        }
                        options |= F_INTERVAL;
                        break;
                case 'L':
                        moptions |= MULTICAST_NOLOOP;
                        break;
                case 'l':
                        if (ouid)
                                errc(1, EPERM, NULL);
                        preload = strtonum(optarg, 1, INT64_MAX, &errstr);
                        if (errstr)
                                errx(1, "preload value is %s: %s", errstr,
                                    optarg);
                        break;
                case 'm':
                        mflag++;
                        break;
                case 'n':
                        options &= ~F_HOSTNAME;
                        break;
                case 'p':               /* fill buffer with user pattern */
                        options |= F_PINGFILLED;
                        fill((char *)datap, optarg);
                        break;
                case 'q':
                        options |= F_QUIET;
                        break;
                case 'R':
                        options |= F_RROUTE;
                        break;
                case 's':               /* size of packet to send */
                        datalen = strtonum(optarg, 0, maxpayload, &errstr);
                        if (errstr)
                                errx(1, "packet size is %s: %s", errstr,
                                    optarg);
                        break;
#ifndef SMALL
                case 'T':
                        options |= F_HDRINCL;
                        options |= F_TOS;
                        errno = 0;
                        errstr = NULL;
                        if (map_tos(optarg, &tos))
                                break;
                        if (strlen(optarg) > 1 && optarg[0] == '0' &&
                            optarg[1] == 'x')
                                tos = (int)strtol(optarg, NULL, 16);
                        else
                                tos = strtonum(optarg, 0, 255, &errstr);
                        if (tos < 0 || tos > 255 || errstr || errno)
                                errx(1, "illegal tos value %s", optarg);
                        break;
#endif  /* SMALL */
                case 't':
                        options |= F_TTL;
                        ttl = strtonum(optarg, 0, MAXTTL, &errstr);
                        if (errstr)
                                errx(1, "ttl value is %s: %s", errstr, optarg);
                        break;
                case 'V':
                        rtableid = strtonum(optarg, 0, RT_TABLEID_MAX, &errstr);
                        if (errstr)
                                errx(1, "rtable value is %s: %s", errstr,
                                    optarg);
                        if (setsockopt(s, SOL_SOCKET, SO_RTABLE, &rtableid,
                            sizeof(rtableid)) == -1)
                                err(1, "setsockopt SO_RTABLE");
                        break;
                case 'v':
                        options |= F_VERBOSE;
                        break;
                case 'w':
                        maxwait = strtonum(optarg, 1, INT_MAX, &errstr);
                        if (errstr)
                                errx(1, "maxwait value is %s: %s",
                                    errstr, optarg);
                        break;
                default:
                        usage();
                }
        }

        if (ouid == 0 && (setgroups(1, &gid) ||
            setresgid(gid, gid, gid) ||
            setresuid(uid, uid, uid)))
                err(1, "unable to revoke privs");

        argc -= optind;
        argv += optind;

        if (argc != 1)
                usage();

        memset(&dst4, 0, sizeof(dst4));
        memset(&dst6, 0, sizeof(dst6));

        target = *argv;

        memset(&hints, 0, sizeof(hints));
        hints.ai_family = v6flag ? AF_INET6 : AF_INET;
        hints.ai_socktype = SOCK_RAW;
        hints.ai_protocol = 0;
        hints.ai_flags = AI_CANONNAME;
        if ((error = getaddrinfo(target, NULL, &hints, &res)))
                errx(1, "%s", gai_strerror(error));

        switch (res->ai_family) {
        case AF_INET:
                dst = (struct sockaddr *)&dst4;
                from = (struct sockaddr *)&from4;
                break;
        case AF_INET6:
                dst = (struct sockaddr *)&dst6;
                from = (struct sockaddr *)&from6;
                break;
        default:
                errx(1, "unsupported AF: %d", res->ai_family);
                break;
        }

        memcpy(dst, res->ai_addr, res->ai_addrlen);

        if (!hostname) {
                hostname = res->ai_canonname ? strdup(res->ai_canonname) :
                    target;
                if (!hostname)
                        err(1, "malloc");
        }

        if (res->ai_next) {
                if (getnameinfo(res->ai_addr, res->ai_addrlen, hbuf,
                    sizeof(hbuf), NULL, 0, NI_NUMERICHOST) != 0)
                        strlcpy(hbuf, "?", sizeof(hbuf));
                warnx("Warning: %s has multiple "
                    "addresses; using %s", hostname, hbuf);
        }
        freeaddrinfo(res);

        if (source) {
                memset(&hints, 0, sizeof(hints));
                hints.ai_family = dst->sa_family;
                if ((error = getaddrinfo(source, NULL, &hints, &res)))
                        errx(1, "%s: %s", source, gai_strerror(error));
                memcpy(from, res->ai_addr, res->ai_addrlen);
                freeaddrinfo(res);

                if (!v6flag && IN_MULTICAST(ntohl(dst4.sin_addr.s_addr))) {
                        if (setsockopt(s, IPPROTO_IP, IP_MULTICAST_IF,
                            &from4.sin_addr, sizeof(from4.sin_addr)) == -1)
                                err(1, "setsockopt IP_MULTICAST_IF");
                } else {
                        if (bind(s, from, from->sa_len) == -1)
                                err(1, "bind");
                }
        } else if (options & F_VERBOSE) {
                /*
                 * get the source address. XXX since we revoked the root
                 * privilege, we cannot use a raw socket for this.
                 */
                int dummy;
                socklen_t len = dst->sa_len;

                if ((dummy = socket(dst->sa_family, SOCK_DGRAM, 0)) == -1)
                        err(1, "UDP socket");

                memcpy(from, dst, dst->sa_len);
                if (v6flag) {
                        from6.sin6_port = ntohs(DUMMY_PORT);
                        if (pktinfo &&
                            setsockopt(dummy, IPPROTO_IPV6, IPV6_PKTINFO,
                            pktinfo, sizeof(*pktinfo)))
                                err(1, "UDP setsockopt(IPV6_PKTINFO)");

                        if (hoplimit != -1 &&
                            setsockopt(dummy, IPPROTO_IPV6, IPV6_UNICAST_HOPS,
                            &hoplimit, sizeof(hoplimit)))
                                err(1, "UDP setsockopt(IPV6_UNICAST_HOPS)");

                        if (hoplimit != -1 &&
                            setsockopt(dummy, IPPROTO_IPV6, IPV6_MULTICAST_HOPS,
                            &hoplimit, sizeof(hoplimit)))
                                err(1, "UDP setsockopt(IPV6_MULTICAST_HOPS)");
                } else {
                        u_char loop = 0;

                        from4.sin_port = ntohs(DUMMY_PORT);

                        if ((moptions & MULTICAST_NOLOOP) && setsockopt(dummy,
                            IPPROTO_IP, IP_MULTICAST_LOOP, &loop,
                            sizeof(loop)) == -1)
                                err(1, "setsockopt IP_MULTICAST_LOOP");
                        if ((moptions & MULTICAST_TTL) && setsockopt(dummy,
                            IPPROTO_IP, IP_MULTICAST_TTL, &ttl,
                            sizeof(ttl)) == -1)
                                err(1, "setsockopt IP_MULTICAST_TTL");
                }

                if (rtableid > 0 &&
                    setsockopt(dummy, SOL_SOCKET, SO_RTABLE, &rtableid,
                    sizeof(rtableid)) == -1)
                        err(1, "setsockopt(SO_RTABLE)");

                if (connect(dummy, from, len) == -1)
                        err(1, "UDP connect");

                if (getsockname(dummy, from, &len) == -1)
                        err(1, "getsockname");

                close(dummy);
        }

        if (options & F_SO_DEBUG)
                (void)setsockopt(s, SOL_SOCKET, SO_DEBUG, &optval,
                    sizeof(optval));

        if ((options & F_FLOOD) && (options & F_INTERVAL))
                errx(1, "-f and -i options are incompatible");

        if ((options & F_FLOOD) && (options & (F_AUD_RECV | F_AUD_MISS)))
                warnx("No audible output for flood pings");

        if (datalen >= sizeof(struct payload))  /* can we time transfer */
                timing = 1;

        if (v6flag) {
                /* in F_VERBOSE case, we may get non-echoreply packets*/
                if ((options & F_VERBOSE) && datalen < 2048) /* XXX 2048? */
                        packlen = 2048 + IP6LEN + ECHOLEN + EXTRA;
                else
                        packlen = datalen + IP6LEN + ECHOLEN + EXTRA;
        } else
                packlen = datalen + MAXIPLEN + MAXICMPLEN;
        if (!(packet = malloc(packlen)))
                err(1, "malloc");

        if (!(options & F_PINGFILLED))
                for (i = ECHOTMLEN; i < datalen; ++i)
                        *datap++ = i;

        ident = arc4random() & 0xFFFF;

        /*
         * When trying to send large packets, you must increase the
         * size of both the send and receive buffers...
         */
        maxsizelen = sizeof maxsize;
        if (getsockopt(s, SOL_SOCKET, SO_SNDBUF, &maxsize, &maxsizelen) == -1)
                err(1, "getsockopt");
        if (maxsize < packlen &&
            setsockopt(s, SOL_SOCKET, SO_SNDBUF, &packlen, sizeof(maxsize)) == -1)
                err(1, "setsockopt");

        /*
         * When pinging the broadcast address, you can get a lot of answers.
         * Doing something so evil is useful if you are trying to stress the
         * ethernet, or just want to fill the arp cache to get some stuff for
         * /etc/ethers.
         */
        while (setsockopt(s, SOL_SOCKET, SO_RCVBUF,
            &bufspace, sizeof(bufspace)) == -1) {
                if ((bufspace -= 1024) <= 0)
                        err(1, "Cannot set the receive buffer size");
        }
        if (bufspace < IP_MAXPACKET)
                warnx("Could only allocate a receive buffer of %d bytes "
                    "(default %d)", bufspace, IP_MAXPACKET);

        if (v6flag) {
                unsigned int loop = 0;

                /*
                 * let the kernel pass extension headers of incoming packets,
                 * for privileged socket options
                 */
                if (options & F_VERBOSE) {
                        int opton = 1;

                        if (setsockopt(s, IPPROTO_IPV6, IPV6_RECVHOPOPTS,
                            &opton, sizeof(opton)))
                                err(1, "setsockopt(IPV6_RECVHOPOPTS)");
                        if (setsockopt(s, IPPROTO_IPV6, IPV6_RECVDSTOPTS,
                            &opton, sizeof(opton)))
                                err(1, "setsockopt(IPV6_RECVDSTOPTS)");
                        if (setsockopt(s, IPPROTO_IPV6, IPV6_RECVRTHDR,
                            &opton, sizeof(opton)))
                                err(1, "setsockopt(IPV6_RECVRTHDR)");
                        ICMP6_FILTER_SETPASSALL(&filt);
                } else {
                        ICMP6_FILTER_SETBLOCKALL(&filt);
                        ICMP6_FILTER_SETPASS(ICMP6_ECHO_REPLY, &filt);
                }

                if ((moptions & MULTICAST_NOLOOP) &&
                    setsockopt(s, IPPROTO_IPV6, IPV6_MULTICAST_LOOP,
                    &loop, sizeof(loop)) == -1)
                        err(1, "setsockopt IPV6_MULTICAST_LOOP");

                optval = IPV6_DEFHLIM;
                if (IN6_IS_ADDR_MULTICAST(&dst6.sin6_addr)) {
                        if (setsockopt(s, IPPROTO_IPV6, IPV6_MULTICAST_HOPS,
                            &optval, sizeof(optval)) == -1)
                                err(1, "IPV6_MULTICAST_HOPS");
                }
                if (mflag != 1) {
                        optval = mflag > 1 ? 0 : 1;

                        if (setsockopt(s, IPPROTO_IPV6, IPV6_USE_MIN_MTU,
                            &optval, sizeof(optval)) == -1)
                                err(1, "setsockopt(IPV6_USE_MIN_MTU)");
                } else {
                        optval = 1;
                        if (setsockopt(s, IPPROTO_IPV6, IPV6_RECVPATHMTU,
                            &optval, sizeof(optval)) == -1)
                                err(1, "setsockopt(IPV6_RECVPATHMTU)");
                }

                if (setsockopt(s, IPPROTO_ICMPV6, ICMP6_FILTER,
                    &filt, sizeof(filt)) == -1)
                        err(1, "setsockopt(ICMP6_FILTER)");

                if (hoplimit != -1) {
                        /* set IP6 packet options */
                        if ((scmsg = malloc( CMSG_SPACE(sizeof(int)))) == NULL)
                                err(1, "malloc");
                        smsghdr.msg_control = (caddr_t)scmsg;
                        smsghdr.msg_controllen = CMSG_SPACE(sizeof(int));

                        scmsg->cmsg_len = CMSG_LEN(sizeof(int));
                        scmsg->cmsg_level = IPPROTO_IPV6;
                        scmsg->cmsg_type = IPV6_HOPLIMIT;
                        *(int *)(CMSG_DATA(scmsg)) = hoplimit;
                }

                if (options & F_TOS) {
                        optval = tos;
                        if (setsockopt(s, IPPROTO_IPV6, IPV6_TCLASS,
                            &optval, sizeof(optval)) == -1)
                                err(1, "setsockopt(IPV6_TCLASS)");
                }

                if (df) {
                        optval = 1;
                        if (setsockopt(s, IPPROTO_IPV6, IPV6_DONTFRAG,
                            &optval, sizeof(optval)) == -1)
                                err(1, "setsockopt(IPV6_DONTFRAG)");
                }

                optval = 1;
                if (setsockopt(s, IPPROTO_IPV6, IPV6_RECVPKTINFO,
                    &optval, sizeof(optval)) == -1)
                        err(1, "setsockopt(IPV6_RECVPKTINFO)");
                if (setsockopt(s, IPPROTO_IPV6, IPV6_RECVHOPLIMIT,
                    &optval, sizeof(optval)) == -1)
                        err(1, "setsockopt(IPV6_RECVHOPLIMIT)");
        } else {
                u_char loop = 0;

                if (options & F_TTL) {
                        if (IN_MULTICAST(ntohl(dst4.sin_addr.s_addr)))
                                moptions |= MULTICAST_TTL;
                        else
                                options |= F_HDRINCL;
                }

                if ((options & F_RROUTE) && (options & F_HDRINCL))
                        errx(1, "-R option and -D or -T, or -t to unicast"
                            " destinations are incompatible");

                if (options & F_HDRINCL) {
                        struct ip *ip = (struct ip *)outpackhdr;

                        if (setsockopt(s, IPPROTO_IP, IP_HDRINCL,
                            &optval, sizeof(optval)) == -1)
                                err(1, "setsockopt(IP_HDRINCL)");
                        ip->ip_v = IPVERSION;
                        ip->ip_hl = sizeof(struct ip) >> 2;
                        ip->ip_tos = tos;
                        ip->ip_id = 0;
                        ip->ip_off = htons(df ? IP_DF : 0);
                        ip->ip_ttl = ttl;
                        ip->ip_p = IPPROTO_ICMP;
                        if (source)
                                ip->ip_src = from4.sin_addr;
                        else
                                ip->ip_src.s_addr = INADDR_ANY;
                        ip->ip_dst = dst4.sin_addr;
                }

                /* record route option */
                if (options & F_RROUTE) {
                        if (IN_MULTICAST(ntohl(dst4.sin_addr.s_addr)))
                                errx(1, "record route not valid to multicast"
                                    " destinations");
                        memset(rspace, 0, sizeof(rspace));
                        rspace[IPOPT_OPTVAL] = IPOPT_RR;
                        rspace[IPOPT_OLEN] = sizeof(rspace)-1;
                        rspace[IPOPT_OFFSET] = IPOPT_MINOFF;
                        if (setsockopt(s, IPPROTO_IP, IP_OPTIONS,
                            rspace, sizeof(rspace)) == -1)
                                err(1, "record route");
                }

                if ((moptions & MULTICAST_NOLOOP) &&
                    setsockopt(s, IPPROTO_IP, IP_MULTICAST_LOOP,
                    &loop, sizeof(loop)) == -1)
                        err(1, "setsockopt IP_MULTICAST_LOOP");
                if ((moptions & MULTICAST_TTL) &&
                    setsockopt(s, IPPROTO_IP, IP_MULTICAST_TTL,
                    &ttl, sizeof(ttl)) == -1)
                        err(1, "setsockopt IP_MULTICAST_TTL");
        }

        if (options & F_HOSTNAME) {
                if (pledge("stdio inet dns", NULL) == -1)
                        err(1, "pledge");
        } else {
                if (pledge("stdio inet", NULL) == -1)
                        err(1, "pledge");
        }

        arc4random_buf(&tv64_offset, sizeof(tv64_offset));
        arc4random_buf(&mac_key, sizeof(mac_key));

        printf("PING %s (", hostname);
        if (options & F_VERBOSE)
                printf("%s --> ", pr_addr(from, from->sa_len));
        printf("%s): %d data bytes\n", pr_addr(dst, dst->sa_len), datalen);

        smsghdr.msg_name = dst;
        smsghdr.msg_namelen = dst->sa_len;
        smsgiov.iov_base = (caddr_t)outpack;
        smsghdr.msg_iov = &smsgiov;
        smsghdr.msg_iovlen = 1;

        /* Drain our socket. */
        (void)signal(SIGALRM, onsignal);
        memset(&itimer, 0, sizeof(itimer));
        itimer.it_value.tv_sec = 1; /* make sure we don't get stuck */
        (void)setitimer(ITIMER_REAL, &itimer, NULL);
        for (;;) {
                struct msghdr           m;
                union {
                        struct cmsghdr hdr;
                        u_char buf[CMSG_SPACE(1024)];
                }                       cmsgbuf;
                struct iovec            iov[1];
                struct pollfd           pfd;
                struct sockaddr_in      peer4;
                struct sockaddr_in6     peer6;
                ssize_t                 cc;

                if (seenalrm)
                        break;

                pfd.fd = s;
                pfd.events = POLLIN;

                if (poll(&pfd, 1, 0) <= 0)
                        break;

                if (v6flag) {
                        m.msg_name = &peer6;
                        m.msg_namelen = sizeof(peer6);
                } else {
                        m.msg_name = &peer4;
                        m.msg_namelen = sizeof(peer4);
                }
                memset(&iov, 0, sizeof(iov));
                iov[0].iov_base = (caddr_t)packet;
                iov[0].iov_len = packlen;

                m.msg_iov = iov;
                m.msg_iovlen = 1;
                m.msg_control = (caddr_t)&cmsgbuf.buf;
                m.msg_controllen = sizeof(cmsgbuf.buf);

                cc = recvmsg(s, &m, 0);
                if (cc == -1 && errno != EINTR)
                        break;
        }
        memset(&itimer, 0, sizeof(itimer));
        (void)setitimer(ITIMER_REAL, &itimer, NULL);

        while (preload--)               /* Fire off them quickies. */
                pinger(s);

        (void)signal(SIGINT, onsignal);
        (void)signal(SIGINFO, onsignal);

        if (!(options & F_FLOOD)) {
                (void)signal(SIGALRM, onsignal);
                itimer.it_interval = interval;
                itimer.it_value = interval;
                (void)setitimer(ITIMER_REAL, &itimer, NULL);
                if (ntransmitted == 0)
                        retransmit(s);
        }

        seenalrm = seenint = 0;
        seeninfo = 0;

        for (;;) {
                struct msghdr           m;
                union {
                        struct cmsghdr hdr;
                        u_char buf[CMSG_SPACE(1024)];
                }                       cmsgbuf;
                struct iovec            iov[1];
                struct pollfd           pfd;
                struct sockaddr_in      peer4;
                struct sockaddr_in6     peer6;
                ssize_t                 cc;
                int                     timeout;

                /* signal handling */
                if (seenint)
                        break;
                if (seenalrm) {
                        if (flooddone)
                                break;
                        retransmit(s);
                        seenalrm = 0;
                        if (ntransmitted - nreceived - 1 > nmissedmax) {
                                nmissedmax = ntransmitted - nreceived - 1;
                                if (!(options & F_FLOOD) &&
                                    (options & F_AUD_MISS))
                                        fputc('\a', stderr);
                                if ((options & F_SHOWCHAR) &&
                                    !(options & F_FLOOD)) {
                                        putchar('.');
                                        fflush(stdout);
                                }
                        }
                        continue;
                }
                if (seeninfo) {
                        summary();
                        seeninfo = 0;
                        continue;
                }

                if ((options & F_FLOOD && !flooddone)) {
                        if (pinger(s) != 0) {
                                (void)signal(SIGALRM, onsignal);
                                timeout = INFTIM;
                                memset(&itimer, 0, sizeof(itimer));
                                if (nreceived) {
                                        itimer.it_value.tv_sec = 2 * tmax /
                                            1000;
                                        if (itimer.it_value.tv_sec == 0)
                                                itimer.it_value.tv_sec = 1;
                                } else
                                        itimer.it_value.tv_sec = maxwait;
                                (void)setitimer(ITIMER_REAL, &itimer, NULL);

                                /* When the alarm goes off we are done. */
                                flooddone = 1;
                        } else
                                timeout = 10;
                } else
                        timeout = INFTIM;

                pfd.fd = s;
                pfd.events = POLLIN;

                if (poll(&pfd, 1, timeout) <= 0)
                        continue;

                if (v6flag) {
                        m.msg_name = &peer6;
                        m.msg_namelen = sizeof(peer6);
                } else {
                        m.msg_name = &peer4;
                        m.msg_namelen = sizeof(peer4);
                }
                memset(&iov, 0, sizeof(iov));
                iov[0].iov_base = (caddr_t)packet;
                iov[0].iov_len = packlen;
                m.msg_iov = iov;
                m.msg_iovlen = 1;
                m.msg_control = (caddr_t)&cmsgbuf.buf;
                m.msg_controllen = sizeof(cmsgbuf.buf);

                cc = recvmsg(s, &m, 0);
                if (cc == -1) {
                        if (errno != EINTR) {
                                warn("recvmsg");
                                sleep(1);
                        }
                        continue;
                } else if (cc == 0) {
                        int mtu;

                        /*
                         * receive control messages only. Process the
                         * exceptions (currently the only possibility is
                         * a path MTU notification.)
                         */
                        if ((mtu = get_pathmtu(&m, &dst6)) > 0) {
                                if (options & F_VERBOSE) {
                                        printf("new path MTU (%d) is "
                                            "notified\n", mtu);
                                }
                        }
                        continue;
                } else
                        pr_pack(packet, cc, &m);

                if (npackets && nreceived >= npackets)
                        break;
        }
        summary();
        exit(nreceived == 0);
}

void
onsignal(int sig)
{
        switch (sig) {
        case SIGALRM:
                seenalrm++;
                break;
        case SIGINT:
                seenint++;
                break;
        case SIGINFO:
                seeninfo++;
                break;
        }
}

void
fill(char *bp, char *patp)
{
        int ii, jj, kk;
        int pat[16];
        char *cp;

        for (cp = patp; *cp; cp++)
                if (!isxdigit((unsigned char)*cp))
                        errx(1, "patterns must be specified as hex digits");
        ii = sscanf(patp,
            "%2x%2x%2x%2x%2x%2x%2x%2x%2x%2x%2x%2x%2x%2x%2x%2x",
            &pat[0], &pat[1], &pat[2], &pat[3], &pat[4], &pat[5], &pat[6],
            &pat[7], &pat[8], &pat[9], &pat[10], &pat[11], &pat[12],
            &pat[13], &pat[14], &pat[15]);

        if (ii > 0)
                for (kk = 0;
                    kk <= maxpayload - (ECHOLEN + ECHOTMLEN + ii);
                    kk += ii)
                        for (jj = 0; jj < ii; ++jj)
                                bp[jj + kk] = pat[jj];
        if (!(options & F_QUIET)) {
                printf("PATTERN: 0x");
                for (jj = 0; jj < ii; ++jj)
                        printf("%02x", bp[jj] & 0xFF);
                printf("\n");
        }
}

void
summary(void)
{
        printf("\n--- %s ping statistics ---\n", hostname);
        printf("%lld packets transmitted, ", ntransmitted);
        printf("%lld packets received, ", nreceived);

        if (nrepeats)
                printf("%lld duplicates, ", nrepeats);
        if (ntransmitted) {
                if (nreceived > ntransmitted)
                        printf("-- somebody's duplicating packets!");
                else
                        printf("%.1f%% packet loss",
                            ((((double)ntransmitted - nreceived) * 100) /
                            ntransmitted));
        }
        printf("\n");
        if (timinginfo) {
                /* Only display average to microseconds */
                double num = nreceived + nrepeats;
                double avg = tsum / num;
                double dev = sqrt(fmax(0, tsumsq / num - avg * avg));
                printf("round-trip min/avg/max/std-dev = %.3f/%.3f/%.3f/%.3f ms\n",
                    tmin, avg, tmax, dev);
        }
}

/*
 * pr_addr --
 *      Return address in numeric form or a host name
 */
const char *
pr_addr(struct sockaddr *addr, socklen_t addrlen)
{
        static char buf[NI_MAXHOST];
        int flag = 0;

        if (!(options & F_HOSTNAME))
                flag |= NI_NUMERICHOST;

        if (getnameinfo(addr, addrlen, buf, sizeof(buf), NULL, 0, flag) == 0)
                return (buf);
        else
                return "?";
}

/*
 * retransmit --
 *      This routine transmits another ping.
 */
void
retransmit(int s)
{
        struct itimerval itimer;
        static int last_time;

        if (last_time) {
                seenint = 1;    /* break out of ping event loop */
                return;
        }

        if (pinger(s) == 0)
                return;

        /*
         * If we're not transmitting any more packets, change the timer
         * to wait two round-trip times if we've received any packets or
         * maxwait seconds if we haven't.
         */
        memset(&itimer, 0, sizeof(itimer));
        if (nreceived) {
                itimer.it_value.tv_sec = 2 * tmax / 1000;
                if (itimer.it_value.tv_sec == 0)
                        itimer.it_value.tv_sec = 1;
        } else
                itimer.it_value.tv_sec = maxwait;
        (void)setitimer(ITIMER_REAL, &itimer, NULL);

        /* When the alarm goes off we are done. */
        last_time = 1;
}

/*
 * pinger --
 *      Compose and transmit an ICMP ECHO REQUEST packet.  The IP packet
 * will be added on by the kernel.  The ID field is a random number,
 * and the sequence number is an ascending integer.  The first 8 bytes
 * of the data portion are used to hold a UNIX "timeval" struct in VAX
 * byte-order, to compute the round-trip time.
 */
int
pinger(int s)
{
        struct icmp *icp = NULL;
        struct icmp6_hdr *icp6 = NULL;
        int cc, i;
        u_int16_t seq;

        if (npackets && ntransmitted >= npackets)
                return(-1);     /* no more transmission */

        seq = htons(ntransmitted++);

        if (v6flag) {
                icp6 = (struct icmp6_hdr *)outpack;
                memset(icp6, 0, sizeof(*icp6));
                icp6->icmp6_cksum = 0;
                icp6->icmp6_type = ICMP6_ECHO_REQUEST;
                icp6->icmp6_code = 0;
                icp6->icmp6_id = ident;
                icp6->icmp6_seq = seq;
        } else {
                icp = (struct icmp *)outpack;
                icp->icmp_type = ICMP_ECHO;
                icp->icmp_code = 0;
                icp->icmp_cksum = 0;
                icp->icmp_seq = seq;
                icp->icmp_id = ident;
        }
        CLR(ntohs(seq) % mx_dup_ck);

        if (timing) {
                SIPHASH_CTX ctx;
                struct timespec ts;
                struct payload payload;
                struct tv64 *tv64 = &payload.tv64;

                if (clock_gettime(CLOCK_MONOTONIC, &ts) == -1)
                        err(1, "clock_gettime(CLOCK_MONOTONIC)");
                tv64->tv64_sec = htobe64((u_int64_t)ts.tv_sec +
                    tv64_offset.tv64_sec);
                tv64->tv64_nsec = htobe64((u_int64_t)ts.tv_nsec +
                    tv64_offset.tv64_nsec);

                SipHash24_Init(&ctx, &mac_key);
                SipHash24_Update(&ctx, tv64, sizeof(*tv64));
                SipHash24_Update(&ctx, &ident, sizeof(ident));
                SipHash24_Update(&ctx, &seq, sizeof(seq));
                SipHash24_Final(&payload.mac, &ctx);

                memcpy(&outpack[ECHOLEN], &payload, sizeof(payload));
        }

        cc = ECHOLEN + datalen;

        if (!v6flag) {
                /* compute ICMP checksum here */
                icp->icmp_cksum = in_cksum((u_short *)icp, cc);

                if (options & F_HDRINCL) {
                        struct ip *ip = (struct ip *)outpackhdr;

                        smsgiov.iov_base = (caddr_t)outpackhdr;
                        cc += sizeof(struct ip);
                        ip->ip_len = htons(cc);
                        ip->ip_sum = in_cksum((u_short *)outpackhdr, cc);
                }
        }

        smsgiov.iov_len = cc;

        i = sendmsg(s, &smsghdr, 0);

        if (i == -1 || i != cc) {
                if (i == -1)
                        warn("sendmsg");
                printf("ping: wrote %s %d chars, ret=%d\n", hostname, cc, i);
        }
        if (!(options & F_QUIET) && (options & F_FLOOD))
                write(STDOUT_FILENO, &DOT, 1);

        return (0);
}

/*
 * pr_pack --
 *      Print out the packet, if it came from us.  This logic is necessary
 * because ALL readers of the ICMP socket get a copy of ALL ICMP packets
 * which arrive ('tis only fair).  This permits multiple copies of this
 * program to be run without having intermingled output (or statistics!).
 */
void
pr_pack(u_char *buf, int cc, struct msghdr *mhdr)
{
        struct ip *ip = NULL;
        struct icmp *icp = NULL;
        struct icmp6_hdr *icp6 = NULL;
        struct timespec ts, tp;
        struct payload payload;
        struct sockaddr *from;
        socklen_t fromlen;
        double triptime = 0;
        int i, dupflag;
        int hlen = -1, hoplim = -1, echo_reply = 0;
        u_int16_t seq;
        u_char *cp, *dp;
        char* pkttime;

        if (clock_gettime(CLOCK_MONOTONIC, &ts) == -1)
                err(1, "clock_gettime(CLOCK_MONOTONIC)");

        if (v6flag) {
                if (!mhdr || !mhdr->msg_name ||
                    mhdr->msg_namelen != sizeof(struct sockaddr_in6) ||
                    ((struct sockaddr *)mhdr->msg_name)->sa_family !=
                    AF_INET6) {
                        if (options & F_VERBOSE)
                                warnx("invalid peername");
                        return;
                }
                from = (struct sockaddr *)mhdr->msg_name;
                fromlen = mhdr->msg_namelen;

                if (cc < sizeof(struct icmp6_hdr)) {
                        if (options & F_VERBOSE)
                                warnx("packet too short (%d bytes) from %s", cc,
                                    pr_addr(from, fromlen));
                        return;
                }
                icp6 = (struct icmp6_hdr *)buf;

                if ((hoplim = get_hoplim(mhdr)) == -1) {
                        warnx("failed to get receiving hop limit");
                        return;
                }

                if (icp6->icmp6_type == ICMP6_ECHO_REPLY) {
                        if (icp6->icmp6_id != ident)
                                return;                 /* 'Twas not our ECHO */
                        seq = icp6->icmp6_seq;
                        echo_reply = 1;
                        pkttime = (char *)(icp6 + 1);
                }
        } else {
                if (!mhdr || !mhdr->msg_name ||
                    mhdr->msg_namelen != sizeof(struct sockaddr_in) ||
                    ((struct sockaddr *)mhdr->msg_name)->sa_family != AF_INET) {
                        if (options & F_VERBOSE)
                                warnx("invalid peername");
                        return;
                }

                from = (struct sockaddr *)mhdr->msg_name;
                fromlen = mhdr->msg_namelen;

                /* Check the IP header */
                ip = (struct ip *)buf;
                hlen = ip->ip_hl << 2;
                if (cc < hlen + ICMP_MINLEN) {
                        if (options & F_VERBOSE)
                                warnx("packet too short (%d bytes) from %s", cc,
                                    pr_addr(from, fromlen));
                        return;
                }

                /* Now the ICMP part */
                cc -= hlen;
                icp = (struct icmp *)(buf + hlen);
                if (icp->icmp_type == ICMP_ECHOREPLY) {
                        if (icp->icmp_id != ident)
                                return;                 /* 'Twas not our ECHO */
                        seq = icp->icmp_seq;
                        echo_reply = 1;
                        pkttime = (char *)icp->icmp_data;
                }
        }

        if (echo_reply) {
                ++nreceived;
                if (cc >= ECHOLEN + ECHOTMLEN) {
                        SIPHASH_CTX ctx;
                        struct tv64 *tv64;
                        u_int8_t mac[SIPHASH_DIGEST_LENGTH];

                        memcpy(&payload, pkttime, sizeof(payload));
                        tv64 = &payload.tv64;

                        SipHash24_Init(&ctx, &mac_key);
                        SipHash24_Update(&ctx, tv64, sizeof(*tv64));
                        SipHash24_Update(&ctx, &ident, sizeof(ident));
                        SipHash24_Update(&ctx, &seq, sizeof(seq));
                        SipHash24_Final(mac, &ctx);

                        if (timingsafe_memcmp(mac, &payload.mac,
                            sizeof(mac)) != 0) {
                                printf("signature mismatch from %s: "
                                    "icmp_seq=%u\n", pr_addr(from, fromlen),
                                    ntohs(seq));
                                --nreceived;
                                return;
                        }
                        timinginfo=1;

                        tp.tv_sec = betoh64(tv64->tv64_sec) -
                            tv64_offset.tv64_sec;
                        tp.tv_nsec = betoh64(tv64->tv64_nsec) -
                            tv64_offset.tv64_nsec;

                        timespecsub(&ts, &tp, &ts);
                        triptime = ((double)ts.tv_sec) * 1000.0 +
                            ((double)ts.tv_nsec) / 1000000.0;
                        tsum += triptime;
                        tsumsq += triptime * triptime;
                        if (triptime < tmin)
                                tmin = triptime;
                        if (triptime > tmax)
                                tmax = triptime;
                }

                if (TST(ntohs(seq) % mx_dup_ck)) {
                        ++nrepeats;
                        --nreceived;
                        dupflag = 1;
                } else {
                        SET(ntohs(seq) % mx_dup_ck);
                        dupflag = 0;
                }

                if (options & F_QUIET)
                        return;

                if (options & F_FLOOD)
                        write(STDOUT_FILENO, &BSPACE, 1);
                else if (options & F_SHOWCHAR) {
                        if (dupflag)
                                putchar('D');
                        else if (cc - ECHOLEN < datalen)
                                putchar('T');
                        else
                                putchar('!');
                } else {
                        printf("%d bytes from %s: icmp_seq=%u", cc,
                            pr_addr(from, fromlen), ntohs(seq));
                        if (v6flag)
                                printf(" hlim=%d", hoplim);
                        else
                                printf(" ttl=%d", ip->ip_ttl);
                        if (cc >= ECHOLEN + ECHOTMLEN)
                                printf(" time=%.3f ms", triptime);
                        if (dupflag)
                                printf(" (DUP!)");
                        /* check the data */
                        if (cc - ECHOLEN < datalen)
                                printf(" (TRUNC!)");
                        if (v6flag)
                                cp = buf + ECHOLEN + ECHOTMLEN;
                        else
                                cp = (u_char *)&icp->icmp_data[ECHOTMLEN];
                        dp = &outpack[ECHOLEN + ECHOTMLEN];
                        for (i = ECHOLEN + ECHOTMLEN;
                            i < cc && i < datalen;
                            ++i, ++cp, ++dp) {
                                if (*cp != *dp) {
                                        printf("\nwrong data byte #%d "
                                            "should be 0x%x but was 0x%x",
                                            i - ECHOLEN, *dp, *cp);
                                        if (v6flag)
                                                cp = buf + ECHOLEN;
                                        else
                                                cp = (u_char *)
                                                    &icp->icmp_data[0];
                                        for (i = ECHOLEN; i < cc && i < datalen;
                                            ++i, ++cp) {
                                                if ((i % 32) == 8)
                                                        printf("\n\t");
                                                printf("%x ", *cp);
                                        }
                                        break;
                                }
                        }
                }
        } else {
                /* We've got something other than an ECHOREPLY */
                if (!(options & F_VERBOSE))
                        return;
                printf("%d bytes from %s: ", cc, pr_addr(from, fromlen));
                if (v6flag)
                        pr_icmph6(icp6, buf + cc);
                else
                        pr_icmph(icp);
        }

        /* Display any IP options */
        if (!v6flag && hlen > sizeof(struct ip))
                pr_ipopt(hlen, buf);

        if (!(options & F_FLOOD)) {
                if (!(options & F_SHOWCHAR)) {
                        putchar('\n');
                        if (v6flag && (options & F_VERBOSE))
                                pr_exthdrs(mhdr);
                }
                fflush(stdout);
                if (options & F_AUD_RECV)
                        fputc('\a', stderr);
        }
}

void
pr_ipopt(int hlen, u_char *buf)
{
        static int old_rrlen;
        static char old_rr[MAX_IPOPTLEN];
        struct sockaddr_in s_in;
        in_addr_t l;
        u_int i, j;
        u_char *cp;

        cp = buf + sizeof(struct ip);

        s_in.sin_len = sizeof(s_in);
        s_in.sin_family = AF_INET;

        for (; hlen > (int)sizeof(struct ip); --hlen, ++cp) {
                switch (*cp) {
                case IPOPT_EOL:
                        hlen = 0;
                        break;
                case IPOPT_LSRR:
                        printf("\nLSRR: ");
                        hlen -= 2;
                        j = *++cp;
                        ++cp;
                        i = 0;
                        if (j > IPOPT_MINOFF) {
                                for (;;) {
                                        l = *++cp;
                                        l = (l<<8) + *++cp;
                                        l = (l<<8) + *++cp;
                                        l = (l<<8) + *++cp;
                                        if (l == 0)
                                                printf("\t0.0.0.0");
                                        else {
                                                s_in.sin_addr.s_addr = ntohl(l);
                                                printf("\t%s",
                                                    pr_addr((struct sockaddr*)
                                                    &s_in, sizeof(s_in)));
                                        }
                                        hlen -= 4;
                                        j -= 4;
                                        i += 4;
                                        if (j <= IPOPT_MINOFF)
                                                break;
                                        if (i >= MAX_IPOPTLEN) {
                                                printf("\t(truncated route)");
                                                break;
                                        }
                                        putchar('\n');
                                }
                        }
                        break;
                case IPOPT_RR:
                        j = *++cp;              /* get length */
                        i = *++cp;              /* and pointer */
                        hlen -= 2;
                        if (i > j)
                                i = j;
                        i -= IPOPT_MINOFF;
                        if (i <= 0)
                                continue;
                        if (i == old_rrlen &&
                            cp == buf + sizeof(struct ip) + 2 &&
                            !memcmp(cp, old_rr, i) &&
                            !(options & F_FLOOD)) {
                                printf("\t(same route)");
                                i = (i + 3) & ~0x3;
                                hlen -= i;
                                cp += i;
                                break;
                        }
                        if (i < MAX_IPOPTLEN) {
                                old_rrlen = i;
                                memcpy(old_rr, cp, i);
                        } else
                                old_rrlen = 0;

                        printf("\nRR: ");
                        j = 0;
                        for (;;) {
                                l = *++cp;
                                l = (l<<8) + *++cp;
                                l = (l<<8) + *++cp;
                                l = (l<<8) + *++cp;
                                if (l == 0)
                                        printf("\t0.0.0.0");
                                else {
                                        s_in.sin_addr.s_addr = ntohl(l);
                                        printf("\t%s",
                                            pr_addr((struct sockaddr*)&s_in,
                                            sizeof(s_in)));
                                }
                                hlen -= 4;
                                i -= 4;
                                j += 4;
                                if (i <= 0)
                                        break;
                                if (j >= MAX_IPOPTLEN) {
                                        printf("\t(truncated route)");
                                        break;
                                }
                                putchar('\n');
                        }
                        break;
                case IPOPT_NOP:
                        printf("\nNOP");
                        break;
                default:
                        printf("\nunknown option %x", *cp);
                        if (cp[IPOPT_OLEN] > 0 && (cp[IPOPT_OLEN] - 1) <= hlen) {
                                hlen = hlen - (cp[IPOPT_OLEN] - 1);
                                cp = cp + (cp[IPOPT_OLEN] - 1);
                        } else
                                hlen = 0;
                        break;
                }
        }
}

/*
 * in_cksum --
 *      Checksum routine for Internet Protocol family headers (C Version)
 */
int
in_cksum(u_short *addr, int len)
{
        int nleft = len;
        u_short *w = addr;
        int sum = 0;
        u_short answer = 0;

        /*
         * Our algorithm is simple, using a 32 bit accumulator (sum), we add
         * sequential 16 bit words to it, and at the end, fold back all the
         * carry bits from the top 16 bits into the lower 16 bits.
         */
        while (nleft > 1) {
                sum += *w++;
                nleft -= 2;
        }

        /* mop up an odd byte, if necessary */
        if (nleft == 1) {
                *(u_char *)(&answer) = *(u_char *)w ;
                sum += answer;
        }

        /* add back carry outs from top 16 bits to low 16 bits */
        sum = (sum >> 16) + (sum & 0xffff);     /* add hi 16 to low 16 */
        sum += (sum >> 16);                     /* add carry */
        answer = ~sum;                          /* truncate to 16 bits */
        return(answer);
}

/*
 * pr_icmph --
 *      Print a descriptive string about an ICMP header.
 */
void
pr_icmph(struct icmp *icp)
{
        switch(icp->icmp_type) {
        case ICMP_ECHOREPLY:
                printf("Echo Reply\n");
                /* XXX ID + Seq + Data */
                break;
        case ICMP_UNREACH:
                switch(icp->icmp_code) {
                case ICMP_UNREACH_NET:
                        printf("Destination Net Unreachable\n");
                        break;
                case ICMP_UNREACH_HOST:
                        printf("Destination Host Unreachable\n");
                        break;
                case ICMP_UNREACH_PROTOCOL:
                        printf("Destination Protocol Unreachable\n");
                        break;
                case ICMP_UNREACH_PORT:
                        printf("Destination Port Unreachable\n");
                        break;
                case ICMP_UNREACH_NEEDFRAG:
                        if (icp->icmp_nextmtu != 0)
                                printf("frag needed and DF set (MTU %d)\n",
                                    ntohs(icp->icmp_nextmtu));
                        else
                                printf("frag needed and DF set\n");
                        break;
                case ICMP_UNREACH_SRCFAIL:
                        printf("Source Route Failed\n");
                        break;
                case ICMP_UNREACH_NET_UNKNOWN:
                        printf("Network Unknown\n");
                        break;
                case ICMP_UNREACH_HOST_UNKNOWN:
                        printf("Host Unknown\n");
                        break;
                case ICMP_UNREACH_ISOLATED:
                        printf("Source Isolated\n");
                        break;
                case ICMP_UNREACH_NET_PROHIB:
                        printf("Dest. Net Administratively Prohibited\n");
                        break;
                case ICMP_UNREACH_HOST_PROHIB:
                        printf("Dest. Host Administratively Prohibited\n");
                        break;
                case ICMP_UNREACH_TOSNET:
                        printf("Destination Net Unreachable for TOS\n");
                        break;
                case ICMP_UNREACH_TOSHOST:
                        printf("Destination Host Unreachable for TOS\n");
                        break;
                case ICMP_UNREACH_FILTER_PROHIB:
                        printf("Route administratively prohibited\n");
                        break;
                case ICMP_UNREACH_HOST_PRECEDENCE:
                        printf("Host Precedence Violation\n");
                        break;
                case ICMP_UNREACH_PRECEDENCE_CUTOFF:
                        printf("Precedence Cutoff\n");
                        break;
                default:
                        printf("Dest Unreachable, Unknown Code: %d\n",
                            icp->icmp_code);
                        break;
                }
                /* Print returned IP header information */
                pr_retip((struct ip *)icp->icmp_data);
                break;
        case ICMP_SOURCEQUENCH:
                printf("Source Quench\n");
                pr_retip((struct ip *)icp->icmp_data);
                break;
        case ICMP_REDIRECT:
                switch(icp->icmp_code) {
                case ICMP_REDIRECT_NET:
                        printf("Redirect Network");
                        break;
                case ICMP_REDIRECT_HOST:
                        printf("Redirect Host");
                        break;
                case ICMP_REDIRECT_TOSNET:
                        printf("Redirect Type of Service and Network");
                        break;
                case ICMP_REDIRECT_TOSHOST:
                        printf("Redirect Type of Service and Host");
                        break;
                default:
                        printf("Redirect, Unknown Code: %d", icp->icmp_code);
                        break;
                }
                printf("(New addr: %s)\n",
                    inet_ntoa(icp->icmp_gwaddr));
                pr_retip((struct ip *)icp->icmp_data);
                break;
        case ICMP_ECHO:
                printf("Echo Request\n");
                /* XXX ID + Seq + Data */
                break;
        case ICMP_ROUTERADVERT:
                /* RFC1256 */
                printf("Router Discovery Advertisement\n");
                printf("(%d entries, lifetime %d seconds)\n",
                    icp->icmp_num_addrs, ntohs(icp->icmp_lifetime));
                break;
        case ICMP_ROUTERSOLICIT:
                /* RFC1256 */
                printf("Router Discovery Solicitation\n");
                break;
        case ICMP_TIMXCEED:
                switch(icp->icmp_code) {
                case ICMP_TIMXCEED_INTRANS:
                        printf("Time to live exceeded\n");
                        break;
                case ICMP_TIMXCEED_REASS:
                        printf("Frag reassembly time exceeded\n");
                        break;
                default:
                        printf("Time exceeded, Unknown Code: %d\n",
                            icp->icmp_code);
                        break;
                }
                pr_retip((struct ip *)icp->icmp_data);
                break;
        case ICMP_PARAMPROB:
                switch(icp->icmp_code) {
                case ICMP_PARAMPROB_OPTABSENT:
                        printf("Parameter problem, required option "
                            "absent: pointer = 0x%02x\n",
                            ntohs(icp->icmp_hun.ih_pptr));
                        break;
                default:
                        printf("Parameter problem: pointer = 0x%02x\n",
                            ntohs(icp->icmp_hun.ih_pptr));
                        break;
                }
                pr_retip((struct ip *)icp->icmp_data);
                break;
        case ICMP_TSTAMP:
                printf("Timestamp\n");
                /* XXX ID + Seq + 3 timestamps */
                break;
        case ICMP_TSTAMPREPLY:
                printf("Timestamp Reply\n");
                /* XXX ID + Seq + 3 timestamps */
                break;
        case ICMP_IREQ:
                printf("Information Request\n");
                /* XXX ID + Seq */
                break;
        case ICMP_IREQREPLY:
                printf("Information Reply\n");
                /* XXX ID + Seq */
                break;
        case ICMP_MASKREQ:
                printf("Address Mask Request\n");
                break;
        case ICMP_MASKREPLY:
                printf("Address Mask Reply (Mask 0x%08x)\n",
                    ntohl(icp->icmp_mask));
                break;
        default:
                printf("Unknown ICMP type: %d\n", icp->icmp_type);
        }
}

/*
 * pr_iph --
 *      Print an IP header with options.
 */
void
pr_iph(struct ip *ip)
{
        int hlen;
        u_char *cp;

        hlen = ip->ip_hl << 2;
        cp = (u_char *)ip + 20;         /* point to options */

        printf("Vr HL TOS  Len   ID Flg  off TTL Pro  cks      Src      Dst Data\n");
        printf(" %1x  %1x  %02x %04x %04x",
            ip->ip_v, ip->ip_hl, ip->ip_tos, ip->ip_len, ip->ip_id);
        printf("   %1x %04x", ((ip->ip_off) & 0xe000) >> 13,
            (ip->ip_off) & 0x1fff);
        printf("  %02x  %02x %04x", ip->ip_ttl, ip->ip_p, ip->ip_sum);
        printf(" %s ", inet_ntoa(*(struct in_addr *)&ip->ip_src.s_addr));
        printf(" %s ", inet_ntoa(*(struct in_addr *)&ip->ip_dst.s_addr));
        /* dump and option bytes */
        while (hlen-- > 20) {
                printf("%02x", *cp++);
        }
        putchar('\n');
}

/*
 * pr_retip --
 *      Dump some info on a returned (via ICMP) IP packet.
 */
void
pr_retip(struct ip *ip)
{
        int hlen;
        u_char *cp;

        pr_iph(ip);
        hlen = ip->ip_hl << 2;
        cp = (u_char *)ip + hlen;

        if (ip->ip_p == 6)
                printf("TCP: from port %u, to port %u (decimal)\n",
                    (*cp * 256 + *(cp + 1)), (*(cp + 2) * 256 + *(cp + 3)));
        else if (ip->ip_p == 17)
                printf("UDP: from port %u, to port %u (decimal)\n",
                    (*cp * 256 + *(cp + 1)), (*(cp + 2) * 256 + *(cp + 3)));
}

#ifndef SMALL
int
map_tos(char *key, int *val)
{
        /* DiffServ Codepoints and other TOS mappings */
        const struct toskeywords {
                const char      *keyword;
                int              val;
        } *t, toskeywords[] = {
                { "af11",               IPTOS_DSCP_AF11 },
                { "af12",               IPTOS_DSCP_AF12 },
                { "af13",               IPTOS_DSCP_AF13 },
                { "af21",               IPTOS_DSCP_AF21 },
                { "af22",               IPTOS_DSCP_AF22 },
                { "af23",               IPTOS_DSCP_AF23 },
                { "af31",               IPTOS_DSCP_AF31 },
                { "af32",               IPTOS_DSCP_AF32 },
                { "af33",               IPTOS_DSCP_AF33 },
                { "af41",               IPTOS_DSCP_AF41 },
                { "af42",               IPTOS_DSCP_AF42 },
                { "af43",               IPTOS_DSCP_AF43 },
                { "critical",           IPTOS_PREC_CRITIC_ECP },
                { "cs0",                IPTOS_DSCP_CS0 },
                { "cs1",                IPTOS_DSCP_CS1 },
                { "cs2",                IPTOS_DSCP_CS2 },
                { "cs3",                IPTOS_DSCP_CS3 },
                { "cs4",                IPTOS_DSCP_CS4 },
                { "cs5",                IPTOS_DSCP_CS5 },
                { "cs6",                IPTOS_DSCP_CS6 },
                { "cs7",                IPTOS_DSCP_CS7 },
                { "ef",                 IPTOS_DSCP_EF },
                { "inetcontrol",        IPTOS_PREC_INTERNETCONTROL },
                { "lowdelay",           IPTOS_LOWDELAY },
                { "netcontrol",         IPTOS_PREC_NETCONTROL },
                { "reliability",        IPTOS_RELIABILITY },
                { "throughput",         IPTOS_THROUGHPUT },
                { "va",                 IPTOS_DSCP_VA },
                { NULL,                 -1 },
        };

        for (t = toskeywords; t->keyword != NULL; t++) {
                if (strcmp(key, t->keyword) == 0) {
                        *val = t->val;
                        return (1);
                }
        }

        return (0);
}
#endif  /* SMALL */

void
pr_exthdrs(struct msghdr *mhdr)
{
        struct cmsghdr *cm;

        for (cm = (struct cmsghdr *)CMSG_FIRSTHDR(mhdr); cm;
            cm = (struct cmsghdr *)CMSG_NXTHDR(mhdr, cm)) {
                if (cm->cmsg_level != IPPROTO_IPV6)
                        continue;

                switch (cm->cmsg_type) {
                case IPV6_HOPOPTS:
                        printf("  HbH Options: ");
                        pr_ip6opt(CMSG_DATA(cm));
                        break;
                case IPV6_DSTOPTS:
                case IPV6_RTHDRDSTOPTS:
                        printf("  Dst Options: ");
                        pr_ip6opt(CMSG_DATA(cm));
                        break;
                case IPV6_RTHDR:
                        printf("  Routing: ");
                        pr_rthdr(CMSG_DATA(cm));
                        break;
                }
        }
}

void
pr_ip6opt(void *extbuf)
{
        struct ip6_hbh *ext;
        int currentlen;
        u_int8_t type;
        size_t extlen;
        socklen_t len;
        void *databuf;
        u_int16_t value2;
        u_int32_t value4;

        ext = (struct ip6_hbh *)extbuf;
        extlen = (ext->ip6h_len + 1) * 8;
        printf("nxt %u, len %u (%lu bytes)\n", ext->ip6h_nxt,
            (unsigned int)ext->ip6h_len, (unsigned long)extlen);

        currentlen = 0;
        while (1) {
                currentlen = inet6_opt_next(extbuf, extlen, currentlen,
                    &type, &len, &databuf);
                if (currentlen == -1)
                        break;
                switch (type) {
                /*
                 * Note that inet6_opt_next automatically skips any padding
                 * options.
                 */
                case IP6OPT_JUMBO:
                        inet6_opt_get_val(databuf, 0, &value4, sizeof(value4));
                        printf("    Jumbo Payload Opt: Length %u\n",
                            (u_int32_t)ntohl(value4));
                        break;
                case IP6OPT_ROUTER_ALERT:
                        inet6_opt_get_val(databuf, 0, &value2, sizeof(value2));
                        printf("    Router Alert Opt: Type %u\n",
                            ntohs(value2));
                        break;
                default:
                        printf("    Received Opt %u len %lu\n",
                            type, (unsigned long)len);
                        break;
                }
        }
        return;
}

void
pr_rthdr(void *extbuf)
{
        struct in6_addr *in6;
        char ntopbuf[INET6_ADDRSTRLEN];
        struct ip6_rthdr *rh = (struct ip6_rthdr *)extbuf;
        int i, segments;

        /* print fixed part of the header */
        printf("nxt %u, len %u (%d bytes), type %u, ", rh->ip6r_nxt,
            rh->ip6r_len, (rh->ip6r_len + 1) << 3, rh->ip6r_type);
        if ((segments = inet6_rth_segments(extbuf)) >= 0)
                printf("%d segments, ", segments);
        else
                printf("segments unknown, ");
        printf("%d left\n", rh->ip6r_segleft);

        for (i = 0; i < segments; i++) {
                in6 = inet6_rth_getaddr(extbuf, i);
                if (in6 == NULL)
                        printf("   [%d]<NULL>\n", i);
                else {
                        if (!inet_ntop(AF_INET6, in6, ntopbuf,
                            sizeof(ntopbuf)))
                                strncpy(ntopbuf, "?", sizeof(ntopbuf));
                        printf("   [%d]%s\n", i, ntopbuf);
                }
        }

        return;

}

int
get_hoplim(struct msghdr *mhdr)
{
        struct cmsghdr *cm;

        for (cm = (struct cmsghdr *)CMSG_FIRSTHDR(mhdr); cm;
             cm = (struct cmsghdr *)CMSG_NXTHDR(mhdr, cm)) {
                if (cm->cmsg_len == 0)
                        return(-1);

                if (cm->cmsg_level == IPPROTO_IPV6 &&
                    cm->cmsg_type == IPV6_HOPLIMIT &&
                    cm->cmsg_len == CMSG_LEN(sizeof(int)))
                        return(*(int *)CMSG_DATA(cm));
        }

        return(-1);
}

int
get_pathmtu(struct msghdr *mhdr, struct sockaddr_in6 *dst)
{
        struct cmsghdr *cm;
        struct ip6_mtuinfo *mtuctl = NULL;

        for (cm = (struct cmsghdr *)CMSG_FIRSTHDR(mhdr); cm;
            cm = (struct cmsghdr *)CMSG_NXTHDR(mhdr, cm)) {
                if (cm->cmsg_len == 0)
                        return(0);

                if (cm->cmsg_level == IPPROTO_IPV6 &&
                    cm->cmsg_type == IPV6_PATHMTU &&
                    cm->cmsg_len == CMSG_LEN(sizeof(struct ip6_mtuinfo))) {
                        mtuctl = (struct ip6_mtuinfo *)CMSG_DATA(cm);

                        /*
                         * If the notified destination is different from
                         * the one we are pinging, just ignore the info.
                         * We check the scope ID only when both notified value
                         * and our own value have non-0 values, because we may
                         * have used the default scope zone ID for sending,
                         * in which case the scope ID value is 0.
                         */
                        if (!IN6_ARE_ADDR_EQUAL(&mtuctl->ip6m_addr.sin6_addr,
                            &dst->sin6_addr) ||
                            (mtuctl->ip6m_addr.sin6_scope_id &&
                            dst->sin6_scope_id &&
                            mtuctl->ip6m_addr.sin6_scope_id !=
                            dst->sin6_scope_id)) {
                                if (options & F_VERBOSE) {
                                        printf("path MTU for %s is notified. "
                                            "(ignored)\n",
                                            pr_addr((struct sockaddr *)
                                            &mtuctl->ip6m_addr,
                                            sizeof(mtuctl->ip6m_addr)));
                                }
                                return(0);
                        }

                        /*
                         * Ignore an invalid MTU. XXX: can we just believe
                         * the kernel check?
                         */
                        if (mtuctl->ip6m_mtu < IPV6_MMTU)
                                return(0);

                        /* notification for our destination. return the MTU. */
                        return((int)mtuctl->ip6m_mtu);
                }
        }
        return(0);
}

/*
 * pr_icmph6 --
 *      Print a descriptive string about an ICMP header.
 */
void
pr_icmph6(struct icmp6_hdr *icp, u_char *end)
{
        char ntop_buf[INET6_ADDRSTRLEN];
        struct nd_redirect *red;

        switch (icp->icmp6_type) {
        case ICMP6_DST_UNREACH:
                switch (icp->icmp6_code) {
                case ICMP6_DST_UNREACH_NOROUTE:
                        printf("No Route to Destination\n");
                        break;
                case ICMP6_DST_UNREACH_ADMIN:
                        printf("Destination Administratively "
                            "Unreachable\n");
                        break;
                case ICMP6_DST_UNREACH_BEYONDSCOPE:
                        printf("Destination Unreachable Beyond Scope\n");
                        break;
                case ICMP6_DST_UNREACH_ADDR:
                        printf("Destination Host Unreachable\n");
                        break;
                case ICMP6_DST_UNREACH_NOPORT:
                        printf("Destination Port Unreachable\n");
                        break;
                default:
                        printf("Destination Unreachable, Bad Code: %d\n",
                            icp->icmp6_code);
                        break;
                }
                /* Print returned IP header information */
                pr_retip6((struct ip6_hdr *)(icp + 1), end);
                break;
        case ICMP6_PACKET_TOO_BIG:
                printf("Packet too big mtu = %d\n",
                    (int)ntohl(icp->icmp6_mtu));
                pr_retip6((struct ip6_hdr *)(icp + 1), end);
                break;
        case ICMP6_TIME_EXCEEDED:
                switch (icp->icmp6_code) {
                case ICMP6_TIME_EXCEED_TRANSIT:
                        printf("Time to live exceeded\n");
                        break;
                case ICMP6_TIME_EXCEED_REASSEMBLY:
                        printf("Frag reassembly time exceeded\n");
                        break;
                default:
                        printf("Time exceeded, Bad Code: %d\n",
                            icp->icmp6_code);
                        break;
                }
                pr_retip6((struct ip6_hdr *)(icp + 1), end);
                break;
        case ICMP6_PARAM_PROB:
                printf("Parameter problem: ");
                switch (icp->icmp6_code) {
                case ICMP6_PARAMPROB_HEADER:
                        printf("Erroneous Header ");
                        break;
                case ICMP6_PARAMPROB_NEXTHEADER:
                        printf("Unknown Nextheader ");
                        break;
                case ICMP6_PARAMPROB_OPTION:
                        printf("Unrecognized Option ");
                        break;
                default:
                        printf("Bad code(%d) ", icp->icmp6_code);
                        break;
                }
                printf("pointer = 0x%02x\n",
                    (u_int32_t)ntohl(icp->icmp6_pptr));
                pr_retip6((struct ip6_hdr *)(icp + 1), end);
                break;
        case ICMP6_ECHO_REQUEST:
                printf("Echo Request");
                /* XXX ID + Seq + Data */
                break;
        case ICMP6_ECHO_REPLY:
                printf("Echo Reply");
                /* XXX ID + Seq + Data */
                break;
        case ICMP6_MEMBERSHIP_QUERY:
                printf("Listener Query");
                break;
        case ICMP6_MEMBERSHIP_REPORT:
                printf("Listener Report");
                break;
        case ICMP6_MEMBERSHIP_REDUCTION:
                printf("Listener Done");
                break;
        case ND_ROUTER_SOLICIT:
                printf("Router Solicitation");
                break;
        case ND_ROUTER_ADVERT:
                printf("Router Advertisement");
                break;
        case ND_NEIGHBOR_SOLICIT:
                printf("Neighbor Solicitation");
                break;
        case ND_NEIGHBOR_ADVERT:
                printf("Neighbor Advertisement");
                break;
        case ND_REDIRECT:
                red = (struct nd_redirect *)icp;
                printf("Redirect\n");
                if (!inet_ntop(AF_INET6, &red->nd_rd_dst, ntop_buf,
                    sizeof(ntop_buf)))
                        strncpy(ntop_buf, "?", sizeof(ntop_buf));
                printf("Destination: %s", ntop_buf);
                if (!inet_ntop(AF_INET6, &red->nd_rd_target, ntop_buf,
                    sizeof(ntop_buf)))
                        strncpy(ntop_buf, "?", sizeof(ntop_buf));
                printf(" New Target: %s", ntop_buf);
                break;
        default:
                printf("Bad ICMP type: %d", icp->icmp6_type);
        }
}

/*
 * pr_iph6 --
 *      Print an IP6 header.
 */
void
pr_iph6(struct ip6_hdr *ip6)
{
        u_int32_t flow = ip6->ip6_flow & IPV6_FLOWLABEL_MASK;
        u_int8_t tc;
        char ntop_buf[INET6_ADDRSTRLEN];

        tc = *(&ip6->ip6_vfc + 1); /* XXX */
        tc = (tc >> 4) & 0x0f;
        tc |= (ip6->ip6_vfc << 4);

        printf("Vr TC  Flow Plen Nxt Hlim\n");
        printf(" %1x %02x %05x %04x  %02x   %02x\n",
            (ip6->ip6_vfc & IPV6_VERSION_MASK) >> 4, tc, (u_int32_t)ntohl(flow),
            ntohs(ip6->ip6_plen), ip6->ip6_nxt, ip6->ip6_hlim);
        if (!inet_ntop(AF_INET6, &ip6->ip6_src, ntop_buf, sizeof(ntop_buf)))
                strncpy(ntop_buf, "?", sizeof(ntop_buf));
        printf("%s->", ntop_buf);
        if (!inet_ntop(AF_INET6, &ip6->ip6_dst, ntop_buf, sizeof(ntop_buf)))
                strncpy(ntop_buf, "?", sizeof(ntop_buf));
        printf("%s\n", ntop_buf);
}

/*
 * pr_retip6 --
 *      Dump some info on a returned (via ICMPv6) IPv6 packet.
 */
void
pr_retip6(struct ip6_hdr *ip6, u_char *end)
{
        u_char *cp = (u_char *)ip6, nh;
        int hlen;

        if (end - (u_char *)ip6 < sizeof(*ip6)) {
                printf("IP6");
                goto trunc;
        }
        pr_iph6(ip6);
        hlen = sizeof(*ip6);

        nh = ip6->ip6_nxt;
        cp += hlen;
        while (end - cp >= 8) {
                switch (nh) {
                case IPPROTO_HOPOPTS:
                        printf("HBH ");
                        hlen = (((struct ip6_hbh *)cp)->ip6h_len+1) << 3;
                        nh = ((struct ip6_hbh *)cp)->ip6h_nxt;
                        break;
                case IPPROTO_DSTOPTS:
                        printf("DSTOPT ");
                        hlen = (((struct ip6_dest *)cp)->ip6d_len+1) << 3;
                        nh = ((struct ip6_dest *)cp)->ip6d_nxt;
                        break;
                case IPPROTO_FRAGMENT:
                        printf("FRAG ");
                        hlen = sizeof(struct ip6_frag);
                        nh = ((struct ip6_frag *)cp)->ip6f_nxt;
                        break;
                case IPPROTO_ROUTING:
                        printf("RTHDR ");
                        hlen = (((struct ip6_rthdr *)cp)->ip6r_len+1) << 3;
                        nh = ((struct ip6_rthdr *)cp)->ip6r_nxt;
                        break;
                case IPPROTO_AH:
                        printf("AH ");
                        hlen = (((struct ah *)cp)->ah_hl+2) << 2;
                        nh = ((struct ah *)cp)->ah_nh;
                        break;
                case IPPROTO_ICMPV6:
                        printf("ICMP6: type = %d, code = %d\n",
                            *cp, *(cp + 1));
                        return;
                case IPPROTO_ESP:
                        printf("ESP\n");
                        return;
                case IPPROTO_TCP:
                        printf("TCP: from port %u, to port %u (decimal)\n",
                            (*cp * 256 + *(cp + 1)),
                            (*(cp + 2) * 256 + *(cp + 3)));
                        return;
                case IPPROTO_UDP:
                        printf("UDP: from port %u, to port %u (decimal)\n",
                            (*cp * 256 + *(cp + 1)),
                            (*(cp + 2) * 256 + *(cp + 3)));
                        return;
                default:
                        printf("Unknown Header(%d)\n", nh);
                        return;
                }

                if ((cp += hlen) >= end)
                        goto trunc;
        }
        if (end - cp < 8)
                goto trunc;

        putchar('\n');
        return;

  trunc:
        printf("...\n");
        return;
}

__dead void
usage(void)
{
        if (v6flag) {
                fprintf(stderr,
                    "usage: ping6 [-DdEefgHLmnqv] [-c count] [-h hoplimit] "
                    "[-I sourceaddr]\n\t[-i interval] [-l preload] "
                    "[-p pattern] [-s packetsize] [-T toskeyword]\n"
                    "\t[-V rtable] [-w maxwait] host\n");
        } else {
                fprintf(stderr,
                    "usage: ping [-DdEefgHLnqRv] [-c count] [-I sourceaddr] "
                    "[-i interval]\n\t[-l preload] [-p pattern] [-s packetsize]"
#ifndef SMALL
                    " [-T toskeyword]"
#endif  /* SMALL */
                    "\n\t[-t ttl] [-V rtable] [-w maxwait] host\n");
        }
        exit(1);
}