#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 <libinetutil.h>
#include "traceroute.h"
struct ip_sourceroute {
uint8_t ipsr_code;
uint8_t ipsr_len;
uint8_t ipsr_ptr;
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 *);
struct ip *
set_buffers(int plen)
{
struct ip *outip;
uchar_t *outp;
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) {
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;
outip->ip_len = htons(plen - optlen);
outip->ip_off = htons(off);
outip->ip_hl = (outp - (uchar_t *)outip) >> 2;
if (useicmp) {
outip->ip_p = IPPROTO_ICMP;
outicmp = (struct icmp *)outp;
outicmp->icmp_type = ICMP_ECHO;
outicmp->icmp_id = htons(ident);
} else {
outip->ip_p = IPPROTO_UDP;
outudp = (struct udphdr *)outp;
outudp->uh_sport = htons(ident);
outudp->uh_ulen =
htons((ushort_t)(plen - (sizeof (struct ip) + optlen)));
}
return (outip);
}
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;
int gwV4_count;
if ((pe = getprotobyname("ip")) == NULL) {
Fprintf(stderr, "%s: unknown protocol ip\n", prog);
exit(EXIT_FAILURE);
}
gwV4_count = (gw_count < MAX_GWS) ? gw_count : MAX_GWS - 1;
gwIPlist[gwV4_count].addr = ip_addr->addr;
optlist[0] = IPOPT_NOP;
srp = (struct ip_sourceroute *)&optlist[1];
srp->ipsr_code = IPOPT_LSRR;
srp->ipsr_len = 3 + (gwV4_count + 1) * sizeof (gwIPlist[0].addr);
srp->ipsr_ptr = IPOPT_MINOFF;
for (i = 0; i <= gwV4_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);
}
}
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;
struct udphdr *outudp;
struct icmp *outicmp;
struct outdata *outdata;
struct ip tip;
int optlen = 0;
int send_size;
outp = (uchar_t *)(outip + 1);
outudp = (struct udphdr *)outp;
outicmp = (struct icmp *)outp;
outdata = (struct outdata *)(outp + ICMP_MINLEN);
if (gw_count > 0) {
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 (!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);
}
}
if (docksum) {
outip->ip_sum =
in_cksum((ushort_t *)outip, sizeof (*outip) + optlen);
if (outip->ip_sum == 0)
outip->ip_sum = 0xffff;
}
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)
((struct sockaddr_in *)to)->sin_port = outudp->uh_dport;
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 {
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);
}
}
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;
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;
icp = (struct icmp *)(buf + hlen);
*type = icp->icmp_type;
*code = icp->icmp_code;
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);
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 {
up = (struct udphdr *)((uchar_t *)hip + hlen);
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);
}
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");
}
void
print_addr(uchar_t *buf, int cc, struct sockaddr *from)
{
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;
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));
}
boolean_t
print_icmp_other(uchar_t type, uchar_t code)
{
boolean_t unreach = _B_FALSE;
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);
}