summaryrefslogtreecommitdiff
path: root/usr/src/cmd/cmd-inet/usr.sbin/traceroute/traceroute_aux.c
diff options
context:
space:
mode:
Diffstat (limited to 'usr/src/cmd/cmd-inet/usr.sbin/traceroute/traceroute_aux.c')
-rw-r--r--usr/src/cmd/cmd-inet/usr.sbin/traceroute/traceroute_aux.c566
1 files changed, 566 insertions, 0 deletions
diff --git a/usr/src/cmd/cmd-inet/usr.sbin/traceroute/traceroute_aux.c b/usr/src/cmd/cmd-inet/usr.sbin/traceroute/traceroute_aux.c
new file mode 100644
index 0000000000..e8947f9ccb
--- /dev/null
+++ b/usr/src/cmd/cmd-inet/usr.sbin/traceroute/traceroute_aux.c
@@ -0,0 +1,566 @@
+/*
+ * Copyright 2004 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 <sys/socket.h>
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <ctype.h>
+#include <strings.h>
+#include <libintl.h>
+#include <errno.h>
+
+#include <netinet/in_systm.h>
+#include <netinet/in.h>
+#include <netinet/ip.h>
+#include <netinet/ip_var.h>
+#include <netinet/ip_icmp.h>
+#include <netinet/udp.h>
+#include <netinet/udp_var.h>
+
+#include <arpa/inet.h>
+#include <netdb.h>
+
+#include <ifaddrlist.h>
+#include "traceroute.h"
+
+/*
+ * IPv4 source routing option.
+ * In order to avoid padding for the alignment of IPv4 addresses, ipsr_addrs
+ * is defined as a 2-D array of uint8_t, instead of 1-D array of struct in_addr.
+ */
+struct ip_sourceroute {
+ uint8_t ipsr_code;
+ uint8_t ipsr_len;
+ uint8_t ipsr_ptr;
+ /* up to 9 IPv4 addresses */
+ uint8_t ipsr_addrs[1][sizeof (struct in_addr)];
+};
+
+int check_reply(struct msghdr *, int, int, uchar_t *, uchar_t *);
+extern ushort_t in_cksum(ushort_t *, int);
+extern char *inet_name(union any_in_addr *, int);
+static char *pr_type(uchar_t);
+void print_addr(uchar_t *, int, struct sockaddr *);
+boolean_t print_icmp_other(uchar_t, uchar_t);
+void send_probe(int, struct sockaddr *, struct ip *, int, int,
+ struct timeval *, int);
+struct ip *set_buffers(int);
+void set_IPv4opt_sourcerouting(int, union any_in_addr *, union any_in_addr *);
+
+/*
+ * prepares the buffer to be sent as an IP datagram
+ */
+struct ip *
+set_buffers(int plen)
+{
+ struct ip *outip;
+ uchar_t *outp; /* packet following the IP header (UDP/ICMP) */
+ struct udphdr *outudp;
+ struct icmp *outicmp;
+ int optlen = 0;
+
+ outip = (struct ip *)malloc((size_t)plen);
+ if (outip == NULL) {
+ Fprintf(stderr, "%s: malloc: %s\n", prog, strerror(errno));
+ exit(EXIT_FAILURE);
+ }
+
+ if (gw_count > 0) {
+ /* 8 = 5 (NO OPs) + 3 (code, len, ptr) */
+ optlen = 8 + gw_count * sizeof (struct in_addr);
+ }
+
+ (void) memset((char *)outip, 0, (size_t)plen);
+ outp = (uchar_t *)(outip + 1);
+
+ outip->ip_v = IPVERSION;
+ if (settos)
+ outip->ip_tos = tos;
+
+ /*
+ * LBNL bug fixed: missing '- optlen' before, causing optlen
+ * added twice
+ *
+ * BSD bug: BSD touches the header fields 'len' and 'ip_off'
+ * even when HDRINCL is set. It applies htons() on these
+ * fields. It should send the header untouched when HDRINCL
+ * is set.
+ */
+ outip->ip_len = htons(plen - optlen);
+ outip->ip_off = htons(off);
+ outip->ip_hl = (outp - (uchar_t *)outip) >> 2;
+
+ /* setup ICMP or UDP */
+ if (useicmp) {
+ outip->ip_p = IPPROTO_ICMP;
+
+ /* LINTED E_BAD_PTR_CAST_ALIGN */
+ outicmp = (struct icmp *)outp;
+ outicmp->icmp_type = ICMP_ECHO;
+ outicmp->icmp_id = htons(ident);
+ } else {
+ outip->ip_p = IPPROTO_UDP;
+
+ /* LINTED E_BAD_PTR_CAST_ALIGN */
+ outudp = (struct udphdr *)outp;
+ outudp->uh_sport = htons(ident);
+ outudp->uh_ulen =
+ htons((ushort_t)(plen - (sizeof (struct ip) + optlen)));
+ }
+
+ return (outip);
+}
+
+/*
+ * Setup the source routing for IPv4.
+ */
+void
+set_IPv4opt_sourcerouting(int sndsock, union any_in_addr *ip_addr,
+ union any_in_addr *gwIPlist)
+{
+ struct protoent *pe;
+ struct ip_sourceroute *srp;
+ uchar_t optlist[MAX_IPOPTLEN];
+ int i;
+
+ if ((pe = getprotobyname("ip")) == NULL) {
+ Fprintf(stderr, "%s: unknown protocol ip\n", prog);
+ exit(EXIT_FAILURE);
+ }
+
+ /* final hop */
+ gwIPlist[gw_count].addr = ip_addr->addr;
+
+ /*
+ * the option length passed to setsockopt() needs to be a multiple of
+ * 32 bits. Therefore we need to use a 1-byte padding (source routing
+ * information takes 4x+3 bytes).
+ */
+ optlist[0] = IPOPT_NOP;
+
+ srp = (struct ip_sourceroute *)&optlist[1];
+ srp->ipsr_code = IPOPT_LSRR;
+ /* 3 = 1 (code) + 1 (len) + 1 (ptr) */
+ srp->ipsr_len = 3 + (gw_count + 1) * sizeof (gwIPlist[0].addr);
+ srp->ipsr_ptr = IPOPT_MINOFF;
+
+ for (i = 0; i <= gw_count; i++) {
+ (void) bcopy((char *)&gwIPlist[i].addr, &srp->ipsr_addrs[i],
+ sizeof (struct in_addr));
+ }
+
+ if (setsockopt(sndsock, pe->p_proto, IP_OPTIONS, (const char *)optlist,
+ srp->ipsr_len + 1) < 0) {
+ Fprintf(stderr, "%s: IP_OPTIONS: %s\n", prog, strerror(errno));
+ exit(EXIT_FAILURE);
+ }
+}
+
+/*
+ * send a probe packet to the destination
+ */
+void
+send_probe(int sndsock, struct sockaddr *to, struct ip *outip,
+ int seq, int ttl, struct timeval *tp, int packlen)
+{
+ int cc;
+ struct udpiphdr *ui;
+ uchar_t *outp; /* packet following the IP header (UDP/ICMP) */
+ struct udphdr *outudp;
+ struct icmp *outicmp;
+ struct outdata *outdata;
+ struct ip tip;
+ int optlen = 0;
+ int send_size;
+
+ /* initialize buffer pointers */
+ outp = (uchar_t *)(outip + 1);
+ /* LINTED E_BAD_PTR_CAST_ALIGN */
+ outudp = (struct udphdr *)outp;
+ /* LINTED E_BAD_PTR_CAST_ALIGN */
+ outicmp = (struct icmp *)outp;
+ /* LINTED E_BAD_PTR_CAST_ALIGN */
+ outdata = (struct outdata *)(outp + ICMP_MINLEN);
+
+ if (gw_count > 0) {
+ /* 8 = 5 (NO OPs) + 3 (code, len, ptr) */
+ optlen = 8 + gw_count * sizeof (struct in_addr);
+ }
+
+ if (raw_req) {
+ send_size = packlen - optlen;
+ } else if (useicmp) {
+ send_size = packlen - optlen - sizeof (struct ip);
+ } else {
+ send_size = packlen - optlen - sizeof (struct ip) -
+ sizeof (struct udphdr);
+ }
+
+ outip->ip_ttl = ttl;
+ outip->ip_id = htons(ident + seq);
+
+ /*
+ * If a raw IPv4 packet is going to be sent, the Time to Live
+ * field in the packet was initialized above. Otherwise, it is
+ * initialized here using the IPPROTO_IP level socket option.
+ */
+ if (!raw_req) {
+ if (setsockopt(sndsock, IPPROTO_IP, IP_TTL, (char *)&ttl,
+ sizeof (ttl)) < 0) {
+ Fprintf(stderr, "%s: IP_TTL: %s\n", prog,
+ strerror(errno));
+ exit(EXIT_FAILURE);
+ }
+ }
+
+ /*
+ * In most cases, the kernel will recalculate the ip checksum.
+ * But we must do it anyway so that the udp checksum comes out
+ * right.
+ */
+ if (docksum) {
+ outip->ip_sum =
+ in_cksum((ushort_t *)outip, sizeof (*outip) + optlen);
+ if (outip->ip_sum == 0)
+ outip->ip_sum = 0xffff;
+ }
+
+ /* Payload */
+ outdata->seq = seq;
+ outdata->ttl = ttl;
+ outdata->tv = *tp;
+
+ if (useicmp) {
+ outicmp->icmp_seq = htons(seq);
+ } else {
+ outudp->uh_dport = htons((port + seq) % (MAX_PORT + 1));
+ }
+
+ if (!raw_req)
+ /* LINTED E_BAD_PTR_CAST_ALIGN */
+ ((struct sockaddr_in *)to)->sin_port = outudp->uh_dport;
+
+ /* (We can only do the checksum if we know our ip address) */
+ if (docksum) {
+ if (useicmp) {
+ outicmp->icmp_cksum = 0;
+ outicmp->icmp_cksum = in_cksum((ushort_t *)outicmp,
+ packlen - (sizeof (struct ip) + optlen));
+ if (outicmp->icmp_cksum == 0)
+ outicmp->icmp_cksum = 0xffff;
+ } else {
+ /* Checksum (must save and restore ip header) */
+ tip = *outip;
+ ui = (struct udpiphdr *)outip;
+ ui->ui_next = 0;
+ ui->ui_prev = 0;
+ ui->ui_x1 = 0;
+ ui->ui_len = outudp->uh_ulen;
+ outudp->uh_sum = 0;
+ outudp->uh_sum = in_cksum((ushort_t *)ui, packlen);
+ if (outudp->uh_sum == 0)
+ outudp->uh_sum = 0xffff;
+ *outip = tip;
+ }
+ }
+
+ if (raw_req) {
+ cc = sendto(sndsock, (char *)outip, send_size, 0, to,
+ sizeof (struct sockaddr_in));
+ } else if (useicmp) {
+ cc = sendto(sndsock, (char *)outicmp, send_size, 0, to,
+ sizeof (struct sockaddr_in));
+ } else {
+ cc = sendto(sndsock, (char *)outp, send_size, 0, to,
+ sizeof (struct sockaddr_in));
+ }
+
+ if (cc < 0 || cc != send_size) {
+ if (cc < 0) {
+ Fprintf(stderr, "%s: sendto: %s\n", prog,
+ strerror(errno));
+ }
+ Printf("%s: wrote %s %d chars, ret=%d\n",
+ prog, hostname, send_size, cc);
+ (void) fflush(stdout);
+ }
+}
+
+/*
+ * 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
+check_reply(struct msghdr *msg, int cc, int seq, uchar_t *type, uchar_t *code)
+{
+ uchar_t *buf = msg->msg_iov->iov_base;
+ struct sockaddr_in *from_in = (struct sockaddr_in *)msg->msg_name;
+ struct icmp *icp;
+ int hlen;
+ int save_cc = cc;
+ struct ip *ip;
+
+ /* LINTED E_BAD_PTR_CAST_ALIGN */
+ ip = (struct ip *)buf;
+ hlen = ip->ip_hl << 2;
+ if (cc < hlen + ICMP_MINLEN) {
+ if (verbose) {
+ Printf("packet too short (%d bytes) from %s\n",
+ cc, inet_ntoa(from_in->sin_addr));
+ }
+ return (REPLY_SHORT_PKT);
+ }
+ cc -= hlen;
+ /* LINTED E_BAD_PTR_CAST_ALIGN */
+ icp = (struct icmp *)(buf + hlen);
+
+ *type = icp->icmp_type;
+ *code = icp->icmp_code;
+
+ /*
+ * traceroute interpretes only ICMP_TIMXCEED_INTRANS, ICMP_UNREACH and
+ * ICMP_ECHOREPLY, ignores others
+ */
+ if ((*type == ICMP_TIMXCEED && *code == ICMP_TIMXCEED_INTRANS) ||
+ *type == ICMP_UNREACH || *type == ICMP_ECHOREPLY) {
+ struct ip *hip;
+ struct udphdr *up;
+ struct icmp *hicmp;
+
+ cc -= ICMP_MINLEN;
+ hip = &icp->icmp_ip;
+ hlen = hip->ip_hl << 2;
+ cc -= hlen;
+ if (useicmp) {
+ if (*type == ICMP_ECHOREPLY &&
+ icp->icmp_id == htons(ident) &&
+ icp->icmp_seq == htons(seq))
+ return (REPLY_GOT_TARGET);
+
+ /* LINTED E_BAD_PTR_CAST_ALIGN */
+ hicmp = (struct icmp *)((uchar_t *)hip + hlen);
+
+ if (ICMP_MINLEN <= cc &&
+ hip->ip_p == IPPROTO_ICMP &&
+ hicmp->icmp_id == htons(ident) &&
+ hicmp->icmp_seq == htons(seq)) {
+ return ((*type == ICMP_TIMXCEED) ?
+ REPLY_GOT_GATEWAY : REPLY_GOT_OTHER);
+ }
+ } else {
+ /* LINTED E_BAD_PTR_CAST_ALIGN */
+ up = (struct udphdr *)((uchar_t *)hip + hlen);
+ /*
+ * at least 4 bytes of UDP header is required for this
+ * check
+ */
+ if (4 <= cc &&
+ hip->ip_p == IPPROTO_UDP &&
+ up->uh_sport == htons(ident) &&
+ up->uh_dport == htons((port + seq) %
+ (MAX_PORT + 1))) {
+ if (*type == ICMP_UNREACH &&
+ *code == ICMP_UNREACH_PORT) {
+ return (REPLY_GOT_TARGET);
+ } else if (*type == ICMP_TIMXCEED) {
+ return (REPLY_GOT_GATEWAY);
+ } else {
+ return (REPLY_GOT_OTHER);
+ }
+ }
+ }
+ }
+
+ if (verbose) {
+ int i, j;
+ uchar_t *lp = (uchar_t *)ip;
+
+ cc = save_cc;
+ Printf("\n%d bytes from %s to ", cc,
+ inet_ntoa(from_in->sin_addr));
+ Printf("%s: icmp type %d (%s) code %d\n",
+ inet_ntoa(ip->ip_dst), *type, pr_type(*type), *code);
+ for (i = 0; i < cc; i += 4) {
+ Printf("%2d: x", i);
+ for (j = 0; ((j < 4) && ((i + j) < cc)); j++)
+ Printf("%2.2x", *lp++);
+ (void) putchar('\n');
+ }
+ }
+
+ return (REPLY_SHORT_PKT);
+}
+
+/*
+ * convert an ICMP "type" field to a printable string.
+ */
+static char *
+pr_type(uchar_t type)
+{
+ static struct icmptype_table ttab[] = {
+ {ICMP_ECHOREPLY, "Echo Reply"},
+ {1, "ICMP 1"},
+ {2, "ICMP 2"},
+ {ICMP_UNREACH, "Dest Unreachable"},
+ {ICMP_SOURCEQUENCH, "Source Quench"},
+ {ICMP_REDIRECT, "Redirect"},
+ {6, "ICMP 6"},
+ {7, "ICMP 7"},
+ {ICMP_ECHO, "Echo"},
+ {ICMP_ROUTERADVERT, "Router Advertisement"},
+ {ICMP_ROUTERSOLICIT, "Router Solicitation"},
+ {ICMP_TIMXCEED, "Time Exceeded"},
+ {ICMP_PARAMPROB, "Param Problem"},
+ {ICMP_TSTAMP, "Timestamp"},
+ {ICMP_TSTAMPREPLY, "Timestamp Reply"},
+ {ICMP_IREQ, "Info Request"},
+ {ICMP_IREQREPLY, "Info Reply"},
+ {ICMP_MASKREQ, "Netmask Request"},
+ {ICMP_MASKREPLY, "Netmask Reply"}
+ };
+ int i = 0;
+
+ for (i = 0; i < A_CNT(ttab); i++) {
+ if (ttab[i].type == type)
+ return (ttab[i].message);
+ }
+
+ return ("OUT-OF-RANGE");
+}
+
+/*
+ * print the IPv4 src address of the reply packet
+ */
+void
+print_addr(uchar_t *buf, int cc, struct sockaddr *from)
+{
+ /* LINTED E_BAD_PTR_CAST_ALIGN */
+ struct sockaddr_in *from_in = (struct sockaddr_in *)from;
+ struct ip *ip;
+ union any_in_addr ip_addr;
+
+ ip_addr.addr = from_in->sin_addr;
+
+ /* LINTED E_BAD_PTR_CAST_ALIGN */
+ ip = (struct ip *)buf;
+
+ if (nflag) {
+ Printf(" %s", inet_ntoa(from_in->sin_addr));
+ } else {
+ Printf(" %s (%s)", inet_name(&ip_addr, AF_INET),
+ inet_ntoa(from_in->sin_addr));
+ }
+
+ if (verbose)
+ Printf(" %d bytes to %s", cc, inet_ntoa(ip->ip_dst));
+}
+
+/*
+ * ICMP 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'.
+ */
+boolean_t
+print_icmp_other(uchar_t type, uchar_t code)
+{
+ boolean_t unreach = _B_FALSE;
+
+ /*
+ * this function only prints '!*' for ICMP unreachable messages,
+ * ignores others.
+ */
+ if (type != ICMP_UNREACH) {
+ return (_B_FALSE);
+ }
+
+ switch (code) {
+ case ICMP_UNREACH_PORT:
+ break;
+
+ case ICMP_UNREACH_NET_UNKNOWN:
+ case ICMP_UNREACH_NET:
+ unreach = _B_TRUE;
+ Printf(" !N");
+ break;
+
+ case ICMP_UNREACH_HOST_UNKNOWN:
+ case ICMP_UNREACH_HOST:
+ unreach = _B_TRUE;
+ Printf(" !H");
+ break;
+
+ case ICMP_UNREACH_PROTOCOL:
+ Printf(" !P");
+ break;
+
+ case ICMP_UNREACH_NEEDFRAG:
+ unreach = _B_TRUE;
+ Printf(" !F");
+ break;
+
+ case ICMP_UNREACH_SRCFAIL:
+ unreach = _B_TRUE;
+ Printf(" !S");
+ break;
+
+ case ICMP_UNREACH_FILTER_PROHIB:
+ case ICMP_UNREACH_NET_PROHIB:
+ case ICMP_UNREACH_HOST_PROHIB:
+ unreach = _B_TRUE;
+ Printf(" !X");
+ break;
+
+ case ICMP_UNREACH_TOSNET:
+ case ICMP_UNREACH_TOSHOST:
+ unreach = _B_TRUE;
+ Printf(" !T");
+ break;
+
+ case ICMP_UNREACH_ISOLATED:
+ case ICMP_UNREACH_HOST_PRECEDENCE:
+ case ICMP_UNREACH_PRECEDENCE_CUTOFF:
+ unreach = _B_TRUE;
+ Printf(" !U");
+ break;
+
+ default:
+ unreach = _B_TRUE;
+ Printf(" !<%d>", code);
+ break;
+ }
+
+ return (unreach);
+}