/*
* Copyright 2006 Sun Microsystems, Inc. All rights reserved.
* Use is subject to license terms.
*/
/*
* Copyright (c) 1988, 1989, 1991, 1994, 1995, 1996, 1997
* The Regents of the University of California. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that: (1) source code distributions
* retain the above copyright notice and this paragraph in its entirety, (2)
* distributions including binary code include the above copyright notice and
* this paragraph in its entirety in the documentation or other materials
* provided with the distribution, and (3) all advertising materials mentioning
* features or use of this software display the following acknowledgement:
* ``This product includes software developed by the University of California,
* Lawrence Berkeley Laboratory and its contributors.'' Neither the name of
* the University nor the names of its contributors may be used to endorse
* or promote products derived from this software without specific prior
* written permission.
* THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR IMPLIED
* WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED WARRANTIES OF
* MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.
*
*
* @(#)$Header: traceroute.c,v 1.49 97/06/13 02:30:23 leres Exp $ (LBL)
*/
#pragma ident "%Z%%M% %I% %E% SMI"
#include <stdio.h>
#include <stdlib.h>
#include <ctype.h>
#include <strings.h>
#include <libintl.h>
#include <errno.h>
#include <netdb.h>
#include <netinet/in_systm.h>
#include <libinetutil.h>
#include "traceroute.h"
void *find_ancillary_data(struct msghdr *, int, int);
extern char *inet_name(union any_in_addr *, int);
struct timeval *, int);
struct ip *set_buffers6(int);
/*
* prepares the buffer to be sent as an IP datagram
*/
struct ip *
{
int optlen = 0;
}
if (gw_count > 0) {
/* ip6_rthdr0 structure includes one gateway address */
optlen = sizeof (struct ip6_rthdr0) +
}
if (useicmp) {
/* LINTED E_BAD_PTR_CAST_ALIGN */
} else {
/* LINTED E_BAD_PTR_CAST_ALIGN */
/*
* "source port" is set at bind() call, so we don't do it
* again
*/
}
return (outip);
}
/*
* Initialize the msghdr for specifying hoplimit, outgoing interface and routing
* header for the probe packets.
*/
void
{
int i;
msgp->msg_controllen = 0;
/*
* 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;
/* We'll always set the hoplimit of the outgoing packets */
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.
*/
}
/*
* Fill ancillary data. First hoplimit, then rthdr and pktinfo if
* needed.
*/
/* set hoplimit ancillary data */
/* LINTED E_BAD_PTR_CAST_ALIGN */
*(int *)cmsg_datap = hoplimit;
/* set rthdr ancillary data if needed */
if (gw_cnt > 0) {
/*
* Initialize rthdr structure
*/
/* LINTED E_BAD_PTR_CAST_ALIGN */
prog);
}
/*
* Stuff in gateway addresses
*/
for (i = 0; i < gw_cnt; i++) {
if (inet6_rth_add(rthdr0p,
"%s: inet6_rth_add\n", prog);
}
}
}
/* set pktinfo ancillary data if needed */
if (if_index != 0) {
/* LINTED E_BAD_PTR_CAST_ALIGN */
/*
* 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.
*/
}
}
/*
* Parses the given msg->msg_control to find the IPV6_HOPLIMIT ancillary data
* and update the hoplimit.
* Returns _B_FALSE if it can't find IPV6_HOPLIMIT ancillary data, _B_TRUE
* otherwise.
*/
static boolean_t
{
int *intp;
/* LINTED E_BAD_PTR_CAST_ALIGN */
return (_B_TRUE);
}
}
return (_B_FALSE);
}
/*
* send a probe packet to the destination
*/
void
{
int cc;
int optlen = 0;
int send_size;
if (gw_count > 0) {
/* ip6_rthdr0 structure includes one gateway address */
optlen = sizeof (struct ip6_rthdr0) +
}
/* if using UDP, further discount UDP header size */
if (!useicmp)
/* initialize buffer pointers */
/* LINTED E_BAD_PTR_CAST_ALIGN */
/* LINTED E_BAD_PTR_CAST_ALIGN */
"%s: can't find IPV6_HOPLIMIT ancillary data\n", prog);
}
/* Payload */
if (useicmp) {
} else {
}
if (cc < 0) {
}
Printf("%s: wrote %s %d chars, ret=%d\n",
}
}
/*
* Return a pointer to the ancillary data for the given cmsg_level and
* cmsg_type.
* If not found return NULL.
*/
void *
{
}
}
return (NULL);
}
/*
* Check out the reply packet to see if it's what we were expecting.
* Returns REPLY_GOT_TARGET if the reply comes from the target
* REPLY_GOT_GATEWAY if an intermediate gateway sends TIME_EXCEEDED
* REPLY_GOT_OTHER for other kinds of unreachables indicating none of
* the above two cases
*
* It also sets the icmp type and icmp code values
*/
int
{
/* Ignore packets > 64k or control buffers that don't fit */
if (verbose) {
Printf("Truncated message: msg_flags 0x%x from %s\n",
}
return (REPLY_SHORT_PKT);
}
if (cc < ICMP6_MINLEN) {
if (verbose) {
Printf("packet too short (%d bytes) from %s\n",
cc,
}
return (REPLY_SHORT_PKT);
}
/* LINTED E_BAD_PTR_CAST_ALIGN */
/*
* traceroute interprets only ICMP6_TIME_EXCEED_TRANSIT,
* ICMP6_DST_UNREACH, ICMP6_ECHO_REPLY, ICMP6_PACKET_TOO_BIG and
* ICMP6_PARAMPROB_NEXTHEADER, ignores others
*/
if ((*type == ICMP6_TIME_EXCEEDED &&
*code == ICMP6_TIME_EXCEED_TRANSIT) ||
*type == ICMP6_PACKET_TOO_BIG ||
(*type == ICMP6_PARAM_PROB &&
*code == ICMP6_PARAMPROB_NEXTHEADER)) {
cc -= ICMP6_MINLEN;
cc -= ip6hdr_len;
if (useicmp) {
if (*type == ICMP6_ECHO_REPLY &&
return (REPLY_GOT_TARGET);
}
/* LINTED E_BAD_PTR_CAST_ALIGN */
if (ICMP6_MINLEN <= cc &&
last_hdr == IPPROTO_ICMPV6 &&
if (*type == ICMP6_TIME_EXCEEDED) {
return (REPLY_GOT_GATEWAY);
} else {
return (REPLY_GOT_OTHER);
}
}
} else {
/* LINTED E_BAD_PTR_CAST_ALIGN */
/*
* at least 4 bytes of UDP header is required for this
* check
*/
if (4 <= cc &&
last_hdr == IPPROTO_UDP &&
(MAX_PORT + 1))) {
if (*type == ICMP6_DST_UNREACH &&
*code == ICMP6_DST_UNREACH_NOPORT) {
return (REPLY_GOT_TARGET);
} else if (*type == ICMP6_TIME_EXCEEDED) {
return (REPLY_GOT_GATEWAY);
} else {
return (REPLY_GOT_OTHER);
}
}
}
}
if (verbose) {
int i, j;
"%s: can't find IPV6_PKTINFO ancillary data\n",
prog);
}
Printf("%s: icmp type %d (%s) code %d\n",
for (i = 0; i < cc; i += 4) {
Printf("%2d: x", i);
for (j = 0; ((j < 4) && ((i + j) < cc)); j++)
(void) putchar('\n');
}
}
return (REPLY_SHORT_PKT);
}
/*
* Return the length of the IPv6 related headers (including 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 E_BAD_PTR_CAST_ALIGN */
return (length);
break;
case IPPROTO_NONE:
default:
return (length);
}
}
*last_hdr_rtrn = nexthdr;
return (length);
}
/*
* 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, "Param 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 = 0;
}
return ("OUT-OF-RANGE");
}
/*
* print the IPv6 src address of the reply packet
*/
void
{
/* LINTED E_BAD_PTR_CAST_ALIGN */
char *resolved_name;
/* LINTED E_BAD_PTR_CAST_ALIGN */
sizeof (temp_buf));
if (!nflag)
/*
* If the IPv6 address cannot be resolved to hostname, inet_name()
* returns the IPv6 address as a string. In that case, we choose not
* to print it twice. This saves us space on display.
*/
else
if (verbose) {
sizeof (temp_buf)));
}
}
/*
* ICMP6 messages which doesn't mean we got the target, or we got a gateway, are
* processed here. It returns _B_TRUE if it's some sort of 'unreachable'.
*/
{
switch (type) {
/* this corresponds to "ICMP_UNREACH_NEEDFRAG" in ICMP */
case ICMP6_PACKET_TOO_BIG:
Printf(" !B");
break;
case ICMP6_PARAM_PROB:
/* this corresponds to "ICMP_UNREACH_PROTOCOL" in ICMP */
if (code == ICMP6_PARAMPROB_NEXTHEADER) {
Printf(" !R");
}
break;
case ICMP6_DST_UNREACH:
switch (code) {
case ICMP6_DST_UNREACH_NOPORT:
break;
Printf(" !H");
break;
case ICMP6_DST_UNREACH_ADMIN:
Printf(" !X");
break;
case ICMP6_DST_UNREACH_ADDR:
Printf(" !A");
break;
Printf(" !E");
break;
default:
break;
}
break;
default:
break;
}
return (unreach);
}