/*
* Copyright (c) 1994, 1995, 1996, 1997, 1998, 1999, 2000, 2001,
* 2002, 2003, 2004
*
* ---
*
* Starting with the release of tcptrace version 6 in 2001, tcptrace
* is licensed under the GNU General Public License (GPL). We believe
* that, among the available licenses, the GPL will do the best job of
* allowing tcptrace to continue to be a valuable, freely-available
* and well-maintained tool for the networking community.
*
* Previous versions of tcptrace were released under a license that
* was much less restrictive with respect to how tcptrace could be
* used in commercial products. Because of this, I am willing to
* consider alternate license arrangements as allowed in Section 10 of
* the GNU GPL. Before I would consider licensing tcptrace under an
* alternate agreement with a particular individual or company,
* however, I would have to be convinced that such an alternative
* would be to the greater benefit of the networking community.
*
* ---
*
* This file is part of Tcptrace.
*
* Tcptrace was originally written and continues to be maintained by
* Shawn Ostermann with the help of a group of devoted students and
* users (see the file 'THANKS'). The work on tcptrace has been made
* possible over the years through the generous support of NASA GRC,
* the National Science Foundation, and Sun Microsystems.
*
* Tcptrace is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* Tcptrace is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with Tcptrace (in the file 'COPYING'); if not, write to the
* Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston,
* MA 02111-1307 USA
*
* Author: Shawn Ostermann
* School of Electrical Engineering and Computer Science
* Ohio University
* Athens, OH
* ostermann@cs.ohiou.edu
* http://www.tcptrace.org/
*/
#include "tcptrace.h"
static char const GCC_UNUSED copyright[] =
"@(#)Copyright (c) 2004 -- Ohio University.\n";
static char const GCC_UNUSED rcsid[] =
"@(#)$Header: /usr/local/cvs/tcptrace/print.c,v 5.27 2003/11/19 14:38:05 sdo Exp $";
/*
* print.c -- packet printing routines
*/
/* local routines */
static void printeth_packet(struct ether_header *);
static void printip_packet(struct ip *, void *plast);
static void printtcp_packet(struct ip *, void *plast, tcb *tcb);
static void printudp_packet(struct ip *, void *plast);
static char *ParenServiceName(portnum);
static char *ParenHostName(struct ipaddr addr);
static void printipv4(struct ip *pip, void *plast);
static void printipv6(struct ipv6 *pipv6, void *plast);
static char *ipv6addr2str(struct in6_addr addr);
static void printipv4_opt_addrs(char *popt, int ptr, int len);
static char *PrintSeqRep(tcb *ptcb, u_long seq);
/* Resulting string format: "Fri Sep 13 00:00:00.123456 1986" */
/* 1 2 3 */
/* 0123456789012345678901234567890 */
char *
ts2ascii(
struct timeval *ptime)
{
static char buf[32];
struct tm *ptm;
char *now;
int decimal;
if (ZERO_TIME(ptime))
return(" <the epoch> ");
ptm = localtime((time_t *)&ptime->tv_sec);
now = asctime(ptm);
/* splice in the microseconds */
now[19] = '\00';
/* decimal = (ptime->tv_usec + 50) / 100;*/ /* for 4 digits */
decimal = ptime->tv_usec; /* for 6 digits */
now[24] = '\00'; /* nuke the newline */
snprintf(buf,sizeof(buf), "%s.%06d %s", now, decimal, &now[20]);
return(buf);
}
/* same as ts2ascii, but no year */
char *
ts2ascii_date(
struct timeval *ptime)
{
static char buf[30];
struct tm *ptm;
char *now;
int decimal;
if (ZERO_TIME(ptime))
return(" <the epoch> ");
ptm = localtime((time_t *)&ptime->tv_sec);
now = asctime(ptm);
now[24] = '\00';
/* decimal = (ptime->tv_usec + 50) / 100;*/ /* for 4 digits */
decimal = ptime->tv_usec; /* for 6 digits */
snprintf(buf,sizeof(buf), "%s.%06d", now, decimal);
return(buf);
}
static void
printeth_packet(
struct ether_header *pep)
{
printf("\tETH Srce: %s\n", Ether_Ntoa((struct ether_addr *)&pep->ether_shost));
printf("\tETH Dest: %s\n", Ether_Ntoa((struct ether_addr *)&pep->ether_dhost));
printf(
hex?"\t Type: 0x%x %s\n":"\t Type: %d %s\n",
ntohs(pep->ether_type),
(ntohs(pep->ether_type) == ETHERTYPE_IP)?"(IP)":
(ntohs(pep->ether_type) == ETHERTYPE_IPV6)?"(IPv6)":
(ntohs(pep->ether_type) == ETHERTYPE_ARP)?"(ARP)":
(ntohs(pep->ether_type) == ETHERTYPE_REVARP)?"(RARP)":
"");
}
static void
printip_packet(
struct ip *pip,
void *plast)
{
/* print an ipv6 header */
if (PIP_ISV6(pip)) {
if ((char *)pip+sizeof(struct ipv6)-1 > (char *)plast) {
if (warn_printtrunc)
printf("\t[packet truncated too short for IP details]\n");
++ctrunc;
return;
}
printipv6((struct ipv6 *)pip, plast);
return;
}
if (PIP_ISV4(pip)) {
/* make sure we have enough of the packet */
if ((char *)pip+sizeof(struct ip)-1 > (char *)plast) {
if (warn_printtrunc)
printf("\t[packet truncated too short for IP details]\n");
++ctrunc;
return;
}
printipv4(pip, plast);
return;
}
/* unknown type */
printf("Unknown IP version %d\n", PIP_VERS(pip));
}
static void
printipv4(
struct ip *pip,
void *plast)
{
u_short offset;
Bool mf;
/* make sure we have enough of the packet */
if ((char *)pip+sizeof(struct ip)-1 > (char *)plast) {
if (warn_printtrunc)
printf("\t[packet truncated too short for IP details]\n");
++ctrunc;
return;
}
printf("\tIP VERS: %d\n", IP_V(pip));
printf("\tIP Srce: %s %s\n",
inet_ntoa(pip->ip_src),
ParenHostName(*IPV4ADDR2ADDR(&pip->ip_src)));
printf("\tIP Dest: %s %s\n",
inet_ntoa(pip->ip_dst),
ParenHostName(*IPV4ADDR2ADDR(&pip->ip_dst)));
printf(
hex?"\t Type: 0x%x %s\n":"\t Type: %d %s\n",
pip->ip_p,
(pip->ip_p == IPPROTO_UDP)?"(UDP)":
(pip->ip_p == IPPROTO_TCP)?"(TCP)":
(pip->ip_p == IPPROTO_ICMP)?"(ICMP)":
(pip->ip_p == IPPROTO_IGMP)?"(IGMP)":
(pip->ip_p == IPPROTO_EGP)?"(EGP)":
"");
printf("\t HLEN: %d\n", IP_HL(pip)*4);
printf("\t TTL: %d\n", pip->ip_ttl);
printf("\t LEN: %d\n", ntohs(pip->ip_len));
printf("\t ID: %d\n", ntohs(pip->ip_id));
printf("\t CKSUM: 0x%04x", ntohs(pip->ip_sum));
if (verify_checksums)
printf(" (%s)", ip_cksum_valid(pip,plast)?"CORRECT":"WRONG");
printf("\n");
/* fragmentation stuff */
offset = ntohs(pip->ip_off) << 3;
mf = (ntohs(pip->ip_off) & IP_MF) != 0;
if ((offset == 0) && (!mf)) {
printf("\t OFFSET: 0x%04x", ntohs(pip->ip_off));
} else {
printf("\t OFFSET: 0x%04x (frag: %d bytes at offset %u - %s)",
ntohs(pip->ip_off),
ntohs(pip->ip_len)-IP_HL(pip)*4,
offset,
mf?"More Frags":"Last Frag");
}
if ((ntohs(pip->ip_off) & IP_DF) != 0)
printf(" Don't Fragment\n"); /* don't fragment */
/* print IP options if there are any */
if (IP_HL(pip) != 5) {
char *popt = (char *)pip + 20;
void *plast_option;
/* find the last option in the file */
plast_option = (char *)pip+4*IP_HL(pip)-1;
if (plast_option > plast)
plast_option = plast; /* truncated shorter than that */
printf("\t Options: %d bytes\n", 4*IP_HL(pip)-20);
while ((char *)popt <= (char *)plast_option) {
u_int opt = *popt;
u_int len = *(popt+1);
u_int ptr = *(popt+2);
int optcopy = (opt&0x80);
int optclass = (opt&0x60)>>5;
int optnum = (opt&0x1f);
/* check for truncated option */
if ((void *)(popt+len-1) > plast) {
printf("\t IP option (truncated)\n");
break;
}
printf("\t IP option %d (copy:%c class:%s number:%d)\n",
opt,
optcopy==0?'N':'Y',
optclass==0?"ctrl":
optclass==1?"reserved1":
optclass==2?"debug":
optclass==3?"reserved3":"unknown",
optnum);
switch(opt) {
case 3:
printf("\t Loose source route: len: %d ptr:%d\n",
len, ptr);
printipv4_opt_addrs(popt, ptr, len);
break;
case 7:
printf("\t Record Route: len: %d ptr:%d\n",
len, ptr);
printipv4_opt_addrs(popt, ptr, len);
break;
case 9:
printf("\t Strict source route: len: %d ptr:%d\n",
len, ptr);
printipv4_opt_addrs(popt, ptr, len);
break;
case 4:
printf("\t Timestamps: len: %d ptr:%d\n",
len, ptr);
break;
case 0:
printf("\t EOL\n");
len = 1;
break;
case 1:
printf("\t PADDING\n");
len = 1;
break;
default:
printf("\t Unknown Option %d, len: %d\n", opt, len);
break;
}
if (len <= 0)
break;
popt += len;
}
}
printf("\n");
}
/* print out the little table in the source route and record route options */
static void
printipv4_opt_addrs(
char *popt,
int ptr,
int len)
{
struct in_addr ina;
int nptr;
int i;
for (nptr=4,i=1;nptr < len; nptr += 4,++i) {
memcpy(&ina.s_addr,popt+nptr-1,4);
if (nptr < ptr)
printf("\t %d: %-15s %s\n",
i, inet_ntoa(ina),
HostName(*IPV4ADDR2ADDR(&ina)));
else
printf("\t %d: xxxxxxxxxxx\n", i);
}
}
static void
printtcp_packet(
struct ip *pip,
void *plast,
tcb *thisdir)
{
unsigned tcp_length;
unsigned tcp_data_length;
struct tcphdr *ptcp;
int i;
u_char *pdata;
struct ipv6 *pipv6;
tcb *otherdir = NULL;
/* find the tcp header */
if (gettcp(pip, &ptcp, &plast))
return; /* not TCP or bad TCP packet */
/* make sure we have enough of the packet */
if ((char *)ptcp+sizeof(struct tcphdr)-1 > (char *)plast) {
if (warn_printtrunc)
printf("\t[packet truncated too short for TCP details]\n");
++ctrunc;
return;
}
/* calculate data length */
if (PIP_ISV6(pip)) {
pipv6 = (struct ipv6 *) pip;
tcp_length = ntohs(pipv6->ip6_lngth);
} else {
tcp_length = ntohs(pip->ip_len) - (4 * IP_HL(pip));
}
tcp_data_length = tcp_length - (4 * TH_OFF(ptcp));
/* find the tcb's (if available) */
if (thisdir)
otherdir = thisdir->ptwin;
printf("\tTCP SPRT: %u %s\n",
ntohs(ptcp->th_sport),
ParenServiceName(ntohs(ptcp->th_sport)));
printf("\t DPRT: %u %s\n",
ntohs(ptcp->th_dport),
ParenServiceName(ntohs(ptcp->th_dport)));
printf("\t FLG: %c%c%c%c%c%c%c%c (0x%02x)\n",
FLAG6_SET(ptcp)? '?':' ',
FLAG7_SET(ptcp)? '?':' ',
URGENT_SET(ptcp)?'U':'-',
ACK_SET(ptcp)? 'A':'-',
PUSH_SET(ptcp)? 'P':'-',
RESET_SET(ptcp)? 'R':'-',
SYN_SET(ptcp)? 'S':'-',
FIN_SET(ptcp)? 'F':'-',
ptcp->th_flags);
printf("\t SEQ: %s\n", PrintSeqRep(thisdir, ntohl(ptcp->th_seq)));
printf("\t ACK: %s\n", PrintSeqRep(otherdir, ntohl(ptcp->th_ack)));
printf("\t WIN: %u\n", ntohs(ptcp->th_win));
printf("\t HLEN: %u", TH_OFF(ptcp)*4);
if ((char *)ptcp + TH_OFF(ptcp)*4 - 1 > (char *)plast) {
/* not all there */
printf(" (only %lu bytes in dump file)",
(u_long)((char *)plast - (char *)ptcp + 1));
}
printf("\n");
if (TH_X2(ptcp) != 0) {
printf("\t MBZ: 0x%01x (these are supposed to be zero!)\n",
TH_X2(ptcp));
}
printf("\t CKSUM: 0x%04x", ntohs(ptcp->th_sum));
pdata = (u_char *)ptcp + TH_OFF(ptcp)*4;
if (verify_checksums) {
if ((char *)pdata + tcp_data_length > ((char *)plast+1))
printf(" (too short to verify)");
else
printf(" (%s)", tcp_cksum_valid(pip,ptcp,plast)?"CORRECT":"WRONG");
}
printf("\n");
printf("\t DLEN: %u", tcp_data_length);
if ((tcp_data_length != 0) &&
((char *)pdata + tcp_data_length > ((char *)plast+1))) {
int available = (char *)plast - (char *)pdata + 1;
if (available > 1)
printf(" (only %lu bytes in dump file)",
(u_long)((char *)plast - (char *)pdata + 1));
else
printf(" (none of it in dump file)");
}
printf("\n");
if (TH_OFF(ptcp) != 5) {
struct tcp_options *ptcpo;
printf("\t OPTS: %lu bytes",
(unsigned long)(TH_OFF(ptcp)*4) - sizeof(struct tcphdr));
if ((char *)ptcp + TH_OFF(ptcp)*4 - 1 > (char *)plast) {
/* not all opts were stored */
u_long available = 1 + (char *)plast -
((char *)ptcp + sizeof(struct tcphdr));
if (available > 1)
printf(" (%lu bytes in file)", available);
else
printf(" (none of it in dump file)");
}
printf("\t");
ptcpo = ParseOptions(ptcp,plast);
if (ptcpo->mss != -1)
printf(" MSS(%d)", ptcpo->mss);
if (ptcpo->ws != -1)
printf(" WS(%d)", ptcpo->ws);
if (ptcpo->tsval != -1) {
printf(" TS(%ld,%ld)", ptcpo->tsval, ptcpo->tsecr);
}
if (ptcpo->sack_req) {
printf(" SACKREQ");
}
if (ptcpo->sack_count >= 0) {
printf(" SACKS(%d)", ptcpo->sack_count);
for (i=0; i < ptcpo->sack_count; ++i) {
printf("[%s-",
PrintSeqRep(otherdir,
(u_long)ptcpo->sacks[i].sack_left));
printf("%s]",
PrintSeqRep(otherdir,
(u_long)ptcpo->sacks[i].sack_right));
}
}
if (ptcpo->echo_req != -1)
printf(" ECHO(%lu)", ptcpo->echo_req);
if (ptcpo->echo_repl != -1)
printf(" ECHOREPL(%lu)", ptcpo->echo_repl);
if (ptcpo->cc != -1)
printf(" CC(%lu)", ptcpo->cc);
if (ptcpo->ccnew != -1)
printf(" CCNEW(%lu)", ptcpo->ccnew);
if (ptcpo->ccecho != -1)
printf(" CCECHO(%lu)", ptcpo->ccecho);
for (i=0; i < ptcpo->unknown_count; ++i) {
if (i < MAX_UNKNOWN) {
printf(" UNKN(op:%d,len:%d)",
ptcpo->unknowns[i].unkn_opt,
ptcpo->unknowns[i].unkn_len);
} else {
printf("... more unsaved unknowns\n");
break;
}
}
printf("\n");
}
if (tcp_data_length > 0) {
if (dump_packet_data) {
char *ptcp_data = (char *)ptcp + (4 * TH_OFF(ptcp));
PrintRawData(" data", ptcp_data, plast, TRUE);
} else {
printf("\t data: %u bytes\n", tcp_data_length);
}
}
}
static void
printudp_packet(
struct ip *pip,
void *plast)
{
struct udphdr *pudp;
unsigned udp_length;
unsigned udp_data_length;
u_char *pdata;
/* find the udp header */
if (getudp(pip, &pudp, &plast))
return; /* not UDP or bad UDP packet */
/* make sure we have enough of the packet */
if ((char *)pudp+sizeof(struct udphdr)-1 > (char *)plast) {
if (warn_printtrunc)
printf("\t[packet truncated too short for UDP details]\n");
++ctrunc;
return;
}
printf("\tUDP SPRT: %u %s\n",
ntohs(pudp->uh_sport),
ParenServiceName(ntohs(pudp->uh_sport)));
printf("\t DPRT: %u %s\n",
ntohs(pudp->uh_dport),
ParenServiceName(ntohs(pudp->uh_dport)));
pdata = (u_char *)pudp + sizeof(struct udphdr);
udp_length = ntohs(pudp->uh_ulen);
udp_data_length = udp_length - sizeof(struct udphdr);
printf("\t UCKSUM: 0x%04x", ntohs(pudp->uh_sum));
pdata = (u_char *)pudp + sizeof(struct udphdr);
if (verify_checksums) {
if ((char *)pdata + udp_data_length > ((char *)plast+1))
printf(" (too short to verify)");
else
printf(" (%s)", udp_cksum_valid(pip,pudp,plast)?"CORRECT":"WRONG");
}
printf("\n");
printf("\t DLEN: %u", ntohs(pudp->uh_ulen));
if ((char *)pdata + ntohs(pudp->uh_ulen) > ((char *)plast+1))
printf(" (only %lu bytes in dump file)\n",
(u_long)((char *)plast - (char *)pdata + 1));
if (ntohs(pudp->uh_ulen) > 0) {
if (dump_packet_data)
PrintRawData(" data", pdata, plast, TRUE);
}
}
void
printpacket(
int len,
int tlen,
void *phys,
int phystype,
struct ip *pip,
void *plast,
tcb *tcb)
{
if (len == 0)
/* original length unknown */
printf("\tSaved Length: %d\n", tlen);
else if (len == tlen)
printf("\tPacket Length: %d\n", len);
else
printf("\tPacket Length: %d (saved length %d)\n", len,tlen);
printf("\tCollected: %s\n", ts2ascii(¤t_time));
if (phys) {
switch(phystype) {
case PHYS_ETHER:
printeth_packet(phys);
break;
default:
printf("\tPhysical layer: %d (not understood)\n", phystype);
break;
}
}
/* it's always supposed to be an IP packet */
printip_packet(pip,plast);
/* this will fail if it's not TCP */
printtcp_packet(pip,plast,tcb);
/* this will fail if it's not UDP */
printudp_packet(pip,plast);
}
static char *
ParenServiceName(
portnum port)
{
char *pname;
static char buf[80];
pname = ServiceName(port);
if (!pname || isdigit((int)(*pname)))
return("");
snprintf(buf,sizeof(buf),"(%s)",pname);
return(buf);
}
static char *
ParenHostName(
struct ipaddr addr)
{
char *pname;
static char buf[80];
pname = HostName(addr);
if (!pname || isdigit((int)(*pname)))
return("");
snprintf(buf,sizeof(buf),"(%s)",pname);
return(buf);
}
void
PrintRawData(
char *label,
void *pfirst,
void *plast,
Bool octal) /* hex or octal? */
{
int lcount = 0;
int count = (char *)plast - (char *)pfirst + 1;
u_char *pch = pfirst;
if (count <= 0)
return;
printf("========================================\n");
printf("%s (%d bytes):\n", label, count);
while (pch <= (u_char *) plast) {
if ((*pch == '\r') && (*(pch+1) == '\n')) {
printf("\n");
++pch;
lcount = 0;
} else if (isprint(*pch)) {
putchar(*pch);
lcount+=1;
} else {
if (octal) {
printf("\\%03o", *pch);
lcount+=4;
} else {
printf("0x%02x", *pch);
lcount+=4;
}
}
if (lcount > 70) {
printf("\\\n");
lcount = 0;
}
++pch;
}
if (lcount != 0)
printf("\\\n");
printf("========================================\n");
}
void
PrintRawDataHex(
char *label,
void *pfirst,
void *plast)
{
PrintRawData(label,pfirst,plast,FALSE);
}
static void
printipv6(
struct ipv6 *pipv6,
void *plast)
{
int ver = (ntohl(pipv6->ip6_ver_tc_flabel) & 0xF0000000) >> 28;
int tc = (ntohl(pipv6->ip6_ver_tc_flabel) & 0x0F000000) >> 24;
struct ipv6_ext *pheader;
u_char nextheader;
printf("\tIP Vers: %d\n", ver);
printf("\tIP Srce: %s\n", ipv6addr2str(pipv6->ip6_saddr));
printf("\tIP Dest: %s\n", ipv6addr2str(pipv6->ip6_daddr));
printf("\t Class: %d\n", tc);
printf("\t Flow: %d\n", (ntohl(pipv6->ip6_ver_tc_flabel) & 0x00FFFFFF));
printf("\t PLEN: %d\n", ntohs(pipv6->ip6_lngth));
printf("\t NXTH: %u (%s)\n",
pipv6->ip6_nheader,
ipv6_header_name(pipv6->ip6_nheader));
printf("\t HLIM: %u\n", pipv6->ip6_hlimit);
/* walk the extension headers */
nextheader = pipv6->ip6_nheader;
pheader = (struct ipv6_ext *)(pipv6+1);
while (pheader) {
u_char old_nextheader = nextheader;
pheader = ipv6_nextheader(pheader,&nextheader);
/* if there isn't a "next", then this isn't an extension header */
if (pheader) {
printf("\tIPv6 Extension Header Type %d (%s)\n",
old_nextheader,
ipv6_header_name(old_nextheader));
/* FIXME - want to give details, but I need some examples first! */
/* (hint to users!!!...) */
}
}
}
/*
* ipv6addr2str: return the string rep. of an ipv6 address
*/
static char *
ipv6addr2str(
struct in6_addr addr)
{
static char adr[INET6_ADDRSTRLEN];
my_inet_ntop(AF_INET6, (char *)&addr, (char *)adr, INET6_ADDRSTRLEN);
return(adr);
}
/* Shawn's version... */
/* Lots of machines HAVE this, but they give slightly different formats */
/* and it messes up my cross-platform testing. I'll just do it the */
/* "one true" way! :-) */
char *
Ether_Ntoa (struct ether_addr *e)
{
unsigned char *pe;
static char buf[30];
pe = (unsigned char *) e;
snprintf(buf,sizeof(buf),"%02x:%02x:%02x:%02x:%02x:%02x",
pe[0], pe[1], pe[2], pe[3], pe[4], pe[5]);
return(buf);
}
/* represent the sequence numbers absolute or relative to 0 */
/* N.B.: will fail will sequence space wraps around more than once */
static char *
PrintSeqRep(
tcb *ptcb,
u_long seq)
{
static char buf[20];
if (ptcb && print_seq_zero && (ptcb->syn_count>0)) {
/* Relative form */
sprintf(buf,hex?"0x%08lx(R)":"%lu(R)",
seq - ptcb->syn);
} else {
/* Absolute form */
sprintf(buf,hex?"0x%08lx":"%lu",seq);
}
return(buf);
}
syntax highlighted by Code2HTML, v. 0.9.1