#if !defined(MAY_NOT_MODIFY)
/****==========------------------------------------------------==========****/
/* */
/* tcpshow, v1.74 */
/* */
/* Quickie to decode a "tcpdump" savefile. */
/* */
/* The application data is displayed as ASCII -- application protocols are */
/* not decoded. */
/* */
/* The data captured by "tcpdump" might be less than in the original */
/* packet. We kludge a solution to this with setjmp()/longjmp(). */
/* */
/* Although written to read tcpdump savefiles, with tcpdump itself as a */
/* front-end, it'll decode any hex dump that adheres to the format */
/* expected. Some programs which capture network data offer an option to */
/* save the trace to a file in hex format -- this can often be massaged */
/* easily with Perl/awk/sh scripts to turn it into the format expected. */
/* As a special case, "tcpdump -s 1518 -lenx | tcpshow -cooked" works */
/* rather well, and "tcpdump -s 1518 -lenx | tcpshow -cooked -data" is nice */
/* for watching the data traffic in real time. */
/* */
/* ------------------------------------------------------------------------ */
/* */
/* Known Bugs: */
/* 1. If "-s n" wasn't specified with tcpdump, then when using the */
/* "-nodata" flag, the "DATA: n bytes" line doesn't get output for the */
/* 1st packet. */
/* */
/* ------------------------------------------------------------------------ */
/* */
/* Copyright (c) 1996, 1997, 1998 I.T. NetworX Ltd. All rights reserved. */
/* */
/* This source code is owned and copyrighted by I.T. NetworX Ltd. This */
/* file and all files derived from it, directly or indirectly (such files */
/* collectively and separately being referred to henceforth as "this file") */
/* may be used, modified and redistributed subject to the following six */
/* Conditions. */
/* */
/* Condition 1 of 6: */
/* That all text (code/comments, etc.) in this file surrounded by the macro */
/* block "#if !defined(MAY_NOT_MODIFY) ... #endif", including the macro */
/* statements themselves, may not be modified in any way, or deleted. In */
/* particular, this comment block and the printf() statements identifying */
/* I.T. NetworX as being the copyright owner, in the function usage(), may */
/* not be modified or deleted. The single, only, exception to this is that */
/* the non-inclusion of C comments by a C compiler/linker, in the object */
/* and executable images it produces, is permitted. */
/* */
/* Condition 2 of 6: */
/* That no financial gain be made from using this file or modifying this */
/* file. It is permitted to charge for redistributing this file. */
/* */
/* Condition 3 of 6: */
/* That no conditions other than these six Conditions be applied to the */
/* use, modification or redistribution of this file. */
/* */
/* Condition 4 of 6: */
/* That all modifications to this file show prominently the name of the */
/* person that made the change and the date on which the change was made. */
/* */
/* Condition 5 of 6: */
/* That I.T. NetworX, its employees, agents and everybody else in the world */
/* dead, living and yet to be born, are hereby free from liability of all */
/* and every kind arising from the use of this file by anybody for any */
/* purpose. This file comes "as is" and all warranties, express or */
/* implied, are disclaimed. As the manual page for chat(1) says, "if it */
/* breaks, then you get to keep both pieces". */
/* */
/* Condition 6 of 6: */
/* That I.T. NetworX reserves the right to alter these Conditions at any */
/* time without giving prior notice, such alterations to apply only to the */
/* version current at the time of issue of the alterations and all later */
/* versions, and such alterations to apply only to versions produced */
/* exclusively by I.T. NetworX or its agents. */
/* */
/* Addendum to Copyright: */
/* I'm not a legal eagle and I worded the above notice off the top of my */
/* head, so it may be full of holes, but the spirit of my intentions are */
/* clear from reading it. Please respect these intentions. */
/* */
/* Me too: */
/* If anybody makes significant improvements to this file, such as adding */
/* decode support for DHCP or DNS traffic, or IP and TCP options, I would */
/* appreciate it if they sent me a copy of their work (mike@NetworX.ie). */
/* Thanks. */
/* */
/* ------------------------------------------------------------------------ */
/* */
/* File layout is as follows: */
/* system includes */
/* local includes */
/* #define macros */
/* typedefs */
/* declarations of extern variables */
/* declarations of extern functions */
/* declarations of global variables */
/* declarations of global functions */
/* declarations of static variables */
/* declarations of static functions */
/* definitions of functions */
/* (all functions and variables are declared/defined in alphabetical order) */
/* */
/* ------------------------------------------------------------------------ */
/* */
/* Compiles as follows: */
/* cc -s -O -o tcpshow tcpshow.c */
/* */
/* ------------------------------------------------------------------------ */
/* */
/* Who and when: */
/* MikeRyan, 11apr96. */
/* */
/* I.T. NetworX, */
/* Stonebridge House, */
/* Shankill, */
/* Co. Dublin. */
/* Ireland. */
/* Phone: +353-1-28-27-233 */
/* Fax: +353-1-28-27-230 */
/* Email: mike@NetworX.ie */
/* */
/* ------------------------------------------------------------------------ */
/* */
/* Modification History */
/* MikeRyan, 14may96: Allow "tcpdump" expressions to be passed in. */
/* MikeRyan, 15may96: Added UDP decode logic. */
/* MikeRyan, 16may96: Added ICMP decode logic. */
/* MikeRyan, 16may96: Added -b switch to break long lines */
/* -w option to specify the width of the page */
/* -h flag to give help on usage. */
/* MikeRyan, 28may96: Added -nolink */
/* -nodata */
/* -noip */
/* -track */
/* -terse */
/* -sb */
/* -s */
/* MikeRyan, 25jun96: Incorporated my "general.h" typedef's, so that the */
/* present source file doesn't depend on any */
/* non-standard header files. This allows it to be */
/* distributed as a single file. Also included the */
/* copyright notice, as I'm making the program freely */
/* available. */
/* MikeRyan, 26jun96: Added -cooked and -pp. */
/* MikeRyan, 26may97: Made "-terse" the default; added "-verbose". */
/* mr971010 v1.2 Reformatted layout to match my current preference */
/* (purely cosmetic change). */
/* mr971014 v1.3 Set width of page to 60 characters and turned on line */
/* wrap by default. Changed "bflag" to "noBflag" in the */
/* process. */
/* mr971021 v1.4 (a) Always show the "Packet n" line (it wasn't being */
/* displayed when "-data" was used). */
/* (b) Display the separator line (prsep()) before the */
/* 1st packet (this is more consistent). */
/* (c) Added "-noPortNames" flag, to turn off mapping */
/* port numbers into port names. Now, port names */
/* are shown except when "-noPortNames" is used. */
/* (d) Display short hostnames instead of IP addresses, */
/* unless "-noHostNames" is specified; display fully */
/* qualified hostnames if "-fqdn" is specified. */
/* Function svcname() was renamed to portName() and */
/* enhanced a little during all this. */
/* mr971120 v1.5 (a) Added ARP/RARP decoding. */
/* mr980117 v1.6 (a) Added delta time for 2nd and subsequent packets. */
/* mr980118 v1.7 (a) Corrected minor 'bug' in etherAddr(). */
/* (b) Added -noEtherNames and the code to print */
/* Ethernet names (which happens by default). This */
/* code is presently particular to FreeBSD, so to */
/* leave it out (so that Ethernet names are never */
/* displayed) define the macro NOETHERNAMES. */
/* mr980129 v1.71 (a) Changed version from x.y to x.yz. */
/* (b) Added icmpExtras() to display the additional */
/* information in some ICMP headers. */
/* mr980130 v1.72 (a) Added ETHER_PROTO_UNKNOWN to correct a bug in */
/* showHdr(). */
/* mr980202 v1.73 (a) Changed "-data" to "-minDecode", indicating a */
/* minimal decode of the headers. The change was */
/* made because "tcpshow -data -nodata" had the */
/* potential for confusion! */
/* (b) Changed options like "-nodata" to "-noData". */
/* (c) Recoded etherProto() to generalise it a bit more. */
/* Also removed ETHER_PROTO_UNKNOWN as it's not */
/* needed given the recoding of etherProto(). */
/* mr980303 v1.74 (a) Don't display "-noEtherNames" as a run-time */
/* option if NOETHERNAMES is defined. */
/* (b) Changed "-minDecode" to "-minHdrDecode". */
/* */
/****==========------------------------------------------------==========****/
#endif
/* tm020221: modification of Tomo.M on 2002/02/21 */
#include <sys/types.h> // mr971021 Next four includes
#include <sys/socket.h>
#if !defined(NOETHERNAMES)
#include <net/ethernet.h> // mr980118 Particular to FreeBSD?
#endif
#include <netinet/in.h>
#include <arpa/inet.h>
#include <netdb.h>
#include <setjmp.h>
#include <ctype.h>
#include <string.h>
#include <unistd.h>
#include <stdlib.h>
#include <stdio.h>
#include <locale.h>
/* Some general defines. */
#if defined(FALSE)
#undef FALSE
#endif
#if defined(TRUE)
#undef TRUE
#endif
#define FALSE (boolean)0
#define TRUE (boolean)1
#define elif else if
#if !defined(reg)
#define reg register /* For debugging purposes */
#endif
#define VERSION 1.74 /* Please change when appropriate */
#define COOKER "tcpdump"
#define MAXCOOKARGS 100 /* Max tcpdump expression words */
#define MAXPKT 10240 /* Should be 1518 for Ethernet */
#define NCOLS 60 /* mr971014 Changed from 1024 */
#define MAX_HOSTNAMELEN 255 // mr971021
#define MAX_PORTNAMELEN 32 // mr971021
#define ETHER_ADDRLEN 17 // mr971120 Ether addr in ASCII
#define IP_ADDRLEN 15 // mr971120 IP addr in ASCII
// Ethernet protocol types (add to this list as needed).
#define ETHER_PROTO_IP 0x0800
#define ETHER_PROTO_ARP 0x0806
#define ETHER_PROTO_RARP 0x8035
// ARP/RARP header elements.
#define ARP_REQ 1
#define ARP_RSP 2
#define RARP_REQ 3
#define RARP_RSP 4
#define ARP_HW_ETHER 1 // Hardware type is Ethernet
#define ARP_PROTO_IP 0x0800 // Protocol type is IP
/* IP header elements. */
#define IPHDRLEN 20
#define FRAGOFF 0x1FFF
#define MF 0x2000
#define DF 0x4000
/* TCP header elements. */
#define TCPHDRLEN 20
#define URG 0x0020
#define ACK 0x0010
#define PSH 0x0008
#define RST 0x0004
#define SYN 0x0002
#define FIN 0x0001
/* UDP header elements. */
#define UDPHDRLEN 8
/* IP protocol types. */
#define IP 0
#define ICMP 1
#define IGMP 2
#define GGP 3
#define IPENCAP 4
#define ST 5
#define TCP 6
#define EGP 8
#define PUP 12
#define UDP 17
#define HMP 20
#define XNSIDP 22
#define RDP 27
#define ISOTP4 29
#define XTP 36
#define IDPRCMTP 39
#define RSVP 46
#define VMTP 81
#define OSPF 89
#define IPIP 94
#define ENCAP 98
/* ICMP types. */
#define ECHO_REPLY 0
#define DST_UNREACH 3
#define SRC_QUENCH 4
#define REDIRECT 5
#define ECHO_REQ 8
#define ROUTER_AD 9
#define ROUTER_SOL 10
#define TIME_EXCEED 11
#define PARAM_PROB 12
#define TIME_REQ 13
#define TIME_REPLY 14
#define INFO_REQ 15
#define INFO_REPLY 16
#define MASK_REQ 17
#define MASK_REPLY 18
/* ICMP codes for type == Destination Unreachable. */
#define NET_UNREACH 0
#define HOST_UNREACH 1
#define PROTO_UNREACH 2
#define PORT_UNREACH 3
#define DF_SET 4
#define SRCROUTE_FAILED 5
#define DSTNET_UNKNOWN 6
#define DSTHOST_UNKNOWN 7
#define SRCHOST_ISOLATED 8
#define DSTNET_PROHIB 9
#define DSTHOST_PROHIB 10
#define NET_UNREACH_TOS 11
#define HOST_UNREACH_TOS 12
#define COMM_PROHIB 13
#define HOST_PREC_VIOL 14
#define PREC_CUTOFF 15
/* ICMP codes for type == Redirect. */
#define REDIR_FOR_NET 0
#define REDIR_FOR_HOST 1
#define REDIR_FOR_TOSNET 2
#define REDIR_FOR_TOSHOST 3
/* ICMP codes for type == Time Exceeded. */
#define TTL_ZERO 0
#define REASS_TIMEOUT 1
/* ICMP codes for type == Parameter Problem. */
#define IP_HDR_BAD 0
#define MISSING_OPT 1
/* Skip remaining lines of current packet. Note that this causes a */
/* longjmp(), so a succeeding "return" from a function isn't needed. */
#define nextPkt() for (dataLen = 0; ; ) (void)getPkt()
/* Display a separator line between packet decodes. */
#define prSep() \
printf( \
"--------------------------------------" \
"-------------------------------------\n" \
)
/* My own preferred basic data types -- amend per target machine. */
typedef char boolean;
typedef float float4;
typedef double float8;
typedef char int1;
typedef short int2;
typedef int int4;
typedef unsigned char uint1;
typedef unsigned short uint2;
typedef unsigned int uint4;
typedef unsigned char uchar;
#if !defined(NOETHERNAMES)
// mr980118 ether_ntohost() and related functions aren't prototyped in the
// standard include directory.
#ifndef __FreeBSD__
extern struct ether_addr *ether_aton(char *);
extern int ether_ntohost(char *, struct ether_addr *);
#endif
#endif
int main(int, char **);
static boolean noBflag = FALSE;
static char *cookArgs[MAXCOOKARGS+1];
static boolean cookedFlag = FALSE;
static int2 dataLen = 0; // tm020221 must be 'signed'.
static char *dfltCookArgs[] = {
COOKER, "-enx", "-s10240", "-r-", (char *)NULL
};
static char dHostName[MAX_HOSTNAMELEN+1];
static char dIp[IP_ADDRLEN+1]; // Destination IP address
static int etherType; // mr971120 Protocol encoded in frame
static boolean fqdnFlag = FALSE; // mr971021
static jmp_buf jmpBuf;
static boolean minHdrDecodeFlag = FALSE;
static boolean noDataFlag = FALSE;
#if !defined(NOETHERNAMES)
static boolean noEtherNames = FALSE; // mr980118
#else
static boolean noEtherNames = TRUE; // mr980303
#endif
static boolean noIpflag = FALSE;
static boolean noLinkFlag = FALSE;
static boolean noHostNames = FALSE; // mr971021
static boolean noPortNames = FALSE; // mr971021
static int nPktsShown = 0;
static char *off = "off,"; /* "off" in middle of list */
static char *off_e = "off"; /* "off" at end of list */
static char *on = "on, "; /* "on" in middle of list */
static char *on_e = "on"; /* "on" at end of list */
static int pageWidth = NCOLS;
static char *pkt;
static boolean ppFlag = FALSE;
static uint1 proto;
static boolean sFlag = FALSE;
static boolean sbFlag = FALSE;
static char sHostName[MAX_HOSTNAMELEN+1];
static char sIp[IP_ADDRLEN+1]; // Source IP address
static boolean terseFlag = TRUE;
static boolean trackFlag = FALSE;
static char *unknown = "<unknown>";
static boolean verboseFlag = FALSE; /* MikeRyan, 26may97 */
static double canonTime(char *, double *, int *);
static char *deltaTime(double *, char *);
static void error(char *);
static char *etherAddr(char *, char **);
static char *etherName(char *, boolean);
static char *etherProto(char *, int *);
static void forkTcpdump(int, char **);
static uint1 getByte(char **);
static uint4 getLongWord(char **);
static char *getPkt(void);
static uint2 getWord(char **);
static char *hostName(char *, boolean);
static char *icmpCode(uint1, uint1);
static char *icmpExtras(uint1, uint1, char **, uint2 *);
static char *icmpType(uint1);
static char *ipAddr(char **);
static char *ipProto(uint1);
static char nextChar(char **);
static char *portName(uint2, char *, boolean);
static char *rmWSpace(char *);
static char *showArp(char *);
static char *showData(char *);
static char *showHdr(char *);
static char *showIcmp(char *);
static char *showIp(char *);
static void showPkt(char *);
static char *showRarp(char *);
static char *showTcp(char *);
static char *showUdp(char *);
static char *skip(char *, uint2);
static void usage(void);
/****==========------------------------------------------------==========****/
/* */
/* Return the canonical time (time in time units). */
/* */
/****==========------------------------------------------------==========****/
static double canonTime (char *dayTime, double *multiplier, int *multLen) {
double time;
time = (double)atoi(dayTime) * 60 * 60;
dayTime += 3;
time += (double)atoi(dayTime) * 60;
dayTime += 3;
time += (double)atoi(dayTime);
dayTime += 3;
// Time resolution differs between machines. We guess the resolution by
// looking at the length of the fractional part of the time stamp.
// Rather than using pow(), which requires the maths library, we just
// run a simple loop to produce the same result. This is only done once,
// so efficiency doesn't matter.
if (nPktsShown == 1) {
int i;
*multLen = strlen(dayTime);
for (i = 0, *multiplier = 1; i < *multLen; ++i) *multiplier *= 10;
}
return time * *multiplier + (double)atol(dayTime);
}
/****==========------------------------------------------------==========****/
/* */
/* Return the time difference between this and the previous packet. */
/* */
/****==========------------------------------------------------==========****/
static char *deltaTime (double *prevTime, char *time) {
double currTime;
double delta;
static char deltaString[32];
int hours;
int mins;
static double multiplier;
static int multLen; // Length of multiplier in chars
char *s;
int secs;
// The timestamp may not be a valid time (e.g. if tcpdump isn't producing
// the input but, rather, some end-user script). We need to increase the
// strength of this validation someday :-)
if (!isdigit(*time) || time[2] != ':' || time[5] != ':') return "";
currTime = canonTime(time, &multiplier, &multLen);
if (nPktsShown == 1) {
*prevTime = currTime; // Initialise on 1st packet
return "";
}
// tm020221 delta should be positive value, but ...
delta = currTime >= *prevTime ? currTime - *prevTime
: multiplier * 86400 + currTime - *prevTime;
*prevTime = currTime;
// Convert the delta time to daytime representation.
// The subtractions from 'delta' simulate the % operator. (Note: don't
// change the value of 'multiplier', as it's set only on the 1st call to
// canonTime()).
hours = (int)(delta / (multiplier * 60 * 60));
delta -= (double)hours * multiplier * 60 * 60;
mins = (int)(delta / (multiplier * 60));
delta -= (double)mins * multiplier * 60;
secs = (int)(delta / multiplier);
delta -= (double)secs * multiplier;
(void)strcpy(deltaString, " (");
s = &deltaString[2];
(void)sprintf(s, "%02d:", hours);
s += 3;
(void)sprintf(s, "%02d:", mins);
s += 3;
(void)sprintf(s, "%d.", secs);
while (*s) ++s;
(void)sprintf(s, "%0*d", multLen, (int)delta);
while (*s) ++s;
*s++ = ')';
*s = '\0';
// Remove the hours and mins components if they're zero.
s = deltaString + 2; // Skip over " ("
if ((hours=atoi(s)) == 0) {
s += 3;
if ((mins=atoi(s)) == 0) {
s += 3;
}
}
bcopy(s, deltaString+2, strlen(s)+1);
return deltaString;
}
/****==========------------------------------------------------==========****/
/* */
/* Print an error message and exit. */
/* */
/****==========------------------------------------------------==========****/
static void error (char *msg) {
fprintf(stderr, "***Error: %s\n", msg);
exit(1);
}
/****==========------------------------------------------------==========****/
/* */
/* Print a formatted Ethernet address. */
/* If pPkt is non-zero, we read the address from the packet stream in raw */
/* hex format. */
/* */
/****==========------------------------------------------------==========****/
static char *etherAddr (char *eAddr, char **pPkt) {
uint1 byte;
static char formatted[ETHER_ADDRLEN+1];
int i;
int j;
// If non-zero, read the hex bytes from the packet stream.
if (pPkt) {
byte = getByte(pPkt); sprintf(formatted+0, "%02X:", byte);
byte = getByte(pPkt); sprintf(formatted+3, "%02X:", byte);
byte = getByte(pPkt); sprintf(formatted+6, "%02X:", byte);
byte = getByte(pPkt); sprintf(formatted+9, "%02X:", byte);
byte = getByte(pPkt); sprintf(formatted+12, "%02X:", byte);
byte = getByte(pPkt); sprintf(formatted+15, "%02X", byte);
}
else {
// The address is already in ASCII form; just pad out.
for (i = j = 0; i < 6; i++)
if (eAddr[1] == ':' || eAddr[1] == '\0') { // mr980118
formatted[j++] = '0';
formatted[j++] = toupper(eAddr[0]);
formatted[j++] = ':';
eAddr += 2;
}
else {
formatted[j++] = toupper(eAddr[0]);
formatted[j++] = toupper(eAddr[1]);
formatted[j++] = ':';
eAddr += 3;
}
formatted[j-1] = '\0';
}
return formatted;
}
/****==========------------------------------------------------==========****/
/* */
/* If noEtherNames is true, then if returnEtherAddr is true return the */
/* Ethernet address, otherwise return the value of 'unknown'; */
/* otherwise, if there is no matching Ethernet name, then if */
/* returnEtherAddr is true, return the Ethernet address, otherwise */
/* return 'unknown'; */
/* otherwise, return the Ethernet name. */
/* */
/* CURRENTLY, THIS FUNCTION USES LIBRARY CALLS PARTICULAR TO FREEBSD 2.1+. */
/* */
/* */
/* mr980118 */
/* */
/****==========------------------------------------------------==========****/
static char *etherName (char *etherAddr, boolean returnEtherAddr) {
#if defined(NOETHERNAMES)
return returnEtherAddr? etherAddr: "no name";
#else
struct ether_addr *e;
static char name[MAX_HOSTNAMELEN+1];
if (noEtherNames) return returnEtherAddr? etherAddr: unknown;
if (!(e=ether_aton(etherAddr))) error("Badly formatted Ethernet address");
if (ether_ntohost(name, e) != 0)
return returnEtherAddr? etherAddr: unknown;
return name;
#endif
}
/****==========------------------------------------------------==========****/
/* */
/* Return the type of protocol encapsulated in the Ethernet frame and also */
/* a string representing that value. */
/* */
/****==========------------------------------------------------==========****/
static char *etherProto (char *typeStr, int *pType) {
int type;
(void)sscanf(typeStr, "%x", &type);
if (pType) *pType = type;
if (type == ETHER_PROTO_IP)
return "IP";
elif (type == ETHER_PROTO_ARP)
return "ARP";
elif (type == ETHER_PROTO_RARP)
return "RARP";
else
return typeStr;
}
/****==========------------------------------------------------==========****/
/* */
/* Run tcpdump to pre-process the trace file. */
/* */
/****==========------------------------------------------------==========****/
static void forkTcpdump (int argc, char **argv) {
int fd[2];
int i;
pid_t pid;
/* Required "tcpdump" flags. */
i = 0;
while (dfltCookArgs[i]) {
cookArgs[i] = dfltCookArgs[i];
i++;
}
while (argc-- > 0) {
if (i >= MAXCOOKARGS) error("Too many expressions");
cookArgs[i++] = *argv++;
}
cookArgs[i] = (char *)NULL;
/* Fork tcpdump to cook our input. */
if (pipe(fd)) error("pipe() failed");
if ((pid=fork()) < 0) error("fork() failed");
if (pid == 0) {
(void)close(1);
if (dup(fd[1]) != 1) error("dup() failed");
(void)close(fd[0]);
(void)close(fd[1]);
execvp(COOKER, cookArgs);
error("execvp() failed");
}
(void)close(0);
if (dup(fd[0]) != 0) error("dup() failed");
(void)close(fd[0]);
(void)close(fd[1]);
}
/****==========------------------------------------------------==========****/
/* */
/* Return the byte value and increment the pointer by sizeof(byte). */
/* */
/****==========------------------------------------------------==========****/
static uint1 getByte (char **pPkt) {
char byte[1*2+1]; /* ASCII representation of a byte */
unsigned int val;
byte[0] = nextChar(pPkt);
byte[1] = nextChar(pPkt);
byte[2] = '\0';
(void)sscanf(byte, "%x", &val);
return (uint1)val;
}
/****==========------------------------------------------------==========****/
/* */
/* Return the longword value and increment the pointer by sizeof(longWord). */
/* */
/****==========------------------------------------------------==========****/
static uint4 getLongWord (char **pPkt) {
char longWord[4*2+1]; /* ASCII representation of a longword */
unsigned long val;
longWord[0] = nextChar(pPkt);
longWord[1] = nextChar(pPkt);
longWord[2] = nextChar(pPkt);
longWord[3] = nextChar(pPkt);
longWord[4] = nextChar(pPkt);
longWord[5] = nextChar(pPkt);
longWord[6] = nextChar(pPkt);
longWord[7] = nextChar(pPkt);
longWord[8] = '\0';
(void)sscanf(longWord, "%lx", &val);
return (uint4)val;
}
/****==========------------------------------------------------==========****/
/* */
/* Read in the next line of packet data. */
/* */
/* Set global pointer "pkt" to start of buffer containing the packet line. */
/* */
/****==========------------------------------------------------==========****/
static char *getPkt () {
static boolean beenHereAlready = FALSE;
static char pktBuf[MAXPKT+1];
nextline:
if (fgets(pktBuf, MAXPKT+1, stdin) == (char *)NULL) {
if (nPktsShown > 0) prSep();
exit(0);
}
// tm020221
// In these days, tcpdump produces much of irregular outputs.
// I had a work around by making logical change to original.
// + check lines in its pattern.
// + HEADER pattern triggers next showPkt();
#define PTN_HEAD(buf) (buf[2] == ':' && buf[5] == ':' && buf[8] == '.')
#define PTN_DATA(buf) (buf[0] == '\t')
if (PTN_HEAD(pktBuf)) {
if (beenHereAlready == FALSE) {
beenHereAlready = TRUE;
return pkt = pktBuf;
} else {
putchar('\n');
if (dataLen > 0)
printf("\n\t<*** Rest of data missing from packet dump ***>\n");
pkt = pktBuf;
longjmp(jmpBuf, 1);
}
}
elif (PTN_DATA(pktBuf)) {
if (nPktsShown > 0)
return pkt = rmWSpace(pktBuf);
}
goto nextline;
}
/****==========------------------------------------------------==========****/
/* */
/* Return the word value and increment the pointer by sizeof(word). */
/* */
/****==========------------------------------------------------==========****/
static uint2 getWord (char **pPkt) {
char word[2*2+1]; /* ASCII representation of a word */
unsigned int val;
word[0] = nextChar(pPkt);
word[1] = nextChar(pPkt);
word[2] = nextChar(pPkt);
word[3] = nextChar(pPkt);
word[4] = '\0';
(void)sscanf(word, "%x", &val);
return (uint2)val;
}
/****==========------------------------------------------------==========****/
/* */
/* If noHostNames is true, then if returnIpAddr is true return the IP */
/* address, otherwise return the value of 'unknown'; */
/* otherwise, if there is no matching hostname, then if returnIpAddr is */
/* true, return the IP address, otherwise return 'unknown'; */
/* otherwise, if fqdnFlag is true, return the fully-qualified hostname; */
/* otherwise, return the short hostname. */
/* */
/* */
/* mr971021 */
/* */
/****==========------------------------------------------------==========****/
static char *hostName (char *ipAddr, boolean returnIpAddr) {
struct hostent *h;
static char name[MAX_HOSTNAMELEN+1];
struct in_addr inAddr;
char *s;
if (noHostNames) return returnIpAddr? ipAddr: unknown;
inAddr.s_addr = inet_addr(ipAddr);
if (!(h = gethostbyaddr((char *)&inAddr, sizeof(inAddr), AF_INET)))
return returnIpAddr? ipAddr: unknown;
if (strlen(h->h_name) > MAX_HOSTNAMELEN) error("Hostname too long");
(void)strcpy(name, h->h_name);
if (!fqdnFlag) {
s = name;
while (*s && *s != '.') ++s;
*s = '\0';
}
return name;
}
/****==========------------------------------------------------==========****/
/* */
/* Print the code relating to the ICMP type. */
/* */
/****==========------------------------------------------------==========****/
static char *icmpCode (uint1 type, uint1 code) {
char *bad;
char *descr;
bad = "<*** CORRUPT ***>";
descr = (char *)NULL;
switch (type) {
case ECHO_REPLY:
switch (code) {
case 0: break;
default: descr = bad; break;
}
break;
case DST_UNREACH:
switch (code) {
case NET_UNREACH: descr = "network-unreachable"; break;
case HOST_UNREACH: descr = "host-unreachable"; break;
case PROTO_UNREACH: descr = "protocol-unreachable"; break;
case PORT_UNREACH: descr = "port-unreachable"; break;
case DF_SET: descr = "frag-needed-but-DF-set"; break;
case SRCROUTE_FAILED: descr = "source-route-failed"; break;
case DSTNET_UNKNOWN: descr = "destination-network-unknown"; break;
case DSTHOST_UNKNOWN: descr = "destination-host-unknown"; break;
case SRCHOST_ISOLATED: descr = "source-host-isolated"; break;
case DSTNET_PROHIB: descr = "dest-net-admin-prohibited"; break;
case DSTHOST_PROHIB: descr = "dest-host-admin-prohibited"; break;
case NET_UNREACH_TOS: descr = "network-unreachable-for-TOS"; break;
case HOST_UNREACH_TOS: descr = "host-unreachable-for-TOS"; break;
case COMM_PROHIB: descr = "trafffic-prohibited-by-filter"; break;
case HOST_PREC_VIOL: descr = "host-precedence-violation"; break;
case PREC_CUTOFF: descr = "precedence-cutoff-in-effect"; break;
default: descr = bad; break;
}
break;
case SRC_QUENCH:
switch (code) {
case 0: break;
default: descr = bad; break;
}
break;
case REDIRECT:
switch (code) {
case REDIR_FOR_NET: descr = "route-wrong-for-network"; break;
case REDIR_FOR_HOST: descr = "route-wrong-for-host"; break;
case REDIR_FOR_TOSNET: descr = "route-wrong-for-TOS-and-net"; break;
case REDIR_FOR_TOSHOST: descr = "route-wrong-for-TOS-and-host"; break;
default: descr = bad; break;
}
break;
case ECHO_REQ:
switch (code) {
case 0: break;
default: descr = bad; break;
}
break;
case ROUTER_AD:
switch (code) {
case 0: break;
default: descr = bad; break;
}
break;
case ROUTER_SOL:
switch (code) {
case 0: break;
default: descr = bad; break;
}
break;
case TIME_EXCEED:
switch (code) {
case TTL_ZERO: descr = "TTL-reached-zero"; break;
case REASS_TIMEOUT: descr = "reassembly-timer-expired"; break;
default: descr = bad; break;
}
break;
case PARAM_PROB:
switch (code) {
case IP_HDR_BAD: descr = "IP-header-bad"; break;
case MISSING_OPT: descr = "required-option-is-missing"; break;
default: descr = bad; break;
}
break;
case TIME_REQ:
switch (code) {
case 0: break;
default: descr = bad; break;
}
break;
case TIME_REPLY:
switch (code) {
case 0: break;
default: descr = bad; break;
}
break;
case INFO_REQ:
switch (code) {
case 0: break;
default: descr = bad; break;
}
break;
case INFO_REPLY:
switch (code) {
case 0: break;
default: descr = bad; break;
}
break;
case MASK_REQ:
switch (code) {
case 0: break;
default: descr = bad; break;
}
break;
case MASK_REPLY:
switch (code) {
case 0: break;
default: descr = bad; break;
}
break;
default:
break;
}
return descr;
}
/****==========------------------------------------------------==========****/
/* */
/* Some ICMP messages contain additional information. */
/* Return this information if it's available, NULL otherwise. */
/* */
/* On entry, *pPkt points at the 1st byte following the ICMP checksum. */
/* */
/****==========------------------------------------------------==========****/
static char *icmpExtras (
uint1 type, uint1 code, char **pPkt, uint2 *nSkipped
) {
char *dfSetText1 = "My next-hop MTU is ";
uint2 dstPort;
boolean haveInfo;
#if defined(INFOBUF)
#error "INFOBUF already defined"
#endif
#define INFOBUF 255
static char info[INFOBUF+1];
int ipHdrLen;
uint2 mtu;
char *portUnreachText1 = "Port ";
char *portUnreachText2 = " is unreachable";
char *redirectText1 = "Use router ";
char *redirectText2 = " instead";
char *s;
uint1 transportProto;
haveInfo = FALSE;
// We're only interested in the ones that have extra information.
switch (type) {
case DST_UNREACH:
switch (code) {
case PORT_UNREACH:
*pPkt = skip(*pPkt, 4); *nSkipped += 4; // Skip over MBZ field
ipHdrLen = (int)getByte(pPkt); *nSkipped += 1;
ipHdrLen = (ipHdrLen & 0x0F) * 4;
*pPkt = skip(*pPkt, 8); *nSkipped += 8; // Skip to protocol field
transportProto = getByte(pPkt); *nSkipped += 1;
// Subtract ten because we've already processed these bytes.
*pPkt = skip(*pPkt, ipHdrLen-1-1-8); *nSkipped += ipHdrLen-1-1-8;
*pPkt = skip(*pPkt, 2); *nSkipped += 2; // Skip past source port
dstPort = getWord(pPkt); *nSkipped += sizeof dstPort;
switch (transportProto) {
case TCP: s = "tcp"; break;
case UDP: s = "udp"; break;
default: error("IP protocol field != TCP or UDP");
}
s = portName(dstPort, s, TRUE);
if (strlen(portUnreachText1)+strlen(portUnreachText2)+strlen(s) >
INFOBUF) error("INFOBUF too small");
(void)strcpy(info, portUnreachText1);
(void)strcat(info, s);
(void)strcat(info, portUnreachText2);
haveInfo = TRUE;
break;
case DF_SET:
// If the router sending the ICMP is properly playing its part in
// PMTU discovery, the high word will contain the next-hop MTU.
*pPkt = skip(*pPkt, 2); *nSkipped += 2;
mtu = getWord(pPkt); *nSkipped += sizeof(mtu);
if (mtu) {
(void)strcpy(info, dfSetText1);
(void)sprintf(info+strlen(info), "%d", mtu);
haveInfo = TRUE;
}
break;
}
break;
case REDIRECT:
s = hostName(ipAddr(pPkt), TRUE); *nSkipped += 4;
if (strlen(redirectText1)+strlen(redirectText2)+strlen(s) > INFOBUF)
error("INFOBUF too small");
(void)strcpy(info, redirectText1);
(void)strcat(info, s);
(void)strcat(info, redirectText2);
haveInfo = TRUE;
break;
case TIME_REQ:
break;
case TIME_REPLY:
break;
case INFO_REQ:
break;
case INFO_REPLY:
break;
case MASK_REQ:
break;
case MASK_REPLY:
break;
}
return haveInfo? info: (char *)NULL;
#undef INFOBUF
}
/****==========------------------------------------------------==========****/
/* */
/* Print the type of ICMP packet. */
/* */
/****==========------------------------------------------------==========****/
static char *icmpType (uint1 type) {
char *descr;
static char unknowntype[80];
snprintf(unknowntype, 80, "%s (%d)", unknown, type);
switch (type) {
case ECHO_REPLY: descr = "echo-reply"; break;
case DST_UNREACH: descr = "destination-unreachable"; break;
case SRC_QUENCH: descr = "source-quench"; break;
case REDIRECT: descr = "redirect"; break;
case ECHO_REQ: descr = "echo-request"; break;
case ROUTER_AD: descr = "router-advertisement"; break;
case ROUTER_SOL: descr = "router-solicitation"; break;
case TIME_EXCEED: descr = "time-exceeded"; break;
case PARAM_PROB: descr = "parameter-problem"; break;
case TIME_REQ: descr = "timestamp-request"; break;
case TIME_REPLY: descr = "timestamp-reply"; break;
case INFO_REQ: descr = "information-request"; break;
case INFO_REPLY: descr = "information-reply"; break;
case MASK_REQ: descr = "address-mask-request"; break;
case MASK_REPLY: descr = "address-mask-reply"; break;
default: descr = unknowntype; break;
}
return descr;
}
/****==========------------------------------------------------==========****/
/* */
/* Print the IP address in dotted-quad. */
/* */
/****==========------------------------------------------------==========****/
static char *ipAddr (char **pPkt) {
static char addr[IP_ADDRLEN+1];
uint2 byte1;
uint2 byte2;
uint2 byte3;
uint2 byte4;
/* We don't use inet_ntoa() because it wants a socket structure. */
byte1 = (uint2)getByte(pPkt);
byte2 = (uint2)getByte(pPkt);
byte3 = (uint2)getByte(pPkt);
byte4 = (uint2)getByte(pPkt);
(void)sprintf(addr, "%d.%d.%d.%d", byte1, byte2, byte3, byte4);
return addr;
}
/****==========------------------------------------------------==========****/
/* */
/* Print the type of protocol encapsulated in the IP datagram. */
/* */
/****==========------------------------------------------------==========****/
static char *ipProto (uint1 code) {
char *name;
/* A simple table won't do, as the codes aren't contiguous. */
switch (code) {
case IP:
name = "IP"; break;
case ICMP:
name = "ICMP"; break;
case IGMP:
name = "IGMP"; break;
case GGP:
name = "GGP"; break;
case IPENCAP:
name = "IPENCAP"; break;
case ST:
name = "ST"; break;
case TCP:
name = "TCP"; break;
case EGP:
name = "EGP"; break;
case PUP:
name = "PUP"; break;
case UDP:
name = "UDP"; break;
case HMP:
name = "HMP"; break;
case XNSIDP:
name = "XNSIDP"; break;
case RDP:
name = "RDP"; break;
case ISOTP4:
name = "ISOTP4"; break;
case XTP:
name = "XTP"; break;
case IDPRCMTP:
name = "IDPRCMTP"; break;
case RSVP:
name = "RSVP"; break;
case VMTP:
name = "VMTP"; break;
case OSPF:
name = "OSPF"; break;
case IPIP:
name = "IPIP"; break;
case ENCAP:
name = "ENCAP"; break;
default:
name = unknown; break;
}
return name;
}
void checklocale(void) {
char *lc;
if ((lc = getenv("LC_CTYPE")) != NULL) setlocale(LC_CTYPE,lc);
else if ((lc = getenv("LC_ALL")) != NULL) setlocale(LC_ALL,lc);
return;
}
/****==========------------------------------------------------==========****/
/* */
/* Decode a "tcpdump" savefile. */
/* */
/****==========------------------------------------------------==========****/
int main (int argc, char **argv) {
/* Command line options. */
while (--argc > 0 && **++argv == '-')
if (strcmp(*argv, "-minHdrDecode") == 0)
minHdrDecodeFlag = noLinkFlag = noIpflag = TRUE;
elif (strcmp(*argv, "-s") == 0) sFlag = TRUE;
elif (strcmp(*argv, "-b") == 0) noBflag = TRUE;
elif (strcmp(*argv, "-sb") == 0) sbFlag = TRUE;
elif (strcmp(*argv, "-terse") == 0) terseFlag = TRUE;
elif (strcmp(*argv, "-verbose") == 0) {
verboseFlag = TRUE;
terseFlag = FALSE;
}
elif (strcmp(*argv, "-track") == 0) trackFlag = TRUE;
elif (strcmp(*argv, "-noData") == 0) noDataFlag = TRUE;
elif (strcmp(*argv, "-noLink") == 0) noLinkFlag = TRUE;
elif (strcmp(*argv, "-noIp") == 0) noIpflag = TRUE;
elif (strcmp(*argv, "-noHostNames") == 0) noHostNames = TRUE;
#if !defined(NOETHERNAMES)
elif (strcmp(*argv, "-noEtherNames") == 0) noEtherNames = TRUE;
#endif
elif (strcmp(*argv, "-noPortNames") == 0) noPortNames = TRUE;
elif (strcmp(*argv, "-fqdn") == 0) fqdnFlag = TRUE;
elif (strcmp(*argv, "-cooked") == 0) cookedFlag = TRUE;
elif (strcmp(*argv, "-pp") == 0) ppFlag = TRUE;
elif (strcmp(*argv, "-h") == 0) usage();
elif (strcmp(*argv, "-w") == 0) {
if (--argc <= 0) error("-w needs a numeric argument");
if ((pageWidth=atoi(*++argv)) < 1) error("-w value too small");
}
else error("Unknown command line flag");
checklocale();
if (!cookedFlag)
forkTcpdump(argc, argv);
elif (argc != 0)
fprintf(stderr, "input is cooked -- ignoring tcpdump expressions\n");
// tm020221
// changed setjmp/longjmp logic to trigger the showPkt()
for ( ; ; ) {
pkt = getPkt();
if (setjmp(jmpBuf) || nPktsShown <= 0)
showPkt(pkt);
}
exit(0);
return 0;
}
/****==========------------------------------------------------==========****/
/* */
/* Return the next character in the packet buffer. */
/* */
/****==========------------------------------------------------==========****/
static char nextChar (char **pPkt) {
if (!**pPkt) *pPkt = getPkt();
return *(*pPkt)++;
}
/****==========------------------------------------------------==========****/
/* */
/* If noPortNames is true, then if wantNumber is true, return the port */
/* number, otherwise return the value of 'unknown'; */
/* otherwise, if the port has a name return that name; */
/* otherwise, if wantNumber is true return the port number; */
/* otherwise, return the value of 'unknown'. */
/* */
/****==========------------------------------------------------==========****/
static char *portName (uint2 port, char *proto, boolean wantNumber) {
char *name;
static char number[6];
struct servent *service; /* Doesn't need to be static */
// We could tighten this code up a little, but don't want to call
// getservbyport() unless necessary.
if (noPortNames)
if (!wantNumber)
name = unknown;
else {
(void)sprintf(number, "%d", port);
name = number;
}
/* The crappy manpage doesn't say the port must be in net byte order. */
elif ( (service = getservbyport((int)htons(port), proto)) )
name = service->s_name;
elif (!wantNumber)
name = unknown;
else {
(void)sprintf(number, "%u", port);
name = number;
}
if (strlen(name) > MAX_PORTNAMELEN) error("Port name too long");
return name;
}
/****==========------------------------------------------------==========****/
/* */
/* Remove whitespace from the buffer. */
/* */
/****==========------------------------------------------------==========****/
static char *rmWSpace (reg char *pktBuf) {
static char cleanPkt[MAXPKT+1];
reg char *cleanBuf;
cleanBuf = cleanPkt;
while (*pktBuf) {
if (!isspace(*pktBuf)) *cleanBuf++ = *pktBuf;
pktBuf++;
}
*cleanBuf = '\0';
if ((*cleanPkt == '0') && (*(cleanPkt+1) == 'x'))
return cleanPkt+7;
return cleanPkt;
}
/****==========------------------------------------------------==========****/
/* */
/* Decode the ARP data. */
/* */
/* This function and showRarp() could be merged into one. */
/* */
/****==========------------------------------------------------==========****/
static char *showArp (char *p) {
uint1 hLen;
uint2 hType;
uint2 op;
uint1 pLen;
uint2 pType;
char sEtherAddr[ETHER_ADDRLEN+1]; // Sender Ethernet address
char sEtherName[MAX_HOSTNAMELEN+1]; // Sender Ethernet name
char sHostName[MAX_HOSTNAMELEN+1]; // Sender hostname
char sIpAddr[IP_ADDRLEN+1]; // Sender IP address
char tEtherAddr[ETHER_ADDRLEN+1]; // Target Ethernet address
char tEtherName[MAX_HOSTNAMELEN+1]; // Target Ethernet name
char tHostName[MAX_HOSTNAMELEN+1]; // Target hostname
char tIpAddr[IP_ADDRLEN+1]; // Target IP address
uint2 nSkipped;
hType = getWord(&p); nSkipped = sizeof(hType);
pType = getWord(&p); nSkipped = sizeof(pType);
hLen = getByte(&p); nSkipped = sizeof(hLen);
pLen = getByte(&p); nSkipped = sizeof(pLen);
op = getWord(&p); nSkipped = sizeof(op);
(void)strcpy(sEtherAddr, etherAddr(0, &p)); nSkipped += 6;
(void)strcpy(sIpAddr, ipAddr(&p)); nSkipped += 4;
(void)strcpy(tEtherAddr, etherAddr(0, &p)); nSkipped += 6;
(void)strcpy(tIpAddr, ipAddr(&p)); nSkipped += 4;
(void)strcpy(sEtherName, etherName(sEtherAddr, TRUE));
(void)strcpy(tEtherName, etherName(tEtherAddr, TRUE));
(void)strcpy(sHostName, hostName(sIpAddr, TRUE));
(void)strcpy(tHostName, hostName(tIpAddr, TRUE));
if (minHdrDecodeFlag) {
switch (op) {
case ARP_REQ:
printf(
"%s (%s) asks where is %s\n",
sHostName, sEtherName, tHostName
);
break;
case ARP_RSP:
printf(
"%s says to %s it's at %s\n",
sHostName, tHostName, sEtherName
);
break;
}
return p;
}
if (terseFlag) {
printf(
"ARP:\thtype=%s ptype=%s hlen=%d plen=%d op=%s\n",
hType == ARP_HW_ETHER? "Ethernet": unknown,
pType == ARP_PROTO_IP? "IP": unknown,
hLen, pLen, op == ARP_REQ? "request": "response"
);
printf(
"\tsender-MAC-addr=%s sender-IP-address=%s\n",
sEtherName, sHostName
);
printf(
"\ttarget-MAC-addr=%s target-IP-address=%s\n",
tEtherName, tHostName
);
}
else {
printf("ARP Header\n");
printf(
"\tHardware Type:\t\t\t%s\n",
hType == ARP_HW_ETHER? "Ethernet": unknown
);
printf(
"\tProtocol Type:\t\t\t%s\n",
pType == ARP_PROTO_IP? "IP": unknown
);
printf("\tHardware Address Length:\t%d bytes\n", hLen);
printf("\tProtocol Address Length:\t%d bytes\n", pLen);
printf(
"\tOperation:\t\t\tARP %s\n",
op == ARP_REQ? "request": "response"
);
printf("\tSender Hardware Address:\t%s", sEtherAddr);
if (!noEtherNames) printf(" (%s)", etherName(sEtherAddr, FALSE));
printf("\n\tSender IP Address:\t\t%s", sIpAddr);
if (!noHostNames) printf(" (%s)", hostName(sIpAddr, FALSE));
printf("\n\tTarget Hardware Address:\t%s", tEtherAddr);
if (!noEtherNames) printf(" (%s)", etherName(tEtherAddr, FALSE));
printf("\n\tTarget IP Address:\t\t%s", tIpAddr);
if (!noHostNames) printf(" (%s)", hostName(tIpAddr, FALSE));
putchar('\n');
}
return p;
}
/****==========------------------------------------------------==========****/
/* */
/* Decode the TCP/UDP data. */
/* */
/****==========------------------------------------------------==========****/
static char *showData (char *p) {
uint1 byte;
int col;
char *descr;
if (minHdrDecodeFlag)
putchar('\t');
elif (terseFlag)
printf("DATA:\t");
else {
switch (proto) {
case TCP: descr = "TCP"; break;
case UDP: descr = "UDP"; break;
case ICMP: descr = "ICMP"; break;
default: descr = unknown; break;
}
printf("%s Data\n\t", descr);
}
if (noDataFlag) {
uint2 ndatabytes = dataLen;
dataLen = 0;
printf("%d bytes\n", ndatabytes);
return skip(p, ndatabytes);
}
if (dataLen == 0) {
printf("<No data>\n");
return p;
}
switch (noBflag) {
case FALSE:
for (col = 1; dataLen > 0; dataLen--, col++) {
byte = getByte(&p);
if (byte == '\n') {
putchar('\n');
byte = '\t';
col = 0;
}
elif (col > pageWidth) {
printf("%s\n\t", sbFlag? "<br>": "");
col = 1;
}
if (byte != '\t' && byte != '\n' && !isprint(byte)) byte = '.';
putchar(byte);
}
break;
case TRUE:
for ( ; dataLen > 0; dataLen--) {
byte = getByte(&p);
if (byte == '\n') {
putchar('\n');
byte = '\t';
}
if (byte != '\t' && byte != '\n' && !isprint(byte)) byte = '.';
putchar(byte);
}
break;
default:
error("Tri-valued boolean!");
}
putchar('\n');
return p;
}
/****==========------------------------------------------------==========****/
/* */
/* Decode the packet header. */
/* */
/****==========------------------------------------------------==========****/
static char *showHdr (char *p) {
char eFrom[ETHER_ADDRLEN+1]; /* Source Ethernet address */
char eFromName[MAX_HOSTNAMELEN+1]; // Sender Ethernet name
char eTo[ETHER_ADDRLEN+1]; /* Destination Ethernet address */
char eToName[MAX_HOSTNAMELEN+1]; // Target Ethernet name
char eType[40]; /* Ethernet type (decoded to ASCII) */
static double prevTime; // Timestamp of previous packet
char time[16]; /* Packet timestamp */
if (ppFlag) {
(void)sscanf(p, "%s", time);
etherType = ETHER_PROTO_IP; /* tcpdump doesn't supply link type */
if (!noLinkFlag) {
if (terseFlag)
printf("TIME:\t%s%s\n", time, deltaTime(&prevTime, time));
else
printf(
"\tTimestamp:\t\t\t%s%s\n", time, deltaTime(&prevTime, time)
);
}
return getPkt();
}
(void)sscanf(p, "%s %s %s %s", time, eFrom, eTo, eType);
/* decode output from tcpdump-3.8.x and later */
/* format: TIME MACSRC > MACDST, ethertype TYPE (0xCODE), ... */
if (*eTo == '>') {
char *s;
(void)sscanf(p, "%s %s > %s", time, eFrom, eTo);
if ((s = strstr(p, "ethertype ")) != NULL) {
strlcpy(eType, s+10, sizeof(eType));
if ((s = strchr(eType, ' ')) != NULL) {
*s = '\0';
*(s+8)='\0';
(void)etherProto(s+4, ðerType);
}
else {
etherType = 0;
}
}
else {
strlcpy(eType, unknown, sizeof(eType));
etherType = 0;
}
}
/* decode output from tcpdump-3.7.4 and earlier */
else
(void)etherProto(eType, ðerType);
(void)strcpy(eFrom, etherAddr(eFrom, 0));
(void)strcpy(eFromName, etherName(eFrom, TRUE));
(void)strcpy(eTo, etherAddr(eTo, 0));
(void)strcpy(eToName, etherName(eTo, TRUE));
if (!noLinkFlag) {
if (terseFlag) {
printf("TIME:\t%s%s\n", time, deltaTime(&prevTime, time));
printf(
"LINK:\t%s -> %s type=%s\n",
eFromName, eToName, etherProto(eType, 0)
);
}
else {
printf("\tTimestamp:\t\t\t%s%s\n", time, deltaTime(&prevTime, time));
printf("\tSource Ethernet Address:\t%s", eFrom);
if (!noEtherNames) printf(" (%s)", etherName(eFrom, FALSE));
printf("\n\tDestination Ethernet Address:\t%s", eTo);
if (!noEtherNames) printf(" (%s)", etherName(eTo, FALSE));
printf("\n\tEncapsulated Protocol:\t\t%s\n", etherProto(eType, 0));
}
}
return getPkt();
}
/****==========------------------------------------------------==========****/
/* */
/* Decode the ICMP header. */
/* */
/****==========------------------------------------------------==========****/
static char *showIcmp (char *p) {
uint2 cksum;
uint1 code;
char *extraInfo;
char *msgType;
uint2 nSkipped;
uint1 type;
char *why;
type = getByte(&p); nSkipped = sizeof(type);
code = getByte(&p); nSkipped += sizeof(code);
cksum = getWord(&p); nSkipped += sizeof(cksum);
msgType = icmpType(type);
why = icmpCode(type, code);
extraInfo = icmpExtras(type, code, &p, &nSkipped);
/* The length of the ICMP packet isn't recorded in the packet itself. */
dataLen -= nSkipped;
if (minHdrDecodeFlag) {
printf(
"%s -> %s ICMP %s%s%s\n",
sHostName, dHostName, msgType, why? " because ": "", why? why: ""
);
if (extraInfo) printf("\t%s\n", extraInfo);
return p; /* Header is read; nothing to skip */
}
if (terseFlag) {
printf(
"ICMP:\t%s%s%s cksum=%04X\n",
msgType, why? " because ": "", why? why: "", cksum
);
if (extraInfo) printf("\t%s\n", extraInfo);
}
else {
printf("ICMP Header\n");
printf(
"\tType:\t\t\t\t%s%s%s\n",
msgType, why? "\n\tBecause:\t\t\t": "", why? why: ""
);
if (extraInfo) printf("\tAdditional Information:\t\t%s\n", extraInfo);
printf("\tChecksum:\t\t\t0x%04X\n", cksum);
}
return p;
}
/****==========------------------------------------------------==========****/
/* */
/* Decode the IP header. */
/* */
/****==========------------------------------------------------==========****/
static char *showIp (char *p) {
uint2 cksum;
uint2 dgramLen;
uint2 flags;
uint2 hLen;
uint2 id;
uint2 nSkipped;
uint1 servType;
uint1 ttl;
uint1 ver;
ver = getByte(&p); nSkipped = sizeof(ver);
if ((ver & 0xF0) != 0x40) {
if (terseFlag) printf("IP:\tnot v4\n");
else
printf(
"IP Header\n\t<Not an IPv4 datagram (ver=%d)>\n",
(ver & 0xF0) >> 4
);
nextPkt();
}
servType = getByte(&p); nSkipped += sizeof(servType);
dgramLen = getWord(&p); nSkipped += sizeof(dgramLen);
id = getWord(&p); nSkipped += sizeof(id);
flags = getWord(&p); nSkipped += sizeof(flags);
ttl = getByte(&p); nSkipped += sizeof(ttl);
proto = getByte(&p); nSkipped += sizeof(proto);
cksum = getWord(&p); nSkipped += sizeof(cksum);
(void)strcpy(sIp, ipAddr(&p)); nSkipped += 4;
(void)strcpy(dIp, ipAddr(&p)); nSkipped += 4;
hLen = (ver & 0x0F) * 4;
dataLen = dgramLen - hLen;
(void)strcpy(sHostName, hostName(sIp, TRUE));
(void)strcpy(dHostName, hostName(dIp, TRUE));
if (noIpflag) return skip(p, hLen - nSkipped);
printf("%s", terseFlag? " IP:\t": "IP Header\n");
if (terseFlag) {
printf(
"%s -> %s hlen=%d TOS=%02X dgramlen=%d id=%04X\n",
sHostName, dHostName, hLen, (uint2)servType, dgramLen, id
);
printf(
"\tMF/DF=%s/%s frag=%d TTL=%d proto=%s cksum=%04X\n",
(flags & MF) == MF? "1": "0", (flags & DF) == DF? "1": "0",
flags & FRAGOFF, ttl, ipProto(proto), cksum
);
}
else {
printf("\tVersion:\t\t\t4\n\tHeader Length:\t\t\t%d bytes\n", hLen);
printf("\tService Type:\t\t\t0x%02X\n", (uint2)servType);
printf("\tDatagram Length:\t\t%d bytes\n", dgramLen);
printf("\tIdentification:\t\t\t0x%04X\n", id);
printf(
"\tFlags:\t\t\t\tMF=%s DF=%s\n",
(flags & MF) == MF? on: off, (flags & DF) == DF? on_e: off_e
);
printf("\tFragment Offset:\t\t%d\n", flags & FRAGOFF);
printf("\tTTL:\t\t\t\t%d\n", ttl);
printf("\tEncapsulated Protocol:\t\t%s\n", ipProto(proto));
printf("\tHeader Checksum:\t\t0x%04X\n", cksum);
printf("\tSource IP Address:\t\t%s", sIp);
if (!noHostNames) printf(" (%s)", hostName(sIp, FALSE));
printf("\n\tDestination IP Address:\t\t%s", dIp);
if (!noHostNames) printf(" (%s)", hostName(dIp, FALSE));
putchar('\n');
}
if (hLen > IPHDRLEN) {
if (!terseFlag) printf("\t<Options not displayed>\n");
p = skip(p, hLen - IPHDRLEN);
}
return p;
}
/****==========------------------------------------------------==========****/
/* */
/* Decode the packet chunk in the buffer. */
/* */
/****==========------------------------------------------------==========****/
static void showPkt (reg char *p) {
char *warnMsg = "<*** No decode support for encapsulated protocol ***>";
char *warnMsg2 = "<*** No decode support for encap protocol in IPIP packet ***>";
prSep();
printf("Packet %d\n", ++nPktsShown);
p = showHdr(p);
switch (etherType) {
case ETHER_PROTO_ARP:
p = showArp(p);
break;
case ETHER_PROTO_RARP:
p = showRarp(p);
break;
case ETHER_PROTO_IP:
p = showIp(p);
switch (proto) {
case TCP:
p = showTcp(p);
p = showData(p);
break;
case UDP:
p = showUdp(p);
p = showData(p);
break;
case ICMP:
p = showIcmp(p);
p = showData(p);
break;
// IPIP decode support by M. Nowlin (mike@argos.org) 20000321
case IPIP:
p = showIp(p);
switch(proto) {
case TCP:
p = showTcp(p);
p = showData(p);
break;
case UDP:
p = showUdp(p);
p = showData(p);
break;
case ICMP:
p = showIcmp(p);
p = showData(p);
break;
default:
printf("\t%s\n", warnMsg2);
nextPkt();
break;
}
break;
default:
printf("\t%s\n", warnMsg);
nextPkt(); /* Doesn't return */
}
break;
default:
if (!minHdrDecodeFlag) printf("\t%s\n", warnMsg);
nextPkt();
break;
}
/* "tcpdump" sometimes displays data at the end of a packet which, given */
/* the recorded Datagram Length, don't belong to the packet. */
if (*p) {
if (sFlag) printf("\t<*** Spurious data at end: \"%s\" ***>\n", p);
nextPkt();
}
/* Note that if getPkt() returns here, then the line read isn't the */
/* start of a new packet, i.e. there's spurious data. */
if ( (p = getPkt()) ) {
if (sFlag) printf("\t<*** Spurious data at end: \"%s\" ***>\n", p);
nextPkt();
}
}
/****==========------------------------------------------------==========****/
/* */
/* Decode the RARP data. */
/* */
/* This function and showArp() could be merged into one. */
/* */
/****==========------------------------------------------------==========****/
static char *showRarp (char *p) {
uint1 hLen;
uint2 hType;
uint2 op;
uint1 pLen;
uint2 pType;
char sEtherAddr[ETHER_ADDRLEN+1]; // Sender Ethernet address
char sEtherName[MAX_HOSTNAMELEN+1]; // Sender Ethernet name
char sHostName[MAX_HOSTNAMELEN+1]; // Sender hostname
char sIpAddr[IP_ADDRLEN+1]; // Sender IP address
char tEtherAddr[ETHER_ADDRLEN+1]; // Target Ethernet address
char tEtherName[MAX_HOSTNAMELEN+1]; // Target Ethernet name
char tHostName[MAX_HOSTNAMELEN+1]; // Target hostname
char tIpAddr[IP_ADDRLEN+1]; // Target IP address
uint2 nSkipped;
hType = getWord(&p); nSkipped = sizeof(hType);
pType = getWord(&p); nSkipped = sizeof(pType);
hLen = getByte(&p); nSkipped = sizeof(hLen);
pLen = getByte(&p); nSkipped = sizeof(pLen);
op = getWord(&p); nSkipped = sizeof(op);
(void)strcpy(sEtherAddr, etherAddr(0, &p)); nSkipped += 6;
(void)strcpy(sIpAddr, ipAddr(&p)); nSkipped += 4;
(void)strcpy(tEtherAddr, etherAddr(0, &p)); nSkipped += 6;
(void)strcpy(tIpAddr, ipAddr(&p)); nSkipped += 4;
(void)strcpy(sEtherName, etherName(sEtherAddr, TRUE));
(void)strcpy(tEtherName, etherName(tEtherAddr, TRUE));
(void)strcpy(sHostName, hostName(sIpAddr, TRUE));
(void)strcpy(tHostName, hostName(tIpAddr, TRUE));
if (minHdrDecodeFlag) {
switch (op) {
case RARP_REQ:
printf(
"%s asks for its %s address\n",
sEtherName, pType == ARP_PROTO_IP? "Ethernet": unknown
);
break;
case RARP_RSP:
printf(
"%s says to %s its %s address is %s\n",
sHostName, tEtherName,
pType == ARP_PROTO_IP? "Ethernet": unknown, tHostName
);
break;
}
return p;
}
if (terseFlag) {
printf(
"RARP:\thtype=%s ptype=%s hlen=%d plen=%d op=%s\n",
hType == ARP_HW_ETHER? "Ethernet": unknown,
pType == ARP_PROTO_IP? "IP": unknown,
hLen, pLen, op == RARP_REQ? "request": "response"
);
printf(
"\tsender-MAC-addr=%s sender-IP-address=%s\n",
sEtherName, sHostName
);
printf(
"\ttarget-MAC-addr=%s target-IP-address=%s\n",
tEtherName, tHostName
);
}
else {
printf("RARP Header\n");
printf(
"\tHardware Type:\t\t\t%s\n",
hType == ARP_HW_ETHER? "Ethernet": unknown
);
printf(
"\tProtocol Type:\t\t\t%s\n",
pType == ARP_PROTO_IP? "IP": unknown
);
printf("\tHardware Address Length:\t%d bytes\n", hLen);
printf("\tProtocol Address Length:\t%d bytes\n", pLen);
printf(
"\tOperation:\t\t\tRARP %s\n",
op == RARP_REQ? "request": "response"
);
printf("\tSender Hardware Address:\t%s", sEtherAddr);
if (!noEtherNames) printf(" (%s)", etherName(sEtherAddr, FALSE));
printf("\n\tSender IP Address:\t\t%s", sIpAddr);
if (!noHostNames) printf(" (%s)", hostName(sIpAddr, FALSE));
printf("\n\tTarget Hardware Address:\t%s", tEtherAddr);
if (!noEtherNames) printf(" (%s)", etherName(tEtherAddr, FALSE));
printf("\n\tTarget IP Address:\t\t%s", tIpAddr);
if (!noHostNames) printf(" (%s)", hostName(tIpAddr, FALSE));
putchar('\n');
}
return p;
}
/****==========------------------------------------------------==========****/
/* */
/* Decode the TCP header. */
/* */
/****==========------------------------------------------------==========****/
static char *showTcp (char *p) {
uint4 ack;
uint2 advert;
uint2 cksum;
uint2 dPort;
char dPortName[MAX_PORTNAMELEN+1];
uint4 expect;
uint2 flags;
uint2 hLen;
uint2 nSkipped;
uint4 seq;
uint2 sPort;
char sPortName[MAX_PORTNAMELEN+1];
uint2 urgPtr;
sPort = getWord(&p); nSkipped = sizeof(sPort);
dPort = getWord(&p); nSkipped += sizeof(dPort);
seq = getLongWord(&p); nSkipped += sizeof(seq);
ack = getLongWord(&p); nSkipped += sizeof(ack);
flags = getWord(&p); nSkipped += sizeof(flags);
advert = getWord(&p); nSkipped += sizeof(advert);
cksum = getWord(&p); nSkipped += sizeof(cksum);
urgPtr = getWord(&p); nSkipped += sizeof(urgPtr);
hLen = (flags >> 12 & 0x0F) * 4;
dataLen -= hLen;
(void)strcpy(sPortName, portName(sPort, "tcp", TRUE));
(void)strcpy(dPortName, portName(dPort, "tcp", TRUE));
if (minHdrDecodeFlag) {
printf(
"%s.%s -> %s.%s over TCP\n",
sHostName, sPortName, dHostName, dPortName
);
return skip(p, hLen - nSkipped);
}
if (trackFlag) {
expect = seq + dataLen;
if ((flags & SYN) == SYN || (flags & FIN) == FIN) expect++;
}
if (terseFlag) {
printf(
" TCP:\tport %s -> %s seq=%010lu", sPortName, dPortName, (u_long)seq
);
if (trackFlag) printf(" (expect=%010lu)", (u_long)expect);
printf(" ack=%010lu\n", (u_long)ack);
printf(
"\thlen=%d (data=%u) UAPRSF=%s%s%s%s%s%s",
hLen, dataLen,
(flags & URG) == URG? "1": "0", (flags & ACK) == ACK? "1": "0",
(flags & PSH) == PSH? "1": "0", (flags & RST) == RST? "1": "0",
(flags & SYN) == SYN? "1": "0", (flags & FIN) == FIN? "1": "0"
);
printf(" wnd=%d cksum=%04X urg=%d\n", advert, cksum, urgPtr);
}
else {
printf("TCP Header\n");
printf("\tSource Port:\t\t\t%d", sPort);
if (!noPortNames) printf(" (%s)", portName(sPort, "tcp", FALSE));
printf("\n\tDestination Port:\t\t%d", dPort);
if (!noPortNames) printf(" (%s)", portName(dPort, "tcp", FALSE));
printf("\n\tSequence Number:\t\t%010lu\n", (u_long)seq);
if (trackFlag) printf("\tExpect peer ACK:\t\t%010lu\n", (u_long)expect);
printf("\tAcknowledgement Number:\t\t%010lu\n", (u_long)ack);
printf("\tHeader Length:\t\t\t%d bytes (data=%u)\n", hLen, dataLen);
printf(
"\tFlags:%s%s%s%s%s%s\n%s%s%s%s%s%s\n",
"\t\t\t\tURG=", (flags & URG) == URG? on: off,
" ACK=", (flags & ACK) == ACK? on: off,
" PSH=", (flags & PSH) == PSH? on_e: off_e,
"\t\t\t\t\tRST=", (flags & RST) == RST? on: off,
" SYN=", (flags & SYN) == SYN? on: off,
" FIN=", (flags & FIN) == FIN? on_e: off_e
);
printf("\tWindow Advertisement:\t\t%d bytes\n", advert);
printf("\tChecksum:\t\t\t0x%04X\n", cksum);
printf("\tUrgent Pointer:\t\t\t%d\n", urgPtr);
}
if (hLen > TCPHDRLEN) {
if (!terseFlag) printf("\t<Options not displayed>\n");
p = skip(p, hLen - TCPHDRLEN);
}
return p;
}
/****==========------------------------------------------------==========****/
/* */
/* Decode the UDP header. */
/* */
/****==========------------------------------------------------==========****/
static char *showUdp (char *p) {
uint2 cksum;
uint2 dgramLen;
uint2 dPort;
char dPortName[MAX_PORTNAMELEN+1];
uint2 nSkipped;
uint2 sPort;
char sPortName[MAX_PORTNAMELEN+1];
sPort = getWord(&p); nSkipped = sizeof(sPort);
dPort = getWord(&p); nSkipped += sizeof(dPort);
dgramLen = getWord(&p); nSkipped += sizeof(dgramLen);
cksum = getWord(&p); nSkipped += sizeof(cksum);
/* The size of the IP data field should equal the UDP packet length. */
if (dataLen != dgramLen) {
printf("\t<*** Packet length corrupt ***>\n");
nextPkt(); /* Doesn't return */
}
dataLen -= UDPHDRLEN;
(void)strcpy(sPortName, portName(sPort, "udp", TRUE));
(void)strcpy(dPortName, portName(dPort, "udp", TRUE));
if (minHdrDecodeFlag) {
printf(
"%s.%s -> %s.%s over UDP\n",
sHostName, sPortName, dHostName, dPortName
);
return p; /* Header is read; nothing to skip */
}
if (terseFlag)
printf(
" UDP:\tport %s -> %s hdr=%u data=%u\n",
sPortName, dPortName, UDPHDRLEN, dataLen
);
else {
printf("UDP Header\n");
printf("\tSource Port:\t\t\t%d", sPort);
if (!noPortNames) printf(" (%s)", portName(sPort, "udp", FALSE));
printf("\n\tDestination Port:\t\t%d", dPort);
if (!noPortNames) printf(" (%s)", portName(dPort, "udp", FALSE));
printf(
"\n\tDatagram Length:\t\t%u bytes (Header=%u, Data=%u)\n",
dgramLen, UDPHDRLEN, dataLen
);
printf("\tChecksum:\t\t\t0x%04X\n", cksum);
}
return p;
}
/****==========------------------------------------------------==========****/
/* */
/* Skip over un-interesting bytes. */
/* */
/****==========------------------------------------------------==========****/
static char *skip (char *p, uint2 nBytes) {
for ( ; nBytes > 0; nBytes--) (void)getByte(&p);
return p;
}
/****==========------------------------------------------------==========****/
/* */
/* Give a summary of usage. */
/* */
/****==========------------------------------------------------==========****/
static void usage () {
#if !defined(MAY_NOT_MODIFY)
printf("\nCopyright (c) 1996, 1997, 1998 I.T. NetworX Ltd. ");
printf("All rights reserved.\n");
printf("mailto:mike@NetworX.ie\n\n");
#endif
printf("tcpshow -- decode a tcpdump(1) savefile, giving a fully\n");
printf(" decoded display of Ethernet, ARP, RARP, IP, ICMP,\n");
printf(" UDP and TCP headers and an ASCII display of\n");
printf(" the application data.\n\n");
printf("Version %4.2f\n\n", VERSION);
printf("Usage: tcpshow [ options ... ] [ expr ]\n");
printf("\nwhere options are as follows\n");
printf("\t-b\t\tdo not break/wrap long lines\n");
printf("\t-sb\t\tshow breaks (show where we broke a line)\n");
printf("\t-w width\tset pagewidth to \"width\" columns (used by -b)\n");
printf("\t-noLink\t\tdon't decode link header (Ethernet header)\n");
printf("\t-noIp\t\tdon't decode IP header\n");
printf("\t-noHostNames\tdon't map IP addresses to host names\n");
printf("\t-fqdn\t\tshow host names as fully-qualified\n");
#if !defined(NOETHERNAMES)
printf("\t-noEtherNames\tdon't map Ethernet addresses to host names\n");
#endif
printf("\t-noPortNames\tdon't map port numbers to names\n");
printf("\t-noData\t\tdon't show data (show headers only)\n");
printf("\t-minHdrDecode\tshow only a minimal header decode\n");
printf("\t-track\t\ttrack sequence numbers (show next-expected ACK)\n");
printf("\t-terse\t\tshow header decode in compact format (default)\n");
printf("\t-verbose\tshow header decode in expanded format\n");
printf("\t-cooked\t\tdon't run tcpdump to pre-process the input\n");
printf("\t-pp\t\tpoint-to-point link (no Ethernet header available)\n");
printf("\t-s\t\tdisplay hex dump of spurious data at packet-end\n");
printf("\t-h\t\tdisplay this help summary\n\n");
printf("expr is a tcpdump(1) expression, and is only valid when ");
printf("the -cooked\noption is not used.\n\n");
printf("Input is from stdin, ");
printf("which must be a raw tcpdump(1) data file (savefile),\n");
printf("unless the -cooked option is used, in which case stdin ");
printf("must be in the\nformat produced by tcpdump -lenx.\n\n");
printf("Output is to stdout.\n\n");
printf("tcpdump(1) must be on your PATH unless -cooked is used.\n\n");
exit(0);
}
syntax highlighted by Code2HTML, v. 0.9.1