diff options
author | Florian Obser <florian@cvs.openbsd.org> | 2016-09-17 14:58:43 +0000 |
---|---|---|
committer | Florian Obser <florian@cvs.openbsd.org> | 2016-09-17 14:58:43 +0000 |
commit | 03908425aa5f0b200378192f7478d60a47e2c913 (patch) | |
tree | 6d5c4c027c625b1776a898a9850a6bb955a87be1 /sbin/ping | |
parent | ec9109535ba4e4a92459818ecea88edab6892e55 (diff) |
The final merge ping6 into ping.
"why slow down?" deraadt@
Diffstat (limited to 'sbin/ping')
-rw-r--r-- | sbin/ping/ping.c | 934 |
1 files changed, 804 insertions, 130 deletions
diff --git a/sbin/ping/ping.c b/sbin/ping/ping.c index 0c95139638c..8514da0f0e0 100644 --- a/sbin/ping/ping.c +++ b/sbin/ping/ping.c @@ -1,4 +1,4 @@ -/* $OpenBSD: ping.c,v 1.205 2016/09/17 09:38:26 florian Exp $ */ +/* $OpenBSD: ping.c,v 1.206 2016/09/17 14:58:42 florian Exp $ */ /* * Copyright (C) 1995, 1996, 1997, and 1998 WIDE Project. @@ -123,6 +123,9 @@ struct payload { #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 */ @@ -174,6 +177,7 @@ char BSPACE = '\b'; /* characters written for flood */ char DOT = '.'; char *hostname; int ident; /* process id to identify our packets */ +int v6flag = 0; /* are we ping6? */ /* counters */ int64_t npackets; /* max packets to transmit */ @@ -211,6 +215,7 @@ 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 *); @@ -220,6 +225,16 @@ void pr_iph(struct ip *); 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[]) { @@ -227,10 +242,15 @@ main(int argc, char *argv[]) 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; socklen_t maxsizelen; int64_t preload; - int ch, i, optval = 1, packlen, maxsize, error, s; - int df = 0, tos = 0, bufspace = IP_MAXPACKET; + int ch, i, optval = 1, packlen, maxsize, error, s4, s6, s; + int df = 0, tos = 0, bufspace = IP_MAXPACKET, hoplimit = -1, mflag = 0; + int v4sock_errno = 0, v6sock_errno = 0; u_char *datap, *packet, loop = 1; u_char ttl = MAXTTL; char *e, *target, hbuf[NI_MAXHOST], *source = NULL; @@ -239,19 +259,40 @@ main(int argc, char *argv[]) double intval; uid_t uid; u_int rtableid = 0; + extern char *__progname; - if ((s = socket(AF_INET, SOCK_RAW, IPPROTO_ICMP)) < 0) - err(1, "socket"); + if ((s4 = socket(AF_INET, SOCK_RAW, IPPROTO_ICMP)) < 0) + v4sock_errno = errno; + if ((s6 = socket(AF_INET6, SOCK_RAW, IPPROTO_ICMPV6)) < 0) + v6sock_errno = errno; /* revoke privs */ uid = getuid(); if (setresuid(uid, uid, uid) == -1) err(1, "setresuid"); + if (strcmp("ping6", __progname) == 0) { + v6flag = 1; + if (v6sock_errno != 0) + errc(1, v6sock_errno, "socket"); + s = s6; + if (s4 >= 0) + close(s4); + maxpayload = MAXPAYLOAD6; + } else { + v6flag = 0; + if (v4sock_errno != 0) + errc(1, v4sock_errno, "socket"); + s = s4; + if (s6 >= 0) + close(s6); + maxpayload = MAXPAYLOAD; + } + preload = 0; - maxpayload = MAXPAYLOAD; datap = &outpack[ECHOLEN + ECHOTMLEN]; - while ((ch = getopt(argc, argv, + while ((ch = getopt(argc, argv, v6flag ? + "c:dEefHh:I:i:Ll:mNnp:qS:s:V:vw:" : "DEI:LRS:c:defHi:l:np:qs:T:t:V:vw:")) != -1) { switch(ch) { case 'c': @@ -283,6 +324,11 @@ main(int argc, char *argv[]) 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; @@ -319,6 +365,9 @@ main(int argc, char *argv[]) errx(1, "preload value is %s: %s", errstr, optarg); break; + case 'm': + mflag++; + break; case 'n': options &= ~F_HOSTNAME; break; @@ -390,6 +439,7 @@ main(int argc, char *argv[]) usage(); memset(&dst4, 0, sizeof(dst4)); + memset(&dst6, 0, sizeof(dst6)); if (inet_aton(*argv, &dst4.sin_addr) != 0) { hostname = *argv; @@ -399,7 +449,7 @@ main(int argc, char *argv[]) target = *argv; memset(&hints, 0, sizeof(hints)); - hints.ai_family = AF_INET; + hints.ai_family = v6flag ? AF_INET6 : AF_INET; hints.ai_socktype = SOCK_RAW; hints.ai_protocol = 0; hints.ai_flags = AI_CANONNAME; @@ -414,6 +464,11 @@ main(int argc, char *argv[]) from = (struct sockaddr *)&from4; break; case AF_INET6: + if (res->ai_addrlen != sizeof(dst6)) + errx(1, "size of sockaddr mismatch"); + dst = (struct sockaddr *)&dst6; + from = (struct sockaddr *)&from6; + break; default: errx(1, "unsupported AF: %d", res->ai_family); break; @@ -444,7 +499,7 @@ main(int argc, char *argv[]) err(1, "malloc"); } memset(&hints, 0, sizeof(hints)); - hints.ai_family = AF_INET; + hints.ai_family = dst->sa_family; if ((error = getaddrinfo(source, NULL, &hints, &res))) errx(1, "%s: %s", source, gai_strerror(error)); if (res->ai_addrlen != sizeof(from4)) @@ -452,7 +507,7 @@ main(int argc, char *argv[]) memcpy(from, res->ai_addr, res->ai_addrlen); freeaddrinfo(res); - if (IN_MULTICAST(ntohl(dst4.sin_addr.s_addr))) { + 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)) < 0) err(1, "setsockopt IP_MULTICAST_IF"); @@ -472,16 +527,34 @@ main(int argc, char *argv[]) err(1, "UDP socket"); memcpy(from, dst, dst->sa_len); - from4.sin_port = ntohs(DUMMY_PORT); - - if ((moptions & MULTICAST_NOLOOP) && - setsockopt(dummy, IPPROTO_IP, IP_MULTICAST_LOOP, &loop, - sizeof(loop)) < 0) - err(1, "setsockopt IP_MULTICAST_LOOP"); - if ((moptions & MULTICAST_TTL) && - setsockopt(dummy, IPPROTO_IP, IP_MULTICAST_TTL, &ttl, - sizeof(ttl)) < 0) - err(1, "setsockopt IP_MULTICAST_TTL"); + if (v6flag) { + from6.sin6_port = ntohs(DUMMY_PORT); + if (pktinfo && + setsockopt(dummy, IPPROTO_IPV6, IPV6_PKTINFO, + (void *)pktinfo, sizeof(*pktinfo))) + err(1, "UDP setsockopt(IPV6_PKTINFO)"); + + if (hoplimit != -1 && + setsockopt(dummy, IPPROTO_IPV6, IPV6_UNICAST_HOPS, + (void *)&hoplimit, sizeof(hoplimit))) + err(1, "UDP setsockopt(IPV6_UNICAST_HOPS)"); + + if (hoplimit != -1 && + setsockopt(dummy, IPPROTO_IPV6, IPV6_MULTICAST_HOPS, + (void *)&hoplimit, sizeof(hoplimit))) + err(1, "UDP setsockopt(IPV6_MULTICAST_HOPS)"); + } else { + from4.sin_port = ntohs(DUMMY_PORT); + + if ((moptions & MULTICAST_NOLOOP) && setsockopt(dummy, + IPPROTO_IP, IP_MULTICAST_LOOP, &loop, + sizeof(loop)) < 0) + err(1, "setsockopt IP_MULTICAST_LOOP"); + if ((moptions & MULTICAST_TTL) && setsockopt(dummy, + IPPROTO_IP, IP_MULTICAST_TTL, &ttl, + sizeof(ttl)) < 0) + err(1, "setsockopt IP_MULTICAST_TTL"); + } if (rtableid > 0 && setsockopt(dummy, SOL_SOCKET, SO_RTABLE, &rtableid, @@ -509,7 +582,15 @@ main(int argc, char *argv[]) if (datalen >= sizeof(struct payload)) /* can we time transfer */ timing = 1; - packlen = datalen + MAXIPLEN + MAXICMPLEN; + + 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"); @@ -545,58 +626,132 @@ main(int argc, char *argv[]) warnx("Could only allocate a receive buffer of %d bytes " "(default %d)", bufspace, IP_MAXPACKET); - if (options & F_TTL) { - if (IN_MULTICAST(ntohl(dst4.sin_addr.s_addr))) - moptions |= MULTICAST_TTL; - else - options |= F_HDRINCL; - } + if (v6flag) { + /* + * let the kernel pass extension headers of incoming packets, + * for privileged socket options + */ + if ((options & F_VERBOSE) != 0) { + int opton = 1; + + if (setsockopt(s, IPPROTO_IPV6, IPV6_RECVHOPOPTS, + &opton, (socklen_t)sizeof(opton))) + err(1, "setsockopt(IPV6_RECVHOPOPTS)"); + if (setsockopt(s, IPPROTO_IPV6, IPV6_RECVDSTOPTS, + &opton, (socklen_t)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 (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; - - setsockopt(s, IPPROTO_IP, IP_HDRINCL, &optval, sizeof(optval)); - 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; - } + if ((moptions & MULTICAST_NOLOOP) && + setsockopt(s, IPPROTO_IPV6, IPV6_MULTICAST_LOOP, &loop, + sizeof(loop)) < 0) + err(1, "setsockopt IP6_MULTICAST_LOOP"); + + optval = IPV6_DEFHLIM; + if (IN6_IS_ADDR_MULTICAST(&dst6.sin6_addr)) + if (setsockopt(s, IPPROTO_IPV6, IPV6_MULTICAST_HOPS, + &optval, (socklen_t)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, (socklen_t)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, + (socklen_t)sizeof(filt)) < 0) + 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)); - /* 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)) < 0) { - perror("ping: record route"); - exit(1); + scmsg->cmsg_len = CMSG_LEN(sizeof(int)); + scmsg->cmsg_level = IPPROTO_IPV6; + scmsg->cmsg_type = IPV6_HOPLIMIT; + *(int *)(CMSG_DATA(scmsg)) = hoplimit; } - } - if ((moptions & MULTICAST_NOLOOP) && - setsockopt(s, IPPROTO_IP, IP_MULTICAST_LOOP, &loop, - sizeof(loop)) < 0) - err(1, "setsockopt IP_MULTICAST_LOOP"); - if ((moptions & MULTICAST_TTL) && - setsockopt(s, IPPROTO_IP, IP_MULTICAST_TTL, &ttl, - sizeof(ttl)) < 0) - err(1, "setsockopt IP_MULTICAST_TTL"); + optval = 1; + if (setsockopt(s, IPPROTO_IPV6, IPV6_RECVPKTINFO, &optval, + (socklen_t)sizeof(optval)) < 0) + warn("setsockopt(IPV6_RECVPKTINFO)"); /* XXX err? */ + if (setsockopt(s, IPPROTO_IPV6, IPV6_RECVHOPLIMIT, &optval, + (socklen_t)sizeof(optval)) < 0) + warn("setsockopt(IPV6_RECVHOPLIMIT)"); /* XXX err? */ + } else { + 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; + + setsockopt(s, IPPROTO_IP, IP_HDRINCL, &optval, + sizeof(optval)); + 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)) < 0) { + perror("ping: record route"); + exit(1); + } + } + + if ((moptions & MULTICAST_NOLOOP) && + setsockopt(s, IPPROTO_IP, IP_MULTICAST_LOOP, &loop, + sizeof(loop)) < 0) + err(1, "setsockopt IP_MULTICAST_LOOP"); + if ((moptions & MULTICAST_TTL) && + setsockopt(s, IPPROTO_IP, IP_MULTICAST_TTL, &ttl, + sizeof(ttl)) < 0) + err(1, "setsockopt IP_MULTICAST_TTL"); + } if (options & F_HOSTNAME) { if (pledge("stdio inet dns", NULL) == -1) @@ -609,8 +764,11 @@ main(int argc, char *argv[]) arc4random_buf(&tv64_offset, sizeof(tv64_offset)); arc4random_buf(&mac_key, sizeof(mac_key)); - printf("PING %s (", hostname); - if (0 && (options & F_VERBOSE)) + if (v6flag) + printf("PING6 %s (", hostname); + else + printf("PING %s (", hostname); + if (v6flag && (options & F_VERBOSE)) printf("%s --> ", pr_addr(from, from->sa_len)); printf("%s): %d data bytes\n", pr_addr(dst, dst->sa_len), datalen); @@ -646,7 +804,8 @@ main(int argc, char *argv[]) } cmsgbuf; struct iovec iov[1]; struct pollfd pfd; - struct sockaddr_in peer; + struct sockaddr_in peer4; + struct sockaddr_in6 peer6; ssize_t cc; int timeout; @@ -682,8 +841,13 @@ main(int argc, char *argv[]) if (poll(&pfd, 1, timeout) <= 0) continue; - m.msg_name = &peer; - m.msg_namelen = sizeof(peer); + 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; @@ -699,6 +863,21 @@ main(int argc, char *argv[]) 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) != 0) { + printf("new path MTU (%d) is " + "notified\n", mtu); + } + } + continue; } else pr_pack(packet, cc, &m); @@ -758,7 +937,10 @@ fill(char *bp, char *patp) void summary(void) { - printf("\n--- %s ping statistics ---\n", hostname); + if (v6flag) + printf("\n--- %s ping statistics ---\n", hostname); + else + printf("\n--- %s ping6 statistics ---\n", hostname); printf("%lld packets transmitted, ", ntransmitted); printf("%lld packets received, ", nreceived); @@ -851,7 +1033,8 @@ retransmit(int s) int pinger(int s) { - struct icmp *icp; + struct icmp *icp = NULL; + struct icmp6_hdr *icp6 = NULL; int cc, i; u_int16_t seq; @@ -860,13 +1043,22 @@ pinger(int s) seq = htons(ntransmitted++); - 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; /* ID */ - + 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 = htons(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; /* ID */ + } CLR(ntohs(seq) % mx_dup_ck); if (timing) { @@ -893,16 +1085,18 @@ pinger(int s) cc = ECHOLEN + datalen; - /* compute ICMP checksum here */ - icp->icmp_cksum = in_cksum((u_short *)icp, cc); + 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; + 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_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; @@ -912,7 +1106,12 @@ pinger(int s) if (i < 0 || i != cc) { if (i < 0) warn("sendmsg"); - printf("ping: wrote %s %d chars, ret=%d\n", hostname, cc, i); + if (v6flag) + printf("ping6: wrote %s %d chars, ret=%d\n", hostname, + cc, i); + else + printf("ping: wrote %s %d chars, ret=%d\n", hostname, + cc, i); } if (!(options & F_QUIET) && options & F_FLOOD) (void)write(STDOUT_FILENO, &DOT, 1); @@ -930,15 +1129,16 @@ pinger(int s) void pr_pack(u_char *buf, int cc, struct msghdr *mhdr) { - struct ip *ip; - struct icmp *icp; + 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; + int hlen = -1, hoplim = -1, echo_reply = 0; u_int16_t seq; u_char *cp, *dp; char* pkttime; @@ -946,40 +1146,79 @@ pr_pack(u_char *buf, int cc, struct msghdr *mhdr) if (clock_gettime(CLOCK_MONOTONIC, &ts) == -1) err(1, "clock_gettime(CLOCK_MONOTONIC)"); - 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; + 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; - /* 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; + 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 (ntohs(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; + } } - /* 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; + if (echo_reply) { ++nreceived; if (cc >= ECHOLEN + ECHOTMLEN) { SIPHASH_CTX ctx; struct tv64 *tv64; u_int8_t mac[SIPHASH_DIGEST_LENGTH]; - pkttime = (char *)icp->icmp_data; memcpy(&payload, pkttime, sizeof(payload)); tv64 = &payload.tv64; @@ -1029,7 +1268,10 @@ pr_pack(u_char *buf, int cc, struct msghdr *mhdr) else { (void)printf("%d bytes from %s: icmp_seq=%u", cc, pr_addr(from, fromlen), ntohs(seq)); - (void)printf(" ttl=%d", ip->ip_ttl); + if (v6flag) + (void)printf(" hlim=%d", hoplim); + else + (void)printf(" ttl=%d", ip->ip_ttl); if (cc >= ECHOLEN + ECHOTMLEN) (void)printf(" time=%.3f ms", triptime); if (dupflag) @@ -1037,7 +1279,10 @@ pr_pack(u_char *buf, int cc, struct msghdr *mhdr) /* check the data */ if (cc - ECHOLEN < datalen) (void)printf(" (TRUNC!)"); - cp = (u_char *)&icp->icmp_data[ECHOTMLEN]; + 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; @@ -1046,7 +1291,11 @@ pr_pack(u_char *buf, int cc, struct msghdr *mhdr) (void)printf("\nwrong data byte #%d " "should be 0x%x but was 0x%x", i - ECHOLEN, *dp, *cp); - cp = (u_char *)&icp->icmp_data[0]; + 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) @@ -1062,15 +1311,20 @@ pr_pack(u_char *buf, int cc, struct msghdr *mhdr) if (!(options & F_VERBOSE)) return; (void)printf("%d bytes from %s: ", cc, pr_addr(from, fromlen)); - pr_icmph(icp); + if (v6flag) + pr_icmph6(icp6, buf + cc); + else + pr_icmph(icp); } /* Display any IP options */ - if (hlen > sizeof(struct ip)) + if (!v6flag && hlen > sizeof(struct ip)) pr_ipopt(hlen, buf); if (!(options & F_FLOOD)) { (void)putchar('\n'); + if (v6flag && (options & F_VERBOSE)) + pr_exthdrs(mhdr); (void)fflush(stdout); if (options & F_AUD_RECV) (void)fputc('\a', stderr); @@ -1500,15 +1754,435 @@ map_tos(char *key, int *val) } #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) != 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); + } + } + 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: + (void)printf("No Route to Destination\n"); + break; + case ICMP6_DST_UNREACH_ADMIN: + (void)printf("Destination Administratively " + "Unreachable\n"); + break; + case ICMP6_DST_UNREACH_BEYONDSCOPE: + (void)printf("Destination Unreachable Beyond Scope\n"); + break; + case ICMP6_DST_UNREACH_ADDR: + (void)printf("Destination Host Unreachable\n"); + break; + case ICMP6_DST_UNREACH_NOPORT: + (void)printf("Destination Port Unreachable\n"); + break; + default: + (void)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: + (void)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: + (void)printf("Time to live exceeded\n"); + break; + case ICMP6_TIME_EXCEED_REASSEMBLY: + (void)printf("Frag reassembly time exceeded\n"); + break; + default: + (void)printf("Time exceeded, Bad Code: %d\n", + icp->icmp6_code); + break; + } + pr_retip6((struct ip6_hdr *)(icp + 1), 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; + } + (void)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: + (void)printf("Echo Request"); + /* XXX ID + Seq + Data */ + break; + case ICMP6_ECHO_REPLY: + (void)printf("Echo Reply"); + /* XXX ID + Seq + Data */ + break; + case ICMP6_MEMBERSHIP_QUERY: + (void)printf("Listener Query"); + break; + case ICMP6_MEMBERSHIP_REPORT: + (void)printf("Listener Report"); + break; + case ICMP6_MEMBERSHIP_REDUCTION: + (void)printf("Listener Done"); + break; + case ND_ROUTER_SOLICIT: + (void)printf("Router Solicitation"); + break; + case ND_ROUTER_ADVERT: + (void)printf("Router Advertisement"); + break; + case ND_NEIGHBOR_SOLICIT: + (void)printf("Neighbor Solicitation"); + break; + case ND_NEIGHBOR_ADVERT: + (void)printf("Neighbor Advertisement"); + break; + case ND_REDIRECT: + red = (struct nd_redirect *)icp; + (void)printf("Redirect\n"); + 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; + default: + (void)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) { - (void)fprintf(stderr, - "usage: ping [-DdEefHLnqRv] [-c count] [-I ifaddr] [-i wait]\n" - "\t[-l preload] [-p pattern] [-s packetsize]" + if (v6flag) { + (void)fprintf(stderr, + "usage: ping6 [-dEefHLmnqv] [-c count] [-h hoplimit] " + "[-I sourceaddr]\n\t[-i wait] [-l preload] [-p pattern] " + "[-s packetsize] [-V rtable]\n\t[-w maxwait] host\n"); + } else { + (void)fprintf(stderr, + "usage: ping [-DdEefHLnqRv] [-c count] [-I ifaddr]" + " [-i wait]\n\t[-l preload] [-p pattern] [-s packetsize]" #ifndef SMALL - " [-T toskeyword]" + " [-T toskeyword]" #endif /* SMALL */ - "\n\t[-t ttl] [-V rtable] [-w maxwait] host\n"); + "\n\t[-t ttl] [-V rtable] [-w maxwait] host\n"); + } exit(1); } |