/*
* CDDL HEADER START
*
* The contents of this file are subject to the terms of the
* Common Development and Distribution License, Version 1.0 only
* (the "License"). You may not use this file except in compliance
* with the License.
*
* You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
* See the License for the specific language governing permissions
* and limitations under the License.
*
* When distributing Covered Code, include this CDDL HEADER in each
* file and include the License file at usr/src/OPENSOLARIS.LICENSE.
* If applicable, add the following below this CDDL HEADER, with the
* fields enclosed by brackets "[]" replaced with your own identifying
* information: Portions Copyright [yyyy] [name of copyright owner]
*
* CDDL HEADER END
*/
/*
* Copyright 2006 Sun Microsystems, Inc. All rights reserved.
* Use is subject to license terms.
*/
/* Copyright (c) 1983, 1984, 1985, 1986, 1987, 1988, 1989 AT&T */
/* All Rights Reserved */
/*
* Copyright 2015, Joyent, Inc.
*/
/*
* Portions of this source code were derived from Berkeley 4.3 BSD
* under license from the Regents of the University of California.
*/
#include <stdio.h>
#include <string.h>
#include <strings.h>
#include <errno.h>
#include <fcntl.h>
#include <unistd.h>
#include <signal.h>
#include <netinet/in_systm.h>
#include <netdb.h>
#include <stdlib.h>
#include <libinetutil.h>
#include "ping.h"
static void pr_ext_headers(struct msghdr *);
extern char *pr_name(char *, int);
extern char *pr_protocol(int);
static void pr_rthdr(unsigned char *);
extern void schedule_sigalrm();
extern void send_scheduled_probe();
extern void sigalrm_handler();
/*
* Initialize the msghdr for specifying the hoplimit, outgoing interface and
* routing header.
*/
void
{
int i;
return;
/*
* Need to figure out size of buffer needed for ancillary data
* containing routing header and packet info options.
*
* Portable heuristic to compute upper bound on space needed for
* N ancillary data options. It assumes up to _MAX_ALIGNMENT padding
* after both header and data as the worst possible upper bound on space
* consumed by padding.
* It also adds one extra "sizeof (struct cmsghdr)" for the last option.
* This is needed because we would like to use CMSG_NXTHDR() while
* composing the buffer. The CMSG_NXTHDR() macro is designed better for
* parsing than composing the buffer. It requires the pointer it returns
* to leave space in buffer for addressing a cmsghdr and we want to make
* sure it works for us while we skip beyond the last ancillary data
* option.
*
* bufspace[i] = sizeof(struct cmsghdr) + <pad after header> +
* <option[i] content length> + <pad after data>;
*
* total_bufspace = bufspace[0] + bufspace[1] + ...
* ... + bufspace[N-1] + sizeof (struct cmsghdr);
*/
rthdr_space = 0;
pktinfo_space = 0;
hoplimit_space = 0;
bufspace = 0;
if (hoplimit != -1) {
hoplimit_space = sizeof (int);
}
if (gw_cnt > 0) {
}
if (if_index != 0) {
pktinfo_space = sizeof (struct in6_pktinfo);
}
/*
* We need to temporarily set the msgp->msg_controllen to bufspace
* (we will later trim it to actual length used). This is needed because
* CMSG_NXTHDR() uses it to check we have not exceeded the bounds.
*/
/*
* This function is called more than once only if -l/-S used,
* since we need to modify the middle gateway. So, don't alloc
* new memory, just reuse what msg6 points to.
*/
if (first) {
}
};
/*
* Fill ancillary data. First hoplimit, then rthdr and pktinfo.
*/
/* set hoplimit ancillary data if needed */
if (hoplimit != -1) {
/* LINTED */
*(int *)cmsg_datap = hoplimit;
}
/* set rthdr ancillary data if needed */
if (gw_cnt > 0) {
/*
* Initialize rthdr structure
*/
/* LINTED */
progname);
}
/*
* Stuff in gateway addresses
*/
for (i = 0; i < gw_cnt; i++) {
"%s: inet6_rth_add\n", progname);
}
}
}
/* set pktinfo ancillary data if needed */
if (if_index != 0) {
/* LINTED */
/*
* We don't know if pktinfop->ipi6_addr is aligned properly,
* therefore let's use bcopy, instead of assignment.
*/
sizeof (struct in6_addr));
/*
* We can assume pktinfop->ipi6_ifindex is 32 bit aligned.
*/
}
}
/*
* Check out the packet to see if it came from us. This logic is necessary
* because ALL readers of the ICMP socket get a copy of ALL ICMP packets
* which arrive ('tis only fair). This permits multiple copies of this
* program to be run without having intermingled output (or statistics!).
*/
void
{
/* address of this reply same */
/* as where we're sending */
/* currently? */
/* probe all with npackets>0 */
/* and we received reply for */
/* the last probe sent to */
/* targetaddr */
int cc_left;
int i;
static char *unreach6[] = {
"No Route to Destination",
"Communication Administratively Prohibited",
"Not a Neighbor (obsoleted ICMPv6 code)",
"Address Unreachable",
"Port Unreachable"
};
static char *timexceed6[] = {
"Hop limit exceeded in transit",
"Fragment reassembly time exceeded"
};
static char *param_prob6[] = {
"Erroneous header field encountered",
"Unrecognized next header type encountered",
"Unrecognized IPv6 option encountered"
};
/* decompose msghdr into useful pieces */
/* LINTED */
/* Ignore packets > 64k or control buffers that don't fit */
if (verbose) {
Printf("Truncated message: msg_flags 0x%x from %s\n",
}
return;
}
if (cc < ICMP6_MINLEN) {
if (verbose) {
}
return;
}
/* LINTED */
switch (icmp6->icmp6_type) {
case ICMP6_DST_UNREACH:
/* LINTED */
if (verbose) {
Printf("packet too short (%d bytes) from %s\n",
cc,
AF_INET6));
}
return;
}
/*
* Determine the total length of IPv6 header and extension
* headers, also the upper layer header (UDP, TCP, ICMP, etc.)
* following.
*/
cc_left -= ip6hdr_len;
/* LINTED */
if (verbose) {
Printf("packet too short (%d bytes) from %s\n",
cc,
AF_INET6));
}
return;
}
/* determine if this is *the* reply */
last_hdr == IPPROTO_UDP &&
use_udp) {
} else {
}
if (valid_reply) {
/*
* For this valid reply, if we are still sending to
* this target IP address, we'd like to do some
* updates to targetaddr, so hold SIGALRMs.
*/
nreceived++;
if (reply_matched_current_target) {
/*
* Determine if stats, probe-all, and
* npackets != 0, and this is the reply for
* the last probe we sent to current target
* address.
*/
} else {
/*
* If it's just probe_all and we just received
* a reply from a target address we were
* probing and had timed out (now we are probing
* some other target address), we ignore
* this reply.
*/
/*
* Only if it's verbose, we get a
* message regarding this reply,
* otherwise we are done here.
*/
if (!verbose) {
return;
}
}
}
}
if (valid_reply && !stats) {
/*
* if we are still sending to the same target address,
* then stop it, because we know it's alive.
*/
if (reply_matched_current_target) {
(void) alarm(0); /* cancel alarm */
}
if (!probe_all) {
} else {
if (nflag) {
} else {
Printf("%s (%s) is alive\n",
}
}
if (reply_matched_current_target) {
/*
* Let's get things going again, but now
* ping will start sending to next target IP
* address.
*/
}
return;
} else {
/*
* If we are not moving to next targetaddr, let's
* release the SIGALRM now. We don't want to stall in
* the middle of probing a targetaddr if the pr_name()
* call (see below) takes longer.
*/
/* else, we'll release it later */
}
if (valid_reply) {
Printf("ICMPv6 %d Unreachable from gateway "
AF_INET6));
} else {
Printf("ICMPv6 %s from gateway %s\n",
AF_INET6));
}
AF_INET6));
}
/*
* Update and print the stats, if it's a valid reply and
* contains a timestamp.
*/
/* LINTED */
sizeof (struct udphdr));
}
if (print_newline)
(void) putchar('\n');
/*
* If it's stats, probe-all, npackets > 0, and we received reply
* for the last probe sent to this target address, then we
* don't need to wait anymore, let's move on to next target
* address, now!
*/
if (last_reply_from_targetaddr) {
(void) alarm(0); /* cancel alarm */
}
break;
case ICMP6_PACKET_TOO_BIG:
/* LINTED */
if (verbose) {
Printf("packet too short (%d bytes) from %s\n",
AF_INET6));
}
return;
}
Printf("ICMPv6 packet too big from %s\n",
AF_INET6));
if ((last_hdr == IPPROTO_TCP ||
last_hdr == IPPROTO_UDP) &&
/* LINTED */
((char *)ip6h + ip6hdr_len);
}
}
break;
case ICMP6_TIME_EXCEEDED:
/* LINTED */
if (verbose) {
Printf("packet too short (%d bytes) from %s\n",
cc,
AF_INET6));
}
return;
}
Printf("ICMPv6 %d time exceeded from %s\n",
AF_INET6));
} else {
Printf("ICMPv6 %s from %s\n",
AF_INET6));
}
AF_INET6));
if ((last_hdr == IPPROTO_TCP ||
last_hdr == IPPROTO_UDP) &&
/* LINTED */
((char *)ip6h + ip6hdr_len);
}
(void) putchar('\n');
}
break;
case ICMP6_PARAM_PROB:
/* LINTED */
if (verbose) {
Printf("packet too short (%d bytes) from %s\n",
cc,
AF_INET6));
}
return;
}
Printf("ICMPv6 %d parameter problem from %s\n",
AF_INET6));
} else {
Printf("ICMPv6 %s from %s\n",
AF_INET6));
}
Printf(" (value 0x%x)",
}
AF_INET6));
if ((last_hdr == IPPROTO_TCP ||
last_hdr == IPPROTO_UDP) &&
/* LINTED */
((char *)ip6h + ip6hdr_len);
}
(void) putchar('\n');
}
break;
case ICMP6_ECHO_REQUEST:
return;
case ICMP6_ECHO_REPLY:
if (!use_udp)
else
} else {
return;
}
if (valid_reply) {
/*
* For this valid reply, if we are still sending to
* this target IP address, we'd like to do some
* updates to targetaddr, so hold SIGALRMs.
*/
nreceived++;
if (reply_matched_current_target) {
/*
* Determine if stats, probe-all, and
* npackets != 0, and this is the reply for
* the last probe we sent to current target
* address.
*/
(MAX_ICMP_SEQ + 1) ==
} else {
/*
* If it's just probe_all and we just received
* a reply from a target address we were
* probing and had timed out (now we are probing
* some other target address), we ignore
* this reply.
*/
/*
* Only if it's verbose, we get a
* message regarding this reply,
* otherwise we are done here.
*/
if (!verbose) {
return;
}
}
}
}
if (!stats && valid_reply) {
/*
* if we are still sending to the same target address,
* then stop it, because we know it's alive.
*/
if (reply_matched_current_target) {
(void) alarm(0); /* cancel alarm */
}
if (!probe_all) {
} else {
/*
* If we are using send_reply, the real
* target address is not the src address of the
* replies. Use icmp_seq to find out where this
* probe was sent to.
*/
if (send_reply) {
(void) find_dstaddr(
} else {
}
if (nflag) {
} else {
Printf("%s (%s) is alive\n",
}
}
if (reply_matched_current_target) {
/*
* Let's get things going again, but now
* ping will start sending to next target IP
* address.
*/
}
return;
} else {
/*
* If we are not moving to next targetaddr, let's
* release the SIGALRM now. We don't want to stall in
* the middle of probing a targetaddr if the pr_name()
* call (see below) takes longer.
*/
/* else, we'll release it later */
}
/*
* If we are using send_reply, the real target address is
* not the src address of the replies. Use icmp_seq to find out
* where this probe was sent to.
*/
if (send_reply) {
} else {
}
/* LINTED */
}
(void) putchar('\n');
/*
* If it's stats, probe-all, npackets > 0, and we received reply
* for the last probe sent to this target address, then we
* don't need to wait anymore, let's move on to next target
* address, now!
*/
if (last_reply_from_targetaddr) {
(void) alarm(0); /* cancel alarm */
}
break;
case MLD_LISTENER_QUERY:
case MLD_LISTENER_REPORT:
case MLD_LISTENER_REDUCTION:
case ND_ROUTER_SOLICIT:
case ND_ROUTER_ADVERT:
case ND_NEIGHBOR_SOLICIT:
case ND_NEIGHBOR_ADVERT:
return;
case ND_REDIRECT:
if (verbose) {
Printf("packet too short (%d bytes) from %s\n",
cc,
AF_INET6));
}
return;
}
Printf("ICMPv6 redirect from gateway %s\n",
Printf(" to %s",
Printf(" for %s\n",
}
break;
default:
if (verbose) {
for (i = 0; i < 12; i++) {
Printf("x%2.2x: x%8.8x\n",
}
}
break;
}
/*
* If it's verbose mode and we recv'd ancillary data, print extension
* headers.
*/
}
/*
* Convert an ICMP6 "type" field to a printable string.
*/
static char *
{
{ICMP6_DST_UNREACH, "Dest Unreachable"},
{ICMP6_PACKET_TOO_BIG, "Packet Too Big"},
{ICMP6_TIME_EXCEEDED, "Time Exceeded"},
{ICMP6_PARAM_PROB, "Parameter Problem"},
{ICMP6_ECHO_REQUEST, "Echo Request"},
{ICMP6_ECHO_REPLY, "Echo Reply"},
{MLD_LISTENER_QUERY, "Multicast Listener Query"},
{MLD_LISTENER_REPORT, "Multicast Listener Report"},
{MLD_LISTENER_REDUCTION, "Multicast Listener Done"},
{ND_ROUTER_SOLICIT, "Router Solicitation"},
{ND_ROUTER_ADVERT, "Router Advertisement"},
{ND_NEIGHBOR_SOLICIT, "Neighbor Solicitation"},
{ND_NEIGHBOR_ADVERT, "Neighbor Advertisement"},
{ND_REDIRECT, "Redirect Message"},
};
int i;
}
return ("OUT-OF-RANGE");
}
/*
* Return the length of the IPv6 related headers (including extension headers).
* It also sets the *last_hdr_rtrn to the first upper layer protocol header
* following IPv6 header and extension headers. If print_flag is _B_TRUE, it
* prints extension headers.
*/
static int
{
int length;
int exthdrlength;
return (length);
*last_hdr_rtrn = nexthdr;
switch (nexthdr) {
case IPPROTO_HOPOPTS:
return (length);
length += exthdrlength;
break;
case IPPROTO_DSTOPTS:
return (length);
length += exthdrlength;
break;
case IPPROTO_ROUTING:
return (length);
length += exthdrlength;
break;
case IPPROTO_FRAGMENT:
/* LINTED */
return (length);
break;
case IPPROTO_NONE:
default:
return (length);
}
}
*last_hdr_rtrn = nexthdr;
return (length);
}
/*
* Print extension headers
*/
static void
{
Printf(" IPv6 extension headers: ");
case IPV6_HOPOPTS:
Printf(" <hop-by-hop options>");
break;
case IPV6_DSTOPTS:
Printf(" <destination options (after routing"
"header)>");
break;
case IPV6_RTHDRDSTOPTS:
Printf(" <destination options (before routing"
"header)>");
break;
case IPV6_RTHDR:
break;
default:
break;
}
}
}
(void) putchar('\n');
}
/*
* Print the routing header 0 information
*/
static void
{
int i, num_addr;
Printf(" <type %d routing header, segleft %u> ",
/* LINTED */
for (i = 0; i < num_addr; i++) {
Printf("(Current)");
gw_addr++;
if (i != num_addr - 1)
Printf(", ");
}
}
}