diff options
author | ume <ume@FreeBSD.org> | 2001-06-11 12:39:29 +0000 |
---|---|---|
committer | ume <ume@FreeBSD.org> | 2001-06-11 12:39:29 +0000 |
commit | 832f8d224926758a9ae0b23a6b45353e44fbc87a (patch) | |
tree | a79fc7ad2b97862c4a404f352f0211ad93a7b5f1 /sbin/ping6 | |
parent | 2693854b01a52b0395a91322aa3edf926bddff38 (diff) | |
download | FreeBSD-src-832f8d224926758a9ae0b23a6b45353e44fbc87a.zip FreeBSD-src-832f8d224926758a9ae0b23a6b45353e44fbc87a.tar.gz |
Sync with recent KAME.
This work was based on kame-20010528-freebsd43-snap.tgz and some
critical problem after the snap was out were fixed.
There are many many changes since last KAME merge.
TODO:
- The definitions of SADB_* in sys/net/pfkeyv2.h are still different
from RFC2407/IANA assignment because of binary compatibility
issue. It should be fixed under 5-CURRENT.
- ip6po_m member of struct ip6_pktopts is no longer used. But, it
is still there because of binary compatibility issue. It should
be removed under 5-CURRENT.
Reviewed by: itojun
Obtained from: KAME
MFC after: 3 weeks
Diffstat (limited to 'sbin/ping6')
-rw-r--r-- | sbin/ping6/ping6.8 | 65 | ||||
-rw-r--r-- | sbin/ping6/ping6.c | 1601 |
2 files changed, 1216 insertions, 450 deletions
diff --git a/sbin/ping6/ping6.8 b/sbin/ping6/ping6.8 index 6f8855d..eb3251c 100644 --- a/sbin/ping6/ping6.8 +++ b/sbin/ping6/ping6.8 @@ -1,4 +1,4 @@ -.\" $KAME: ping6.8,v 1.22 2000/05/31 17:00:07 itojun Exp $ +.\" $KAME: ping6.8,v 1.39 2001/04/04 00:08:34 itojun Exp $ .\" .\" Copyright (C) 1995, 1996, 1997, and 1998 WIDE Project. .\" All rights reserved. @@ -40,9 +40,9 @@ packets to network hosts .Sh SYNOPSIS .Nm .\" without ipsec, or new ipsec -.Op Fl dfHnNqRvw +.Op Fl dfHnNqRtvw .\" old ipsec -.\" .Op Fl AdEfnNqRvw +.\" .Op Fl AdEfnNqRtvw .Bk -words .Op Fl a Ar addrtype .Ek @@ -184,13 +184,13 @@ option. If .Ar preload is specified, -.Nm ping +.Nm sends that many packets as fast as possible before falling into its normal mode of behavior. Only the super-user may use this option. .It Fl n Numeric output only. -No attempt will be made to lookup symbolic names for host addresses. +No attempt will be made to lookup symbolic names from addresses in the reply. .It Fl N Probe node information multicast group .Pq Li ff02::2:xxxx:xxxx . @@ -213,11 +213,6 @@ For example, .Dq Li \-p ff will cause the sent packet to be filled with all ones. -.Fl Q -flag, -.Nm -prints out any ICMP error messages caused by its own ECHO_REQUEST -messages. .\" new ipsec .It Fl P Ar policy .Ar policy @@ -259,6 +254,13 @@ header data. You may need to specify .Fl b as well to extend socket buffer size. +.It Fl t +Generate ICMPv6 Node Information supported query types query, +rather than echo-request. +.Fl s +has no effect if +.Fl t +is specified. .It Fl v Verbose output. .Tn ICMP @@ -266,7 +268,7 @@ packets other than .Tn ECHO_RESPONSE that are received are listed. .It Fl w -Generate ICMPv6 Node Information FQDN query, rather than echo-request. +Generate ICMPv6 Node Information DNS Name query, rather than echo-request. .Fl s has no effect if .Fl w @@ -335,7 +337,7 @@ during normal operations or from automated scripts. .\" If less than eight bytes of pad are specified, no round trip times are .\" given. .Sh DUPLICATE AND DAMAGED PACKETS -.Nm Ping6 +.Nm will report duplicate and damaged packets. Duplicate packets should never occur when pinging a unicast address, and seem to be caused by @@ -350,7 +352,7 @@ to the same request. .Pp Damaged packets are obviously serious cause for alarm and often indicate broken hardware somewhere in the -.Nm ping +.Nm packet's path .Pq in the network or in the hosts . .Sh TRYING DIFFERENT DATA PATTERNS @@ -386,8 +388,39 @@ option of .Nm returns 0 on success (the host is alive), and non-zero if the arguments are incorrect or the host is not responding. +.Sh EXAMPLES +Normally, +.Xr ping6 8 +works just like +.Xr ping 8 +would work; the following will send ICMPv6 echo request to +.Li dst.foo.com . +.Bd -literal -offset indent +ping6 -n dst.foo.com +.Ed +.Pp +The following will probe hostnames for all nodes on the network link attached to +.Li wi0 +interface. +The address +.Li ff02::1 +is named the link-local all-node multicast address, and the packet would +reach every node on the network link. +.Bd -literal -offset indent +ping6 -w ff02::1%wi0 +.Ed +.Pp +The following will probe addresses assigned to the destination node, +.Li dst.foo.com . +.Bd -literal -offset indent +ping6 -a agl dst.foo.com +.Ed +.Pp .Sh SEE ALSO .Xr netstat 1 , +.Xr icmp6 4 , +.Xr inet6 4 , +.Xr ip6 4 , .Xr ifconfig 8 , .Xr ping 8 , .Xr routed 8 , @@ -403,8 +436,8 @@ and non-zero if the arguments are incorrect or the host is not responding. .Rs .%A Matt Crawford .%T "IPv6 Node Information Queries" -.%N draft-ietf-ipngwg-icmp-name-lookups-05.txt -.%D October 22, 1999 +.%N draft-ietf-ipngwg-icmp-name-lookups-07.txt +.%D August 2000 .%O work in progress material .Re .Sh BUGS @@ -438,7 +471,7 @@ option (or something like those) to specify the particular address family. This essentially means that we have two different commands. .Sh HISTORY The -.Nm ping +.Xr ping 8 command appeared in .Bx 4.3 . The diff --git a/sbin/ping6/ping6.c b/sbin/ping6/ping6.c index 3d529b6..e7079a3 100644 --- a/sbin/ping6/ping6.c +++ b/sbin/ping6/ping6.c @@ -1,4 +1,4 @@ -/* $KAME: ping6.c,v 1.54 2000/06/12 16:16:44 itojun Exp $ */ +/* $KAME: ping6.c,v 1.126 2001/05/17 03:39:08 itojun Exp $ */ /* * Copyright (C) 1995, 1996, 1997, and 1998 WIDE Project. @@ -99,8 +99,8 @@ static const char rcsid[] = /* * NOTE: * USE_SIN6_SCOPE_ID assumes that sin6_scope_id has the same semantics - * as IPV6_PKTINFO. Some people object it (sin6_scope_id specifies *link* while - * IPV6_PKTINFO specifies *interface*. Link is defined as collection of + * as IPV6_PKTINFO. Some people object it (sin6_scope_id specifies *link* + * while IPV6_PKTINFO specifies *interface*. Link is defined as collection of * network attached to 1 or more interfaces) */ @@ -116,12 +116,16 @@ static const char rcsid[] = #include <netinet/ip6.h> #include <netinet/icmp6.h> #include <arpa/inet.h> +#include <arpa/nameser.h> #include <netdb.h> #include <ctype.h> #include <err.h> #include <errno.h> #include <fcntl.h> +#if defined(__OpenBSD__) || defined(__NetBSD__) +#include <math.h> +#endif #include <signal.h> #include <stdio.h> #include <stdlib.h> @@ -144,7 +148,8 @@ static const char rcsid[] = #define ICMP6ECHOLEN 8 /* icmp echo header len excluding time */ #define ICMP6ECHOTMLEN sizeof(struct timeval) #define ICMP6_NIQLEN (ICMP6ECHOLEN + 8) -#define ICMP6_NIRLEN (ICMP6ECHOLEN + 12) /* 64 bits of nonce + 32 bits ttl */ +/* FQDN case, 64 bits of nonce + 32 bits ttl */ +#define ICMP6_NIRLEN (ICMP6ECHOLEN + 12) #define EXTRA 256 /* for AH and various other headers. weird. */ #define DEFDATALEN ICMP6ECHOTMLEN #define MAXDATALEN MAXPACKETLEN - IP6LEN - ICMP6ECHOLEN @@ -158,7 +163,6 @@ static const char rcsid[] = #define F_FLOOD 0x0001 #define F_INTERVAL 0x0002 -#define F_NUMERIC 0x0004 #define F_PINGFILLED 0x0008 #define F_QUIET 0x0010 #define F_RROUTE 0x0020 @@ -182,13 +186,16 @@ static const char rcsid[] = #define F_HOSTNAME 0x10000 #define F_FQDNOLD 0x20000 #define F_NIGROUP 0x40000 +#define F_SUPTYPES 0x80000 +#define F_NOMINMTU 0x100000 +#define F_NOUSERDATA (F_NODEADDR | F_FQDN | F_FQDNOLD | F_SUPTYPES) u_int options; #define IN6LEN sizeof(struct in6_addr) #define SA6LEN sizeof(struct sockaddr_in6) -#define DUMMY_PORT 10101 +#define DUMMY_PORT 10101 -#define SIN6(s) ((struct sockaddr_in6 *)(s)) +#define SIN6(s) ((struct sockaddr_in6 *)(s)) /* * MAX_DUP_CHK is the number of bits in received table, i.e. the maximum @@ -199,9 +206,9 @@ u_int options; int mx_dup_ck = MAX_DUP_CHK; char rcvd_tbl[MAX_DUP_CHK / 8]; -struct addrinfo *res; -struct sockaddr_in6 dst; /* who to ping6 */ -struct sockaddr_in6 src; /* src addr of this packet */ +struct addrinfo *res; +struct sockaddr_in6 dst; /* who to ping6 */ +struct sockaddr_in6 src; /* src addr of this packet */ int datalen = DEFDATALEN; int s; /* socket file descriptor */ u_char outpack[MAXPACKETLEN]; @@ -209,21 +216,26 @@ char BSPACE = '\b'; /* characters written for flood */ char DOT = '.'; char *hostname; int ident; /* process id to identify our packets */ +u_int8_t nonce[8]; /* nonce field for node information */ struct in6_addr srcaddr; +int hoplimit = -1; /* hoplimit */ +int pathmtu = 0; /* path MTU for the destination. 0 = unspec. */ /* counters */ long npackets; /* max packets to transmit */ long nreceived; /* # of packets we got back */ long nrepeats; /* number of duplicates */ long ntransmitted; /* sequence # for outbound packets = #sent */ -int interval = 1; /* interval between packets */ -int hoplimit = -1; /* hoplimit */ +struct timeval interval = {1, 0}; /* interval between packets */ /* timing */ int timing; /* flag to do timing */ double tmin = 999999999.0; /* minimum round trip time */ double tmax = 0.0; /* maximum round trip time */ double tsum = 0.0; /* sum of all times, for doing average */ +#if defined(__OpenBSD__) || defined(__NetBSD__) +double tsumsq = 0.0; /* sum of all times squared, for std. dev. */ +#endif /* for node addresses */ u_short naflags; @@ -233,22 +245,38 @@ struct msghdr smsghdr; struct iovec smsgiov; char *scmsg = 0; +volatile int signo; +volatile sig_atomic_t seenalrm; +volatile sig_atomic_t seenint; +#ifdef SIGINFO +volatile sig_atomic_t seeninfo; +#endif + int main __P((int, char *[])); void fill __P((char *, char *)); int get_hoplim __P((struct msghdr *)); +int get_pathmtu __P((struct msghdr *)); +void set_pathmtu __P((int)); struct in6_pktinfo *get_rcvpktinfo __P((struct msghdr *)); -void onalrm __P((int)); -void oninfo __P((int)); +void onsignal __P((int)); +void retransmit __P((void)); void onint __P((int)); +size_t pingerlen __P((void)); void pinger __P((void)); -const char *pr_addr __P((struct sockaddr_in6 *)); +const char *pr_addr __P((struct sockaddr *, int)); void pr_icmph __P((struct icmp6_hdr *, u_char *)); void pr_iph __P((struct ip6_hdr *)); +void pr_suptypes __P((struct icmp6_nodeinfo *, size_t)); void pr_nodeaddr __P((struct icmp6_nodeinfo *, int)); +int myechoreply __P((const struct icmp6_hdr *)); +int mynireply __P((const struct icmp6_nodeinfo *)); +char *dnsdecode __P((const u_char **, const u_char *, const u_char *, + u_char *, size_t)); void pr_pack __P((u_char *, int, struct msghdr *)); void pr_exthdrs __P((struct msghdr *)); void pr_ip6opt __P((void *)); void pr_rthdr __P((void *)); +int pr_bitrange __P((u_int32_t, int, int)); void pr_retip __P((struct ip6_hdr *, u_char *)); void summary __P((void)); void tvsub __P((struct timeval *, struct timeval *)); @@ -263,7 +291,7 @@ main(argc, argv) { struct itimerval itimer; struct sockaddr_in6 from; - struct timeval timeout; + struct timeval timeout, *tv; struct addrinfo hints; fd_set *fdmaskp; int fdmasks; @@ -283,6 +311,8 @@ main(argc, argv) char *policy_in = NULL; char *policy_out = NULL; #endif + double intval; + size_t rthlen; /* just to be sure */ memset(&smsghdr, 0, sizeof(&smsghdr)); @@ -291,54 +321,62 @@ main(argc, argv) preload = 0; datap = &outpack[ICMP6ECHOLEN + ICMP6ECHOTMLEN]; #ifndef IPSEC - while ((ch = getopt(argc, argv, "a:b:c:dfHh:I:i:l:nNp:qRS:s:vwW")) != EOF) +#define ADDOPTS #else #ifdef IPSEC_POLICY_IPSEC - while ((ch = getopt(argc, argv, "a:b:c:dfHh:I:i:l:nNp:qRS:s:vwWP:")) != EOF) +#define ADDOPTS "P:" #else - while ((ch = getopt(argc, argv, "a:b:c:dfHh:I:i:l:nNp:qRS:s:vwWAE")) != EOF) +#define ADDOPTS "AE" #endif /*IPSEC_POLICY_IPSEC*/ #endif - { - switch(ch) { - case 'a': - { - char *cp; - - options |= F_NODEADDR; - datalen = 2048; /* XXX: enough? */ - for (cp = optarg; *cp != '\0'; cp++) { - switch(*cp) { - case 'a': - naflags |= NI_NODEADDR_FLAG_ALL; - break; - case 'c': - case 'C': - naflags |= NI_NODEADDR_FLAG_COMPAT; - break; - case 'l': - case 'L': - naflags |= NI_NODEADDR_FLAG_LINKLOCAL; - break; - case 's': - case 'S': - naflags |= NI_NODEADDR_FLAG_SITELOCAL; - break; - case 'g': - case 'G': - naflags |= NI_NODEADDR_FLAG_GLOBAL; - break; - case 'A': /* experimental. not in the spec */ - naflags |= NI_NODEADDR_FLAG_ANYCAST; - break; - default: - usage(); - /*NOTREACHED*/ - } - } - break; - } - case 'b': + while ((ch = getopt(argc, argv, + "a:b:c:dfHh:I:i:l:mnNp:qRS:s:tvwW" ADDOPTS)) != -1) { +#undef ADDOPTS + switch (ch) { + case 'a': + { + char *cp; + + options &= ~F_NOUSERDATA; + options |= F_NODEADDR; + for (cp = optarg; *cp != '\0'; cp++) { + switch (*cp) { + case 'a': + naflags |= NI_NODEADDR_FLAG_ALL; + break; + case 'c': + case 'C': + naflags |= NI_NODEADDR_FLAG_COMPAT; + break; + case 'l': + case 'L': + naflags |= NI_NODEADDR_FLAG_LINKLOCAL; + break; + case 's': + case 'S': + naflags |= NI_NODEADDR_FLAG_SITELOCAL; + break; + case 'g': + case 'G': + naflags |= NI_NODEADDR_FLAG_GLOBAL; + break; + case 'A': /* experimental. not in the spec */ +#ifdef NI_NODEADDR_FLAG_ANYCAST + naflags |= NI_NODEADDR_FLAG_ANYCAST; + break; +#else + errx(1, +"-a A is not supported on the platform"); + /*NOTREACHED*/ +#endif + default: + usage(); + /*NOTREACHED*/ + } + } + break; + } + case 'b': #if defined(SO_SNDBUF) && defined(SO_RCVBUF) sockbufsize = atoi(optarg); #else @@ -380,10 +418,23 @@ main(argc, argv) #endif break; case 'i': /* wait between sending packets */ - interval = strtol(optarg, &e, 10); - if (interval <= 0 || *optarg == '\0' || *e != '\0') - errx(1, - "illegal timing interval -- %s", optarg); + intval = strtod(optarg, &e); + if (*optarg == '\0' || *e != '\0') + errx(1, "illegal timing interval %s", optarg); + if (intval < 1 && getuid()) { + errx(1, "%s: only root may use interval < 1s", + strerror(EPERM)); + } + interval.tv_sec = (long)intval; + interval.tv_usec = + (long)((intval - interval.tv_sec) * 1000000); + if (interval.tv_sec < 0) + errx(1, "illegal timing interval %s", optarg); + /* less than 1/hz does not make sense */ + if (interval.tv_sec == 0 && interval.tv_usec < 10000) { + warnx("too small interval, raised to 0.01"); + interval.tv_usec = 10000; + } options |= F_INTERVAL; break; case 'l': @@ -395,8 +446,16 @@ main(argc, argv) if (preload < 0 || *optarg == '\0' || *e != '\0') errx(1, "illegal preload value -- %s", optarg); break; + case 'm': +#ifdef IPV6_USE_MIN_MTU + options |= F_NOMINMTU; + break; +#else + errx(1, "-%c is not supported on this platform", ch); + /*NOTREACHED*/ +#endif case 'n': - options |= F_NUMERIC; + options &= ~F_HOSTNAME; break; case 'N': options |= F_NIGROUP; @@ -426,18 +485,25 @@ main(argc, argv) datalen = strtol(optarg, &e, 10); if (datalen <= 0 || *optarg == '\0' || *e != '\0') errx(1, "illegal datalen value -- %s", optarg); - if (datalen > MAXDATALEN) + if (datalen > MAXDATALEN) { errx(1, "datalen value too large, maximum is %d", MAXDATALEN); + } + break; + case 't': + options &= ~F_NOUSERDATA; + options |= F_SUPTYPES; break; case 'v': options |= F_VERBOSE; break; case 'w': + options &= ~F_NOUSERDATA; options |= F_FQDN; break; case 'W': + options &= ~F_NOUSERDATA; options |= F_FQDNOLD; break; #ifdef IPSEC @@ -476,11 +542,17 @@ main(argc, argv) } if (argc > 1) { -#ifdef USE_SIN6_SCOPE_ID - ip6optlen += CMSG_SPACE(inet6_rth_space(IPV6_RTHDR_TYPE_0, argc - 1)); -#else /* old advanced API */ - ip6optlen += inet6_rthdr_space(IPV6_RTHDR_TYPE_0, argc - 1); +#ifdef IPV6_RECVRTHDR /* 2292bis */ + rthlen = CMSG_SPACE(inet6_rth_space(IPV6_RTHDR_TYPE_0, + argc - 1)); +#else /* RFC2292 */ + rthlen = inet6_rthdr_space(IPV6_RTHDR_TYPE_0, argc - 1); #endif + if (rthlen == 0) { + errx(1, "too many intermediate hops"); + /*NOTREACHED*/ + } + ip6optlen += rthlen; } if (options & F_NIGROUP) { @@ -494,8 +566,7 @@ main(argc, argv) /* getaddrinfo */ bzero(&hints, sizeof(struct addrinfo)); - if ((options & F_NUMERIC) != 0) - hints.ai_flags = AI_CANONNAME; + hints.ai_flags = AI_CANONNAME; hints.ai_family = AF_INET6; hints.ai_socktype = SOCK_RAW; hints.ai_protocol = IPPROTO_ICMPV6; @@ -509,28 +580,91 @@ main(argc, argv) hostname = res->ai_canonname; else hostname = target; - + if (!res->ai_addr) errx(1, "getaddrinfo failed"); (void)memcpy(&dst, res->ai_addr, res->ai_addrlen); + if ((s = socket(res->ai_family, res->ai_socktype, + res->ai_protocol)) < 0) + err(1, "socket"); + + /* + * let the kerel pass extension headers of incoming packets, + * for privileged socket options + */ + if ((options & F_VERBOSE) != 0) { + int opton = 1; + +#ifdef IPV6_RECVHOPOPTS + if (setsockopt(s, IPPROTO_IPV6, IPV6_RECVHOPOPTS, &opton, + sizeof(opton))) + err(1, "setsockopt(IPV6_RECVHOPOPTS)"); +#else /* old adv. API */ + if (setsockopt(s, IPPROTO_IPV6, IPV6_HOPOPTS, &opton, + sizeof(opton))) + err(1, "setsockopt(IPV6_HOPOPTS)"); +#endif +#ifdef IPV6_RECVDSTOPTS + if (setsockopt(s, IPPROTO_IPV6, IPV6_RECVDSTOPTS, &opton, + sizeof(opton))) + err(1, "setsockopt(IPV6_RECVDSTOPTS)"); +#else /* old adv. API */ + if (setsockopt(s, IPPROTO_IPV6, IPV6_DSTOPTS, &opton, + sizeof(opton))) + err(1, "setsockopt(IPV6_DSTOPTS)"); +#endif +#ifdef IPV6_RECVRTHDRDSTOPTS + if (setsockopt(s, IPPROTO_IPV6, IPV6_RECVRTHDRDSTOPTS, &opton, + sizeof(opton))) + err(1, "setsockopt(IPV6_RECVRTHDRDSTOPTS)"); +#endif + } + + /* revoke root privilege */ + seteuid(getuid()); + setuid(getuid()); + if (options & F_FLOOD && options & F_INTERVAL) errx(1, "-f and -i incompatible options"); - if (datalen >= sizeof(struct timeval)) /* can we time transfer */ - timing = 1; - packlen = datalen + IP6LEN + ICMP6ECHOLEN + EXTRA; + if ((options & F_NOUSERDATA) == 0) { + if (datalen >= sizeof(struct timeval)) { + /* we can time transfer */ + timing = 1; + } else + timing = 0; + /* in F_VERBOSE case, we may get non-echoreply packets*/ + if (options & F_VERBOSE) + packlen = 2048 + IP6LEN + ICMP6ECHOLEN + EXTRA; + else + packlen = datalen + IP6LEN + ICMP6ECHOLEN + EXTRA; + } else { + /* suppress timing for node information query */ + timing = 0; + datalen = 2048; + packlen = 2048 + IP6LEN + ICMP6ECHOLEN + EXTRA; + } + if (!(packet = (u_char *)malloc((u_int)packlen))) err(1, "Unable to allocate packet"); if (!(options & F_PINGFILLED)) - for (i = 8; i < datalen; ++i) + for (i = ICMP6ECHOLEN; i < packlen; ++i) *datap++ = i; ident = getpid() & 0xFFFF; - - if ((s = socket(res->ai_family, res->ai_socktype, res->ai_protocol)) < 0) - err(1, "socket"); +#ifndef __OpenBSD__ + gettimeofday(&timeout, NULL); + srand((unsigned int)(timeout.tv_sec ^ timeout.tv_usec ^ (long)ident)); + memset(nonce, 0, sizeof(nonce)); + for (i = 0; i < sizeof(nonce); i += sizeof(int)) + *((int *)&nonce[i]) = rand(); +#else + memset(nonce, 0, sizeof(nonce)); + for (i = 0; i < sizeof(nonce); i += sizeof(u_int32_t)) + *((u_int32_t *)&nonce[i]) = arc4random(); +#endif hold = 1; @@ -540,8 +674,24 @@ main(argc, argv) optval = IPV6_DEFHLIM; if (IN6_IS_ADDR_MULTICAST(&dst.sin6_addr)) if (setsockopt(s, IPPROTO_IPV6, IPV6_MULTICAST_HOPS, - &optval, sizeof(optval)) == -1) + &optval, sizeof(optval)) == -1) err(1, "IPV6_MULTICAST_HOPS"); +#ifdef IPV6_USE_MIN_MTU + if ((options & F_NOMINMTU) == 0) { + optval = 1; + if (setsockopt(s, IPPROTO_IPV6, IPV6_USE_MIN_MTU, + &optval, sizeof(optval)) == -1) + err(1, "setsockopt(IPV6_USE_MIN_MTU)"); + } +#ifdef IPV6_RECVPATHMTU + else { + optval = 1; + if (setsockopt(s, IPPROTO_IPV6, IPV6_RECVPATHMTU, + &optval, sizeof(optval)) == -1) + err(1, "setsockopt(IPV6_RECVPATHMTU)"); + } +#endif /* IPV6_RECVPATHMTU */ +#endif /* IPV6_USE_MIN_MTU */ #ifdef IPSEC #ifdef IPSEC_POLICY_IPSEC @@ -556,18 +706,18 @@ main(argc, argv) optval = IPSEC_LEVEL_REQUIRE; #ifdef IPV6_AUTH_TRANS_LEVEL if (setsockopt(s, IPPROTO_IPV6, IPV6_AUTH_TRANS_LEVEL, - &optval, sizeof(optval)) == -1) + &optval, sizeof(optval)) == -1) err(1, "setsockopt(IPV6_AUTH_TRANS_LEVEL)"); #else /* old def */ if (setsockopt(s, IPPROTO_IPV6, IPV6_AUTH_LEVEL, - &optval, sizeof(optval)) == -1) + &optval, sizeof(optval)) == -1) err(1, "setsockopt(IPV6_AUTH_LEVEL)"); #endif } if (options & F_ENCRYPT) { optval = IPSEC_LEVEL_REQUIRE; if (setsockopt(s, IPPROTO_IPV6, IPV6_ESP_TRANS_LEVEL, - &optval, sizeof(optval)) == -1) + &optval, sizeof(optval)) == -1) err(1, "setsockopt(IPV6_ESP_TRANS_LEVEL)"); } #endif /*IPSEC_POLICY_IPSEC*/ @@ -579,7 +729,7 @@ main(argc, argv) if (!(options & F_VERBOSE)) { ICMP6_FILTER_SETBLOCKALL(&filt); if ((options & F_FQDN) || (options & F_FQDNOLD) || - (options & F_NODEADDR)) + (options & F_NODEADDR) || (options & F_SUPTYPES)) ICMP6_FILTER_SETPASS(ICMP6_NI_REPLY, &filt); else ICMP6_FILTER_SETPASS(ICMP6_ECHO_REPLY, &filt); @@ -587,55 +737,31 @@ main(argc, argv) ICMP6_FILTER_SETPASSALL(&filt); } if (setsockopt(s, IPPROTO_ICMPV6, ICMP6_FILTER, &filt, - sizeof(filt)) < 0) + sizeof(filt)) < 0) err(1, "setsockopt(ICMP6_FILTER)"); } #endif /*ICMP6_FILTER*/ /* let the kerel pass extension headers of incoming packets */ - /* TODO: implement parsing routine */ if ((options & F_VERBOSE) != 0) { int opton = 1; #ifdef IPV6_RECVRTHDR if (setsockopt(s, IPPROTO_IPV6, IPV6_RECVRTHDR, &opton, - sizeof(opton))) + sizeof(opton))) err(1, "setsockopt(IPV6_RECVRTHDR)"); #else /* old adv. API */ if (setsockopt(s, IPPROTO_IPV6, IPV6_RTHDR, &opton, - sizeof(opton))) + sizeof(opton))) err(1, "setsockopt(IPV6_RTHDR)"); #endif -#ifdef IPV6_RECVHOPOPTS - if (setsockopt(s, IPPROTO_IPV6, IPV6_RECVHOPOPTS, &opton, - sizeof(opton))) - err(1, "setsockopt(IPV6_RECVHOPOPTS)"); -#else /* old adv. API */ - if (setsockopt(s, IPPROTO_IPV6, IPV6_HOPOPTS, &opton, - sizeof(opton))) - err(1, "setsockopt(IPV6_HOPOPTS)"); -#endif -#ifdef IPV6_RECVDSTOPTS - if (setsockopt(s, IPPROTO_IPV6, IPV6_RECVDSTOPTS, &opton, - sizeof(opton))) - err(1, "setsockopt(IPV6_RECVDSTOPTS)"); -#else /* olad adv. API */ - if (setsockopt(s, IPPROTO_IPV6, IPV6_DSTOPTS, &opton, - sizeof(opton))) - err(1, "setsockopt(IPV6_DSTOPTS)"); -#endif -#ifdef IPV6_RECVRTHDRDSTOPTS - if (setsockopt(s, IPPROTO_IPV6, IPV6_RECVRTHDRDSTOPTS, &opton, - sizeof(opton))) - err(1, "setsockopt(IPV6_RECVRTHDRDSTOPTS)"); -#endif } /* optval = 1; if (IN6_IS_ADDR_MULTICAST(&dst.sin6_addr)) if (setsockopt(s, IPPROTO_IPV6, IPV6_MULTICAST_LOOP, - &optval, sizeof(optval)) == -1) + &optval, sizeof(optval)) == -1) err(1, "IPV6_MULTICAST_LOOP"); */ @@ -673,7 +799,7 @@ main(argc, argv) #ifndef USE_SIN6_SCOPE_ID /* pktinfo must have already been allocated */ if ((pktinfo->ipi6_ifindex = if_nametoindex(ifname)) == 0) - errx(1, "%s: invalid interface name", ifname); + errx(1, "%s: invalid interface name", ifname); #else if ((dst.sin6_scope_id = if_nametoindex(ifname)) == 0) errx(1, "%s: invalid interface name", ifname); @@ -714,34 +840,36 @@ main(argc, argv) scmsgp->cmsg_type = IPV6_RTHDR; rthdr = (struct ip6_rthdr *)CMSG_DATA(scmsgp); rthdr = inet6_rth_init((void *)rthdr, rthdrlen, - IPV6_RTHDR_TYPE_0, argc - 1); + IPV6_RTHDR_TYPE_0, argc - 1); if (rthdr == NULL) errx(1, "can't initialize rthdr"); #else /* old advanced API */ if ((scmsgp = (struct cmsghdr *)inet6_rthdr_init(scmsgp, - IPV6_RTHDR_TYPE_0)) == 0) + IPV6_RTHDR_TYPE_0)) == 0) errx(1, "can't initialize rthdr"); #endif /* USE_RFC2292BIS */ for (hops = 0; hops < argc - 1; hops++) { struct addrinfo *iaip; - if ((error = getaddrinfo(argv[hops], NULL, &hints, &iaip))) + if ((error = getaddrinfo(argv[hops], NULL, &hints, + &iaip))) errx(1, "%s", gai_strerror(error)); - if (SIN6(res->ai_addr)->sin6_family != AF_INET6) + if (SIN6(iaip->ai_addr)->sin6_family != AF_INET6) errx(1, - "bad addr family of an intermediate addr"); + "bad addr family of an intermediate addr"); #ifdef USE_RFC2292BIS if (inet6_rth_add(rthdr, - &(SIN6(iaip->ai_addr))->sin6_addr)) + &(SIN6(iaip->ai_addr))->sin6_addr)) errx(1, "can't add an intermediate node"); #else /* old advanced API */ if (inet6_rthdr_add(scmsgp, - &(SIN6(iaip->ai_addr))->sin6_addr, - IPV6_RTHDR_LOOSE)) + &(SIN6(iaip->ai_addr))->sin6_addr, + IPV6_RTHDR_LOOSE)) errx(1, "can't add an intermediate node"); #endif /* USE_RFC2292BIS */ + freeaddrinfo(iaip); } #ifndef USE_RFC2292BIS @@ -757,7 +885,7 @@ main(argc, argv) * source selection */ int dummy, len = sizeof(src); - + if ((dummy = socket(AF_INET6, SOCK_DGRAM, 0)) < 0) err(1, "UDP socket"); @@ -766,38 +894,31 @@ main(argc, argv) src.sin6_port = ntohs(DUMMY_PORT); src.sin6_scope_id = dst.sin6_scope_id; - -#ifdef USE_SIN6_SCOPE_ID - src.sin6_scope_id = dst.sin6_scope_id; -#endif - #ifdef USE_RFC2292BIS if (pktinfo && setsockopt(dummy, IPPROTO_IPV6, IPV6_PKTINFO, - (void *)pktinfo, sizeof(*pktinfo))) + (void *)pktinfo, sizeof(*pktinfo))) err(1, "UDP setsockopt(IPV6_PKTINFO)"); if (hoplimit != -1 && setsockopt(dummy, IPPROTO_IPV6, IPV6_HOPLIMIT, - (void *)&hoplimit, sizeof(hoplimit))) + (void *)&hoplimit, sizeof(hoplimit))) err(1, "UDP setsockopt(IPV6_HOPLIMIT)"); if (rthdr && setsockopt(dummy, IPPROTO_IPV6, IPV6_RTHDR, - (void *)rthdr, (rthdr->ip6r_len + 1) << 3)) + (void *)rthdr, (rthdr->ip6r_len + 1) << 3)) err(1, "UDP setsockopt(IPV6_RTHDR)"); #else /* old advanced API */ if (smsghdr.msg_control && setsockopt(dummy, IPPROTO_IPV6, IPV6_PKTOPTIONS, - (void *)smsghdr.msg_control, - smsghdr.msg_controllen)) { + (void *)smsghdr.msg_control, smsghdr.msg_controllen)) err(1, "UDP setsockopt(IPV6_PKTOPTIONS)"); - } #endif - + if (connect(dummy, (struct sockaddr *)&src, len) < 0) err(1, "UDP connect"); - + if (getsockname(dummy, (struct sockaddr *)&src, &len) < 0) err(1, "getsockname"); @@ -809,10 +930,10 @@ main(argc, argv) if (datalen > sockbufsize) warnx("you need -b to increase socket buffer size"); if (setsockopt(s, SOL_SOCKET, SO_SNDBUF, &sockbufsize, - sizeof(sockbufsize)) < 0) + sizeof(sockbufsize)) < 0) err(1, "setsockopt(SO_SNDBUF)"); if (setsockopt(s, SOL_SOCKET, SO_RCVBUF, &sockbufsize, - sizeof(sockbufsize)) < 0) + sizeof(sockbufsize)) < 0) err(1, "setsockopt(SO_RCVBUF)"); } else { @@ -825,7 +946,8 @@ main(argc, argv) * to get some stuff for /etc/ethers. */ hold = 48 * 1024; - setsockopt(s, SOL_SOCKET, SO_RCVBUF, (char *)&hold, sizeof(hold)); + setsockopt(s, SOL_SOCKET, SO_RCVBUF, (char *)&hold, + sizeof(hold)); } #endif @@ -833,64 +955,99 @@ main(argc, argv) #ifndef USE_SIN6_SCOPE_ID #ifdef IPV6_RECVPKTINFO if (setsockopt(s, IPPROTO_IPV6, IPV6_RECVPKTINFO, &optval, - sizeof(optval)) < 0) + sizeof(optval)) < 0) warn("setsockopt(IPV6_RECVPKTINFO)"); /* XXX err? */ #else /* old adv. API */ if (setsockopt(s, IPPROTO_IPV6, IPV6_PKTINFO, &optval, - sizeof(optval)) < 0) + sizeof(optval)) < 0) warn("setsockopt(IPV6_PKTINFO)"); /* XXX err? */ #endif #endif /* USE_SIN6_SCOPE_ID */ #ifdef IPV6_RECVHOPLIMIT if (setsockopt(s, IPPROTO_IPV6, IPV6_RECVHOPLIMIT, &optval, - sizeof(optval)) < 0) + sizeof(optval)) < 0) warn("setsockopt(IPV6_RECVHOPLIMIT)"); /* XXX err? */ #else /* old adv. API */ if (setsockopt(s, IPPROTO_IPV6, IPV6_HOPLIMIT, &optval, - sizeof(optval)) < 0) + sizeof(optval)) < 0) warn("setsockopt(IPV6_HOPLIMIT)"); /* XXX err? */ #endif - printf("PING6(%d=40+8+%d bytes) ", datalen + 48, datalen); - printf("%s --> ", pr_addr(&src)); - printf("%s\n", pr_addr(&dst)); + printf("PING6(%lu=40+8+%lu bytes) ", (unsigned long)(40 + pingerlen()), + (unsigned long)(pingerlen() - 8)); + printf("%s --> ", pr_addr((struct sockaddr *)&src, sizeof(src))); + printf("%s\n", pr_addr((struct sockaddr *)&dst, sizeof(dst))); while (preload--) /* Fire off them quickies. */ pinger(); - (void)signal(SIGINT, onint); - (void)signal(SIGINFO, oninfo); + (void)signal(SIGINT, onsignal); +#ifdef SIGINFO + (void)signal(SIGINFO, onsignal); +#endif if ((options & F_FLOOD) == 0) { - (void)signal(SIGALRM, onalrm); - itimer.it_interval.tv_sec = interval; - itimer.it_interval.tv_usec = 0; - itimer.it_value.tv_sec = 0; - itimer.it_value.tv_usec = 1; + (void)signal(SIGALRM, onsignal); + itimer.it_interval = interval; + itimer.it_value = interval; (void)setitimer(ITIMER_REAL, &itimer, NULL); + retransmit(); } - fdmasks = howmany(s+1, NFDBITS); + fdmasks = howmany(s + 1, NFDBITS) * sizeof(fd_mask); if ((fdmaskp = malloc(fdmasks)) == NULL) err(1, "malloc"); + signo = seenalrm = seenint = 0; +#ifdef SIGINFO + seeninfo = 0; +#endif + for (;;) { struct msghdr m; struct cmsghdr *cm; u_char buf[1024]; struct iovec iov[2]; + /* signal handling */ + if (seenalrm) { + retransmit(); + seenalrm = 0; + continue; + } + if (seenint) { + onint(SIGINT); + seenint = 0; + continue; + } +#ifdef SIGINFO + if (seeninfo) { + summary(); + seeninfo = 0; + continue; + } +#endif + if (options & F_FLOOD) { pinger(); timeout.tv_sec = 0; timeout.tv_usec = 10000; - memset(fdmaskp, 0, fdmasks); - FD_SET(s, fdmaskp); - if (select(s + 1, fdmaskp, NULL, NULL, &timeout) < 1) - continue; - } - fromlen = sizeof(from); + tv = &timeout; + } else + tv = NULL; + memset(fdmaskp, 0, fdmasks); + FD_SET(s, fdmaskp); + cc = select(s + 1, fdmaskp, NULL, NULL, tv); + if (cc < 0) { + if (errno != EINTR) { + warn("select"); + sleep(1); + } + continue; + } else if (cc == 0) + continue; + fromlen = sizeof(from); m.msg_name = (caddr_t)&from; m.msg_namelen = sizeof(from); memset(&iov, 0, sizeof(iov)); @@ -902,14 +1059,35 @@ main(argc, argv) m.msg_control = (caddr_t)buf; m.msg_controllen = sizeof(buf); - if ((cc = recvmsg(s, &m, 0)) < 0) { - if (errno == EINTR) - continue; - warn("recvfrom"); + cc = recvmsg(s, &m, 0); + if (cc < 0) { + if (errno != EINTR) { + warn("recvmsg"); + sleep(1); + } + continue; + } else if (cc == 0) { + int mtu; + + /* + * receive control messages only. Process the + * exceptions (currently the only possiblity is + * a path MTU notification.) + */ + if ((mtu = get_pathmtu(&m)) > 0) { + if ((options & F_VERBOSE) != 0) { + printf("new path MTU (%d) is " + "notified\n", mtu); + } + set_pathmtu(mtu); + } continue; + } else { + /* + * an ICMPv6 message (probably an echoreply) arrived. + */ + pr_pack(packet, cc, &m); } - - pr_pack(packet, cc, &m); if (npackets && nreceived >= npackets) break; } @@ -917,14 +1095,32 @@ main(argc, argv) exit(nreceived == 0); } +void +onsignal(sig) + int sig; +{ + signo = sig; + switch (sig) { + case SIGALRM: + seenalrm++; + break; + case SIGINT: + seenint++; + break; +#ifdef SIGINFO + case SIGINFO: + seeninfo++; + break; +#endif + } +} + /* - * onalrm -- + * retransmit -- * This routine transmits another ping6. */ -/* ARGSUSED */ void -onalrm(signo) - int signo; +retransmit() { struct itimerval itimer; @@ -961,32 +1157,51 @@ onalrm(signo) * of the data portion are used to hold a UNIX "timeval" struct in VAX * byte-order, to compute the round-trip time. */ +size_t +pingerlen() +{ + size_t l; + + if (options & F_FQDN) + l = ICMP6_NIQLEN + sizeof(dst.sin6_addr); + else if (options & F_FQDNOLD) + l = ICMP6_NIQLEN; + else if (options & F_NODEADDR) + l = ICMP6_NIQLEN + sizeof(dst.sin6_addr); + else if (options & F_SUPTYPES) + l = ICMP6_NIQLEN; + else + l = ICMP6ECHOLEN + datalen; + + return l; +} + void pinger() { struct icmp6_hdr *icp; struct iovec iov[2]; int i, cc; + struct icmp6_nodeinfo *nip; + int seq; icp = (struct icmp6_hdr *)outpack; + nip = (struct icmp6_nodeinfo *)outpack; memset(icp, 0, sizeof(*icp)); - icp->icmp6_code = 0; icp->icmp6_cksum = 0; - icp->icmp6_seq = ntransmitted++; /* htons later */ - icp->icmp6_id = htons(ident); /* ID */ - - CLR(icp->icmp6_seq % mx_dup_ck); - icp->icmp6_seq = htons(icp->icmp6_seq); + seq = ntransmitted++; + CLR(seq % mx_dup_ck); if (options & F_FQDN) { icp->icmp6_type = ICMP6_NI_QUERY; icp->icmp6_code = ICMP6_NI_SUBJ_IPV6; - /* XXX: overwrite icmp6_id */ - ((struct icmp6_nodeinfo *)icp)->ni_qtype = htons(NI_QTYPE_FQDN); - ((struct icmp6_nodeinfo *)icp)->ni_flags = htons(0); - if (timing) - (void)gettimeofday((struct timeval *) - &outpack[ICMP6ECHOLEN], NULL); + nip->ni_qtype = htons(NI_QTYPE_FQDN); + nip->ni_flags = htons(0); + + memcpy(nip->icmp6_ni_nonce, nonce, + sizeof(nip->icmp6_ni_nonce)); + *(u_int16_t *)nip->icmp6_ni_nonce = ntohs(seq); + memcpy(&outpack[ICMP6_NIQLEN], &dst.sin6_addr, sizeof(dst.sin6_addr)); cc = ICMP6_NIQLEN + sizeof(dst.sin6_addr); @@ -994,39 +1209,58 @@ pinger() } else if (options & F_FQDNOLD) { /* packet format in 03 draft - no Subject data on queries */ icp->icmp6_type = ICMP6_NI_QUERY; - /* code field is always 0 */ - /* XXX: overwrite icmp6_id */ - ((struct icmp6_nodeinfo *)icp)->ni_qtype = htons(NI_QTYPE_FQDN); - ((struct icmp6_nodeinfo *)icp)->ni_flags = htons(0); - if (timing) - (void)gettimeofday((struct timeval *) - &outpack[ICMP6ECHOLEN], NULL); + icp->icmp6_code = 0; /* code field is always 0 */ + nip->ni_qtype = htons(NI_QTYPE_FQDN); + nip->ni_flags = htons(0); + + memcpy(nip->icmp6_ni_nonce, nonce, + sizeof(nip->icmp6_ni_nonce)); + *(u_int16_t *)nip->icmp6_ni_nonce = ntohs(seq); + cc = ICMP6_NIQLEN; datalen = 0; } else if (options & F_NODEADDR) { icp->icmp6_type = ICMP6_NI_QUERY; icp->icmp6_code = ICMP6_NI_SUBJ_IPV6; - /* XXX: overwrite icmp6_id */ - ((struct icmp6_nodeinfo *)icp)->ni_qtype = - htons(NI_QTYPE_NODEADDR); - ((struct icmp6_nodeinfo *)icp)->ni_flags = htons(0); - if (timing) - (void)gettimeofday((struct timeval *) - &outpack[ICMP6ECHOLEN], NULL); + nip->ni_qtype = htons(NI_QTYPE_NODEADDR); + nip->ni_flags = naflags; + + memcpy(nip->icmp6_ni_nonce, nonce, + sizeof(nip->icmp6_ni_nonce)); + *(u_int16_t *)nip->icmp6_ni_nonce = ntohs(seq); + memcpy(&outpack[ICMP6_NIQLEN], &dst.sin6_addr, sizeof(dst.sin6_addr)); cc = ICMP6_NIQLEN + sizeof(dst.sin6_addr); datalen = 0; - ((struct icmp6_nodeinfo *)icp)->ni_flags = naflags; - } - else { + } else if (options & F_SUPTYPES) { + icp->icmp6_type = ICMP6_NI_QUERY; + icp->icmp6_code = ICMP6_NI_SUBJ_FQDN; /*empty*/ + nip->ni_qtype = htons(NI_QTYPE_SUPTYPES); + /* we support compressed bitmap */ + nip->ni_flags = NI_SUPTYPE_FLAG_COMPRESS; + + memcpy(nip->icmp6_ni_nonce, nonce, + sizeof(nip->icmp6_ni_nonce)); + *(u_int16_t *)nip->icmp6_ni_nonce = ntohs(seq); + cc = ICMP6_NIQLEN; + datalen = 0; + } else { icp->icmp6_type = ICMP6_ECHO_REQUEST; + icp->icmp6_code = 0; + icp->icmp6_id = htons(ident); + icp->icmp6_seq = ntohs(seq); if (timing) (void)gettimeofday((struct timeval *) &outpack[ICMP6ECHOLEN], NULL); cc = ICMP6ECHOLEN + datalen; } +#ifdef DIAGNOSTIC + if (pingerlen() != cc) + errx(1, "internal error; length mismatch"); +#endif + smsghdr.msg_name = (caddr_t)&dst; smsghdr.msg_namelen = sizeof(dst); memset(&iov, 0, sizeof(iov)); @@ -1047,6 +1281,91 @@ pinger() (void)write(STDOUT_FILENO, &DOT, 1); } +int +myechoreply(icp) + const struct icmp6_hdr *icp; +{ + if (ntohs(icp->icmp6_id) == ident) + return 1; + else + return 0; +} + +int +mynireply(nip) + const struct icmp6_nodeinfo *nip; +{ + if (memcmp(nip->icmp6_ni_nonce + sizeof(u_int16_t), + nonce + sizeof(u_int16_t), + sizeof(nonce) - sizeof(u_int16_t)) == 0) + return 1; + else + return 0; +} + +char * +dnsdecode(sp, ep, base, buf, bufsiz) + const u_char **sp; + const u_char *ep; + const u_char *base; /*base for compressed name*/ + u_char *buf; + size_t bufsiz; +{ + int i; + const u_char *cp; + char cresult[MAXDNAME + 1]; + const u_char *comp; + int l; + + cp = *sp; + *buf = '\0'; + + if (cp >= ep) + return NULL; + while (cp < ep) { + i = *cp; + if (i == 0 || cp != *sp) { + if (strlcat(buf, ".", bufsiz) >= bufsiz) + return NULL; /*result overrun*/ + } + if (i == 0) + break; + cp++; + + if ((i & 0xc0) == 0xc0 && cp - base > (i & 0x3f)) { + /* DNS compression */ + if (!base) + return NULL; + + comp = base + (i & 0x3f); + if (dnsdecode(&comp, cp, base, cresult, + sizeof(cresult)) == NULL) + return NULL; + if (strlcat(buf, cresult, bufsiz) >= bufsiz) + return NULL; /*result overrun*/ + break; + } else if ((i & 0x3f) == i) { + if (i > ep - cp) + return NULL; /*source overrun*/ + while (i-- > 0 && cp < ep) { + l = snprintf(cresult, sizeof(cresult), + isprint(*cp) ? "%c" : "\\%03o", *cp & 0xff); + if (l >= sizeof(cresult)) + return NULL; + if (strlcat(buf, cresult, bufsiz) >= bufsiz) + return NULL; /*result overrun*/ + cp++; + } + } else + return NULL; /*invalid label*/ + } + if (i != 0) + return NULL; /*not terminated*/ + cp++; + *sp = cp; + return buf; +} + /* * pr_pack -- * Print out the packet, if it came from us. This logic is necessary @@ -1062,9 +1381,11 @@ pr_pack(buf, cc, mhdr) { #define safeputc(c) printf((isprint((c)) ? "%c" : "\\%03o"), c) struct icmp6_hdr *icp; + struct icmp6_nodeinfo *ni; int i; int hoplim; - struct sockaddr_in6 *from = (struct sockaddr_in6 *)mhdr->msg_name; + struct sockaddr *from; + int fromlen; u_char *cp = NULL, *dp, *end = buf + cc; struct in6_pktinfo *pktinfo = NULL; struct timeval tv, *tp; @@ -1072,16 +1393,28 @@ pr_pack(buf, cc, mhdr) int dupflag; size_t off; int oldfqdn; + u_int16_t seq; + char dnsname[MAXDNAME + 1]; (void)gettimeofday(&tv, NULL); + 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\n"); + 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\n", cc, - pr_addr(from)); + pr_addr(from, fromlen)); return; } icp = (struct icmp6_hdr *)buf; + ni = (struct icmp6_nodeinfo *)buf; off = 0; if ((hoplim = get_hoplim(mhdr)) == -1) { @@ -1093,11 +1426,8 @@ pr_pack(buf, cc, mhdr) return; } - if (icp->icmp6_type == ICMP6_ECHO_REPLY) { - /* XXX the following line overwrites the original packet */ - icp->icmp6_seq = ntohs(icp->icmp6_seq); - if (ntohs(icp->icmp6_id) != ident) - return; /* It was not our ECHO */ + if (icp->icmp6_type == ICMP6_ECHO_REPLY && myechoreply(icp)) { + seq = ntohs(icp->icmp6_seq); ++nreceived; if (timing) { tp = (struct timeval *)(icp + 1); @@ -1105,18 +1435,21 @@ pr_pack(buf, cc, mhdr) triptime = ((double)tv.tv_sec) * 1000.0 + ((double)tv.tv_usec) / 1000.0; tsum += triptime; +#if defined(__OpenBSD__) || defined(__NetBSD__) + tsumsq += triptime * triptime; +#endif if (triptime < tmin) tmin = triptime; if (triptime > tmax) tmax = triptime; } - if (TST(icp->icmp6_seq % mx_dup_ck)) { + if (TST(seq % mx_dup_ck)) { ++nrepeats; --nreceived; dupflag = 1; } else { - SET(icp->icmp6_seq % mx_dup_ck); + SET(seq % mx_dup_ck); dupflag = 0; } @@ -1127,18 +1460,21 @@ pr_pack(buf, cc, mhdr) (void)write(STDOUT_FILENO, &BSPACE, 1); else { (void)printf("%d bytes from %s, icmp_seq=%u", cc, - pr_addr(from), - icp->icmp6_seq); + pr_addr(from, fromlen), seq); (void)printf(" hlim=%d", hoplim); if ((options & F_VERBOSE) != 0) { struct sockaddr_in6 dstsa; memset(&dstsa, 0, sizeof(dstsa)); dstsa.sin6_family = AF_INET6; +#ifdef SIN6_LEN dstsa.sin6_len = sizeof(dstsa); +#endif dstsa.sin6_scope_id = pktinfo->ipi6_ifindex; dstsa.sin6_addr = pktinfo->ipi6_addr; - (void)printf(" dst=%s", pr_addr(&dstsa)); + (void)printf(" dst=%s", + pr_addr((struct sockaddr *)&dstsa, + sizeof(dstsa))); } if (timing) (void)printf(" time=%g ms", triptime); @@ -1154,24 +1490,50 @@ pr_pack(buf, cc, mhdr) } } } - } else if (icp->icmp6_type == ICMP6_NI_REPLY) { /* ICMP6_NI_REPLY */ - struct icmp6_nodeinfo *ni = (struct icmp6_nodeinfo *)(buf + off); - - (void)printf("%d bytes from %s: ", cc, - pr_addr(from)); - - switch(ntohs(ni->ni_qtype)) { - case NI_QTYPE_NOOP: - printf("NodeInfo NOOP"); - break; - case NI_QTYPE_SUPTYPES: - printf("NodeInfo Supported Qtypes"); - break; - case NI_QTYPE_NODEADDR: - pr_nodeaddr(ni, end - (u_char *)ni); - break; - case NI_QTYPE_FQDN: - default: /* XXX: for backward compatibility */ + } else if (icp->icmp6_type == ICMP6_NI_REPLY && mynireply(ni)) { + seq = ntohs(*(u_int16_t *)ni->icmp6_ni_nonce); + ++nreceived; + if (TST(seq % mx_dup_ck)) { + ++nrepeats; + --nreceived; + dupflag = 1; + } else { + SET(seq % mx_dup_ck); + dupflag = 0; + } + + if (options & F_QUIET) + return; + + (void)printf("%d bytes from %s: ", cc, pr_addr(from, fromlen)); + + switch (ntohs(ni->ni_code)) { + case ICMP6_NI_SUCCESS: + break; + case ICMP6_NI_REFUSED: + printf("refused, type 0x%x", ntohs(ni->ni_type)); + goto fqdnend; + case ICMP6_NI_UNKNOWN: + printf("unknown, type 0x%x", ntohs(ni->ni_type)); + goto fqdnend; + default: + printf("unknown code 0x%x, type 0x%x", + ntohs(ni->ni_code), ntohs(ni->ni_type)); + goto fqdnend; + } + + switch (ntohs(ni->ni_qtype)) { + case NI_QTYPE_NOOP: + printf("NodeInfo NOOP"); + break; + case NI_QTYPE_SUPTYPES: + pr_suptypes(ni, end - (u_char *)ni); + break; + case NI_QTYPE_NODEADDR: + pr_nodeaddr(ni, end - (u_char *)ni); + break; + case NI_QTYPE_FQDN: + default: /* XXX: for backward compatibility */ cp = (u_char *)ni + ICMP6_NIRLEN; if (buf[off + ICMP6_NIRLEN] == cc - off - ICMP6_NIRLEN - 1) @@ -1179,38 +1541,31 @@ pr_pack(buf, cc, mhdr) else oldfqdn = 0; if (oldfqdn) { - cp++; + cp++; /* skip length */ while (cp < end) { safeputc(*cp & 0xff); cp++; } } else { + i = 0; while (cp < end) { - i = *cp++; - if (i) { - if (i > end - cp) { - printf("???"); - break; - } - while (i-- && cp < end) { - safeputc(*cp & 0xff); - cp++; - } - if (cp + 1 < end && *cp) - printf("."); - } else { - if (cp == end) { - /* FQDN */ - printf("."); - } else if (cp + 1 == end && - *cp == '\0') { - /* truncated */ - } else { - /* invalid */ - printf("???"); - } + if (dnsdecode((const u_char **)&cp, end, + (const u_char *)(ni + 1), dnsname, + sizeof(dnsname)) == NULL) { + printf("???"); break; } + /* + * name-lookup special handling for + * truncated name + */ + if (cp + 1 <= end && !*cp && + strlen(dnsname) > 0) { + dnsname[strlen(dnsname) - 1] = '\0'; + cp++; + } + printf("%s%s", i > 0 ? "," : "", + dnsname); } } if (options & F_VERBOSE) { @@ -1219,7 +1574,7 @@ pr_pack(buf, cc, mhdr) (void)printf(" ("); /*)*/ - switch(ni->ni_code) { + switch (ni->ni_code) { case ICMP6_NI_REFUSED: (void)printf("refused"); comma++; @@ -1231,19 +1586,21 @@ pr_pack(buf, cc, mhdr) } if ((end - (u_char *)ni) < ICMP6_NIRLEN) { - /* case of refusion, unkown */ + /* case of refusion, unknown */ + /*(*/ + putchar(')'); goto fqdnend; } ttl = (int32_t)ntohl(*(u_long *)&buf[off+ICMP6ECHOLEN+8]); if (comma) printf(","); - if (!(ni->ni_flags & NI_FQDN_FLAG_VALIDTTL)) + if (!(ni->ni_flags & NI_FQDN_FLAG_VALIDTTL)) { (void)printf("TTL=%d:meaningless", - (int)ttl); - else { + (int)ttl); + } else { if (ttl < 0) { (void)printf("TTL=%d:invalid", - ttl); + ttl); } else (void)printf("TTL=%d", ttl); } @@ -1269,22 +1626,21 @@ pr_pack(buf, cc, mhdr) if (comma) printf(","); (void)printf("invalid namelen:%d/%lu", - buf[off + ICMP6_NIRLEN], - (u_long)cc - off - ICMP6_NIRLEN - 1); + buf[off + ICMP6_NIRLEN], + (u_long)cc - off - ICMP6_NIRLEN - 1); comma++; } /*(*/ putchar(')'); } - fqdnend: + fqdnend: ; } } else { /* We've got something other than an ECHOREPLY */ if (!(options & F_VERBOSE)) return; - (void)printf("%d bytes from %s: ", cc, - pr_addr(from)); + (void)printf("%d bytes from %s: ", cc, pr_addr(from, fromlen)); pr_icmph(icp, end); } @@ -1308,7 +1664,7 @@ pr_exthdrs(mhdr) if (cm->cmsg_level != IPPROTO_IPV6) continue; - switch(cm->cmsg_type) { + switch (cm->cmsg_type) { case IPV6_HOPOPTS: printf(" HbH Options: "); pr_ip6opt(CMSG_DATA(cm)); @@ -1343,13 +1699,13 @@ pr_ip6opt(void *extbuf) ext = (struct ip6_hbh *)extbuf; extlen = (ext->ip6h_len + 1) * 8; - printf("nxt %u, len %u (%d bytes)\n", ext->ip6h_nxt, - ext->ip6h_len, extlen); + 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); + &type, &len, &databuf); if (currentlen == -1) break; switch (type) { @@ -1360,19 +1716,20 @@ pr_ip6opt(void *extbuf) case IP6OPT_JUMBO: offset = 0; offset = inet6_opt_get_val(databuf, offset, - &value4, sizeof(value4)); + &value4, sizeof(value4)); printf(" Jumbo Payload Opt: Length %u\n", - (unsigned int)ntohl(value4)); + (u_int32_t)ntohl(value4)); break; case IP6OPT_ROUTER_ALERT: offset = 0; offset = inet6_opt_get_val(databuf, offset, &value2, sizeof(value2)); printf(" Router Alert Opt: Type %u\n", - ntohs(value2)); + ntohs(value2)); break; default: - printf(" Received Opt %u len %u\n", type, len); + printf(" Received Opt %u len %lu\n", + type, (unsigned long)len); break; } } @@ -1399,7 +1756,7 @@ pr_rthdr(void *extbuf) /* 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); + rh->ip6r_len, (rh->ip6r_len + 1) << 3, rh->ip6r_type); if ((segments = inet6_rth_segments(extbuf)) >= 0) printf("%d segments, ", segments); else @@ -1410,15 +1767,18 @@ pr_rthdr(void *extbuf) in6 = inet6_rth_getaddr(extbuf, i); if (in6 == NULL) printf(" [%d]<NULL>\n", i); - else - printf(" [%d]%s\n", i, - inet_ntop(AF_INET6, in6, - ntopbuf, sizeof(ntopbuf))); + else { + if (!inet_ntop(AF_INET6, in6, ntopbuf, + sizeof(ntopbuf))) + strncpy(ntopbuf, "?", sizeof(ntopbuf)); + printf(" [%d]%s\n", i, ntopbuf); + } } return; - + } + #else /* !USE_RFC2292BIS */ /* ARGSUSED */ void @@ -1429,35 +1789,186 @@ pr_rthdr(void *extbuf) } #endif /* USE_RFC2292BIS */ +int +pr_bitrange(v, s, ii) + u_int32_t v; + int s; + int ii; +{ + int off; + int i; + + off = 0; + while (off < 32) { + /* shift till we have 0x01 */ + if ((v & 0x01) == 0) { + if (ii > 1) + printf("-%u", s + off - 1); + ii = 0; + switch (v & 0x0f) { + case 0x00: + v >>= 4; + off += 4; + continue; + case 0x08: + v >>= 3; + off += 3; + continue; + case 0x04: case 0x0c: + v >>= 2; + off += 2; + continue; + default: + v >>= 1; + off += 1; + continue; + } + } + + /* we have 0x01 with us */ + for (i = 0; i < 32 - off; i++) { + if ((v & (0x01 << i)) == 0) + break; + } + if (!ii) + printf(" %u", s + off); + ii += i; + v >>= i; off += i; + } + return ii; +} + +void +pr_suptypes(ni, nilen) + struct icmp6_nodeinfo *ni; /* ni->qtype must be SUPTYPES */ + size_t nilen; +{ + size_t clen; + u_int32_t v; + const u_char *cp, *end; + u_int16_t cur; + struct cbit { + u_int16_t words; /*32bit count*/ + u_int16_t skip; + } cbit; +#define MAXQTYPES (1 << 16) + size_t off; + int b; + + cp = (u_char *)(ni + 1); + end = ((u_char *)ni) + nilen; + cur = 0; + b = 0; + + printf("NodeInfo Supported Qtypes"); + if (options & F_VERBOSE) { + if (ni->ni_flags & NI_SUPTYPE_FLAG_COMPRESS) + printf(", compressed bitmap"); + else + printf(", raw bitmap"); + } + + while (cp < end) { + clen = (size_t)(end - cp); + if ((ni->ni_flags & NI_SUPTYPE_FLAG_COMPRESS) == 0) { + if (clen == 0 || clen > MAXQTYPES / 8 || + clen % sizeof(v)) { + printf("???"); + return; + } + } else { + if (clen < sizeof(cbit) || clen % sizeof(v)) + return; + memcpy(&cbit, cp, sizeof(cbit)); + if (sizeof(cbit) + ntohs(cbit.words) * sizeof(v) > + clen) + return; + cp += sizeof(cbit); + clen = ntohs(cbit.words) * sizeof(v); + if (cur + clen * 8 + (u_long)ntohs(cbit.skip) * 32 > + MAXQTYPES) + return; + } + + for (off = 0; off < clen; off += sizeof(v)) { + memcpy(&v, cp + off, sizeof(v)); + v = (u_int32_t)ntohl(v); + b = pr_bitrange(v, (int)(cur + off * 8), b); + } + /* flush the remaining bits */ + b = pr_bitrange(0, (int)(cur + off * 8), b); + + cp += clen; + cur += clen * 8; + if ((ni->ni_flags & NI_SUPTYPE_FLAG_COMPRESS) != 0) + cur += ntohs(cbit.skip) * 32; + } +} void pr_nodeaddr(ni, nilen) struct icmp6_nodeinfo *ni; /* ni->qtype must be NODEADDR */ int nilen; { - struct in6_addr *ia6 = (struct in6_addr *)(ni + 1); + u_char *cp = (u_char *)(ni + 1); char ntop_buf[INET6_ADDRSTRLEN]; + int withttl = 0; nilen -= sizeof(struct icmp6_nodeinfo); if (options & F_VERBOSE) { - switch(ni->ni_code) { - case ICMP6_NI_REFUSED: - (void)printf("refused"); - break; - case ICMP6_NI_UNKNOWN: - (void)printf("unknown qtype"); - break; + switch (ni->ni_code) { + case ICMP6_NI_REFUSED: + (void)printf("refused"); + break; + case ICMP6_NI_UNKNOWN: + (void)printf("unknown qtype"); + break; } - if (ni->ni_flags & NI_NODEADDR_FLAG_ALL) + if (ni->ni_flags & NI_NODEADDR_FLAG_TRUNCATE) (void)printf(" truncated"); } putchar('\n'); if (nilen <= 0) printf(" no address\n"); - for (; nilen > 0; nilen -= sizeof(*ia6), ia6 += 1) { - printf(" %s\n", - inet_ntop(AF_INET6, ia6, ntop_buf, sizeof(ntop_buf))); + + /* + * In icmp-name-lookups 05 and later, TTL of each returned address + * is contained in the resposne. We try to detect the version + * by the length of the data, but note that the detection algorithm + * is incomplete. We assume the latest draft by default. + */ + if (nilen % (sizeof(u_int32_t) + sizeof(struct in6_addr)) == 0) + withttl = 1; + while (nilen > 0) { + u_int32_t ttl; + + if (withttl) { + /* XXX: alignment? */ + ttl = (u_int32_t)ntohl(*(u_int32_t *)cp); + cp += sizeof(u_int32_t); + nilen -= sizeof(u_int32_t); + } + + if (inet_ntop(AF_INET6, cp, ntop_buf, sizeof(ntop_buf)) == + NULL) + strncpy(ntop_buf, "?", sizeof(ntop_buf)); + printf(" %s", ntop_buf); + if (withttl) { + if (ttl == 0xffffffff) { + /* + * XXX: can this convention be applied to all + * type of TTL (i.e. non-ND TTL)? + */ + printf("(TTL=infty)"); + } + else + printf("(TTL=%u)", ttl); + } + putchar('\n'); + + nilen -= sizeof(struct in6_addr); + cp += sizeof(struct in6_addr); } } @@ -1469,6 +1980,9 @@ get_hoplim(mhdr) 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))) @@ -1486,6 +2000,9 @@ get_rcvpktinfo(mhdr) for (cm = (struct cmsghdr *)CMSG_FIRSTHDR(mhdr); cm; cm = (struct cmsghdr *)CMSG_NXTHDR(mhdr, cm)) { + if (cm->cmsg_len == 0) + return(NULL); + if (cm->cmsg_level == IPPROTO_IPV6 && cm->cmsg_type == IPV6_PKTINFO && cm->cmsg_len == CMSG_LEN(sizeof(struct in6_pktinfo))) @@ -1495,6 +2012,116 @@ get_rcvpktinfo(mhdr) return(NULL); } +int +get_pathmtu(mhdr) + struct msghdr *mhdr; +{ +#ifdef IPV6_RECVPATHMTU + 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) != 0) { + 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); + } + } +#endif + return(0); +} + +void +set_pathmtu(mtu) + int mtu; +{ +#ifdef IPV6_USE_MTU + static int firsttime = 1; + struct cmsghdr *cm; + + if (firsttime) { + int oldlen = smsghdr.msg_controllen; + char *oldbuf = smsghdr.msg_control; + + /* XXX: We need to enlarge control message buffer */ + firsttime = 0; /* prevent further enlargement */ + + smsghdr.msg_controllen = oldlen + CMSG_SPACE(sizeof(int)); + if ((smsghdr.msg_control = + (char *)malloc(smsghdr.msg_controllen)) == NULL) + err(1, "set_pathmtu: malloc"); + cm = (struct cmsghdr *)CMSG_FIRSTHDR(&smsghdr); + cm->cmsg_len = CMSG_LEN(sizeof(int)); + cm->cmsg_level = IPPROTO_IPV6; + cm->cmsg_type = IPV6_USE_MTU; + + cm = (struct cmsghdr *)CMSG_NXTHDR(&smsghdr, cm); + if (oldlen) + memcpy((void *)cm, (void *)oldbuf, oldlen); + + free(oldbuf); + } + + /* + * look for a cmsgptr that points MTU structure. + * XXX: this procedure seems redundant at this moment, but we'd better + * keep the code generic enough for future extensions. + */ + for (cm = CMSG_FIRSTHDR(&smsghdr); cm; + cm = (struct cmsghdr *)CMSG_NXTHDR(&smsghdr, cm)) { + if (cm->cmsg_len == 0) /* XXX: paranoid check */ + errx(1, "set_pathmtu: internal error"); + + if (cm->cmsg_level == IPPROTO_IPV6 && + cm->cmsg_type == IPV6_USE_MTU && + cm->cmsg_len == CMSG_LEN(sizeof(int))) + break; + } + + if (cm == NULL) + errx(1, "set_pathmtu: internal error: no space for path MTU"); + + *(int *)CMSG_DATA(cm) = mtu; +#endif +} + /* * tvsub -- * Subtract 2 timeval structs: out = out - in. Out is assumed to @@ -1512,18 +2139,6 @@ tvsub(out, in) } /* - * oninfo -- - * SIGINFO handler. - */ -/* ARGSUSED */ -void -oninfo(notused) - int notused; -{ - summary(); -} - -/* * onint -- * SIGINT handler. */ @@ -1548,7 +2163,6 @@ onint(notused) void summary() { - register int i; (void)printf("\n--- %s ping6 statistics ---\n", hostname); (void)printf("%ld packets transmitted, ", ntransmitted); @@ -1566,28 +2180,35 @@ summary() (void)putchar('\n'); if (nreceived && timing) { /* Only display average to microseconds */ - i = 1000.0 * tsum / (nreceived + nrepeats); - (void)printf("round-trip min/avg/max = %g/%g/%g ms\n", - tmin, ((double)i) / 1000.0, tmax); + double num = nreceived + nrepeats; + double avg = tsum / num; +#if defined(__OpenBSD__) || defined(__NetBSD__) + double dev = sqrt(tsumsq / num - avg * avg); + (void)printf( + "round-trip min/avg/max/std-dev = %.3f/%.3f/%.3f/%.3f ms\n", + tmin, avg, tmax, dev); +#else + (void)printf( + "round-trip min/avg/max = %.3f/%.3f/%.3f ms\n", + tmin, avg, tmax); +#endif (void)fflush(stdout); } + (void)fflush(stdout); } -#ifdef notdef -static char *ttab[] = { - "Echo Reply", /* ip + seq + udata */ - "Dest Unreachable", /* net, host, proto, port, frag, sr + IP */ - "Source Quench", /* IP */ - "Redirect", /* redirect type, gateway, + IP */ - "Echo", - "Time Exceeded", /* transit, frag reassem + IP */ - "Parameter Problem", /* pointer + IP */ - "Timestamp", /* id + seq + three timestamps */ - "Timestamp Reply", /* " */ - "Info Request", /* id + sq */ - "Info Reply" /* " */ +/*subject type*/ +static char *niqcode[] = { + "IPv6 address", + "DNS label", /*or empty*/ + "IPv4 address", }; -#endif + +/*result code*/ +static char *nircode[] = { + "Success", "Refused", "Unknown", +}; + /* * pr_icmph -- @@ -1599,16 +2220,21 @@ pr_icmph(icp, end) u_char *end; { char ntop_buf[INET6_ADDRSTRLEN]; + struct nd_redirect *red; + struct icmp6_nodeinfo *ni; + char dnsname[MAXDNAME + 1]; + const u_char *cp; + size_t l; - switch(icp->icmp6_type) { + switch (icp->icmp6_type) { case ICMP6_DST_UNREACH: - switch(icp->icmp6_code) { + switch (icp->icmp6_code) { case ICMP6_DST_UNREACH_NOROUTE: (void)printf("No Route to Destination\n"); break; case ICMP6_DST_UNREACH_ADMIN: (void)printf("Destination Administratively " - "Unreachable\n"); + "Unreachable\n"); break; case ICMP6_DST_UNREACH_BEYONDSCOPE: (void)printf("Destination Unreachable Beyond Scope\n"); @@ -1629,11 +2255,11 @@ pr_icmph(icp, end) break; case ICMP6_PACKET_TOO_BIG: (void)printf("Packet too big mtu = %d\n", - (int)ntohl(icp->icmp6_mtu)); + (int)ntohl(icp->icmp6_mtu)); pr_retip((struct ip6_hdr *)(icp + 1), end); break; case ICMP6_TIME_EXCEEDED: - switch(icp->icmp6_code) { + switch (icp->icmp6_code) { case ICMP6_TIME_EXCEED_TRANSIT: (void)printf("Time to live exceeded\n"); break; @@ -1649,22 +2275,22 @@ pr_icmph(icp, end) break; case ICMP6_PARAM_PROB: (void)printf("Parameter problem: "); - switch(icp->icmp6_code) { - case ICMP6_PARAMPROB_HEADER: - (void)printf("Erroneous Header "); - break; - case ICMP6_PARAMPROB_NEXTHEADER: - (void)printf("Unknown Nextheader "); - break; - case ICMP6_PARAMPROB_OPTION: - (void)printf("Unrecognized Option "); - break; - default: - (void)printf("Bad code(%d) ", icp->icmp6_code); - break; + switch (icp->icmp6_code) { + case ICMP6_PARAMPROB_HEADER: + (void)printf("Erroneous Header "); + break; + case ICMP6_PARAMPROB_NEXTHEADER: + (void)printf("Unknown Nextheader "); + break; + case ICMP6_PARAMPROB_OPTION: + (void)printf("Unrecognized Option "); + break; + default: + (void)printf("Bad code(%d) ", icp->icmp6_code); + break; } (void)printf("pointer = 0x%02x\n", - (int)ntohl(icp->icmp6_pptr)); + (u_int32_t)ntohl(icp->icmp6_pptr)); pr_retip((struct ip6_hdr *)(icp + 1), end); break; case ICMP6_ECHO_REQUEST: @@ -1697,25 +2323,119 @@ pr_icmph(icp, end) (void)printf("Neighbor Advertisement"); break; case ND_REDIRECT: - { - struct nd_redirect *red = (struct nd_redirect *)icp; - + red = (struct nd_redirect *)icp; (void)printf("Redirect\n"); - (void)printf("Destination: %s", - inet_ntop(AF_INET6, &red->nd_rd_dst, - ntop_buf, sizeof(ntop_buf))); - (void)printf("New Target: %s", - inet_ntop(AF_INET6, &red->nd_rd_target, - ntop_buf, sizeof(ntop_buf))); + if (!inet_ntop(AF_INET6, &red->nd_rd_dst, ntop_buf, + sizeof(ntop_buf))) + strncpy(ntop_buf, "?", sizeof(ntop_buf)); + (void)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)); + (void)printf(" New Target: %s", ntop_buf); break; - } case ICMP6_NI_QUERY: (void)printf("Node Information Query"); /* XXX ID + Seq + Data */ + ni = (struct icmp6_nodeinfo *)icp; + l = end - (u_char *)(ni + 1); + printf(", "); + switch (ntohs(ni->ni_qtype)) { + case NI_QTYPE_NOOP: + (void)printf("NOOP"); + break; + case NI_QTYPE_SUPTYPES: + (void)printf("Supported qtypes"); + break; + case NI_QTYPE_FQDN: + (void)printf("DNS name"); + break; + case NI_QTYPE_NODEADDR: + (void)printf("nodeaddr"); + break; + case NI_QTYPE_IPV4ADDR: + (void)printf("IPv4 nodeaddr"); + break; + default: + (void)printf("unknown qtype"); + break; + } + if (options & F_VERBOSE) { + switch (ni->ni_code) { + case ICMP6_NI_SUBJ_IPV6: + if (l == sizeof(struct in6_addr) && + inet_ntop(AF_INET6, ni + 1, ntop_buf, + sizeof(ntop_buf)) != NULL) { + (void)printf(", subject=%s(%s)", + niqcode[ni->ni_code], ntop_buf); + } else { +#if 1 + /* backward compat to -W */ + (void)printf(", oldfqdn"); +#else + (void)printf(", invalid"); +#endif + } + break; + case ICMP6_NI_SUBJ_FQDN: + if (end == (u_char *)(ni + 1)) { + (void)printf(", no subject"); + break; + } + printf(", subject=%s", niqcode[ni->ni_code]); + cp = (const u_char *)(ni + 1); + if (dnsdecode(&cp, end, NULL, dnsname, + sizeof(dnsname)) != NULL) + printf("(%s)", dnsname); + else + printf("(invalid)"); + break; + case ICMP6_NI_SUBJ_IPV4: + if (l == sizeof(struct in_addr) && + inet_ntop(AF_INET, ni + 1, ntop_buf, + sizeof(ntop_buf)) != NULL) { + (void)printf(", subject=%s(%s)", + niqcode[ni->ni_code], ntop_buf); + } else + (void)printf(", invalid"); + break; + default: + (void)printf(", invalid"); + break; + } + } break; case ICMP6_NI_REPLY: (void)printf("Node Information Reply"); /* XXX ID + Seq + Data */ + ni = (struct icmp6_nodeinfo *)icp; + printf(", "); + switch (ntohs(ni->ni_qtype)) { + case NI_QTYPE_NOOP: + (void)printf("NOOP"); + break; + case NI_QTYPE_SUPTYPES: + (void)printf("Supported qtypes"); + break; + case NI_QTYPE_FQDN: + (void)printf("DNS name"); + break; + case NI_QTYPE_NODEADDR: + (void)printf("nodeaddr"); + break; + case NI_QTYPE_IPV4ADDR: + (void)printf("IPv4 nodeaddr"); + break; + default: + (void)printf("unknown qtype"); + break; + } + if (options & F_VERBOSE) { + if (ni->ni_code > sizeof(nircode) / sizeof(nircode[0])) + printf(", invalid"); + else + printf(", %s", nircode[ni->ni_code]); + } break; default: (void)printf("Bad ICMP type: %d", icp->icmp6_type); @@ -1740,13 +2460,14 @@ pr_iph(ip6) printf("Vr TC Flow Plen Nxt Hlim\n"); printf(" %1x %02x %05x %04x %02x %02x\n", - (ip6->ip6_vfc & IPV6_VERSION_MASK) >> 4, tc, (int)ntohl(flow), - ntohs(ip6->ip6_plen), - ip6->ip6_nxt, ip6->ip6_hlim); - printf("%s->", inet_ntop(AF_INET6, &ip6->ip6_src, - ntop_buf, sizeof(ntop_buf))); - printf("%s\n", inet_ntop(AF_INET6, &ip6->ip6_dst, - ntop_buf, sizeof(ntop_buf))); + (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); } /* @@ -1755,22 +2476,25 @@ pr_iph(ip6) * a hostname. */ const char * -pr_addr(addr) - struct sockaddr_in6 *addr; +pr_addr(addr, addrlen) + struct sockaddr *addr; + int addrlen; { - static char buf[MAXHOSTNAMELEN]; - int flag = 0; + static char buf[NI_MAXHOST]; + int flag; +#ifdef NI_WITHSCOPEID + flag = NI_WITHSCOPEID; +#else + flag = 0; +#endif if ((options & F_HOSTNAME) == 0) flag |= NI_NUMERICHOST; -#ifdef KAME_SCOPEID - flag |= NI_WITHSCOPEID; -#endif - - getnameinfo((struct sockaddr *)addr, addr->sin6_len, buf, sizeof(buf), - NULL, 0, flag); - return (buf); + if (getnameinfo(addr, addrlen, buf, sizeof(buf), NULL, 0, flag) == 0) + return (buf); + else + return "?"; } /* @@ -1796,53 +2520,53 @@ pr_retip(ip6, end) 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_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; #ifdef IPSEC - case IPPROTO_AH: - printf("AH "); - hlen = (((struct ah *)cp)->ah_len+2) << 2; - nh = ((struct ah *)cp)->ah_nxt; - break; + case IPPROTO_AH: + printf("AH "); + hlen = (((struct ah *)cp)->ah_len+2) << 2; + nh = ((struct ah *)cp)->ah_nxt; + break; #endif - 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; + 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) @@ -1876,7 +2600,7 @@ fill(bp, patp) &pat[7], &pat[8], &pat[9], &pat[10], &pat[11], &pat[12], &pat[13], &pat[14], &pat[15]); -/* xxx */ +/* xxx */ if (ii > 0) for (kk = 0; kk <= MAXDATALEN - (8 + sizeof(struct timeval) + ii); @@ -1906,8 +2630,8 @@ setpolicy(so, policy) buf = ipsec_set_policy(policy, strlen(policy)); if (buf == NULL) errx(1, "%s", ipsec_strerror()); - if (setsockopt(s, IPPROTO_IPV6, IPV6_IPSEC_POLICY, - buf, ipsec_get_policylen(buf)) < 0) + if (setsockopt(s, IPPROTO_IPV6, IPV6_IPSEC_POLICY, buf, + ipsec_get_policylen(buf)) < 0) warnx("Unable to set IPSec policy"); free(buf); @@ -1921,30 +2645,38 @@ nigroup(name) char *name; { char *p; + unsigned char *q; MD5_CTX ctxt; u_int8_t digest[16]; - char l; + u_int8_t c; + size_t l; char hbuf[NI_MAXHOST]; struct in6_addr in6; - p = name; - while (p && *p && *p != '.') - p++; - if (p - name > 63) - return NULL; /*label too long*/ + p = strchr(name, '.'); + if (!p) + p = name + strlen(name); l = p - name; + if (l > 63 || l > sizeof(hbuf) - 1) + return NULL; /*label too long*/ + strncpy(hbuf, name, l); + hbuf[(int)l] = '\0'; + + for (q = name; *q; q++) { + if (isupper(*q)) + *q = tolower(*q); + } /* generate 8 bytes of pseudo-random value. */ bzero(&ctxt, sizeof(ctxt)); MD5Init(&ctxt); - MD5Update(&ctxt, &l, sizeof(l)); - MD5Update(&ctxt, name, p - name); + c = l & 0xff; + MD5Update(&ctxt, &c, sizeof(c)); + MD5Update(&ctxt, name, l); MD5Final(digest, &ctxt); - bzero(&in6, sizeof(in6)); - in6.s6_addr[0] = 0xff; - in6.s6_addr[1] = 0x02; - in6.s6_addr[11] = 0x02; + if (inet_pton(AF_INET6, "ff02::2:0000:0000", &in6) != 1) + return NULL; /*XXX*/ bcopy(digest, &in6.s6_addr[12], 4); if (inet_ntop(AF_INET6, &in6, hbuf, sizeof(hbuf)) == NULL) @@ -1957,19 +2689,20 @@ void usage() { (void)fprintf(stderr, -"usage: ping6 [-dfHnNqvwW" + "usage: ping6 [-dfHmnNqvwW" #ifdef IPV6_REACHCONF - "R" + "R" #endif #ifdef IPSEC #ifdef IPSEC_POLICY_IPSEC - "] [-P policy" + "] [-P policy" #else - "AE" + "AE" +#endif #endif -#endif - "] [-a [aAclsg]] [-b sockbufsiz] [-c count] \n\ - [-I interface] [-i wait] [-l preload] [-p pattern] [-S sourceaddr]\n\ - [-s packetsize] [-h hoplimit] [hops...] host\n"); + "] [-a [aAclsg]] [-b sockbufsiz] [-c count] \n" + "\t[-I interface] [-i wait] [-l preload] [-p pattern] " + "[-S sourceaddr]\n" + "\t[-s packetsize] [-h hoplimit] [hops...] host\n"); exit(1); } |