/* * TCPVIEW * * Author: Martin Hunt * Networks and Distributed Computing * Computing & Communications * University of Washington * Administration Building, AG-44 * Seattle, WA 98195 * Internet: martinh@cac.washington.edu * * * Copyright 1992 by the University of Washington * * Permission to use, copy, modify, and distribute this software and its * documentation for any purpose and without fee is hereby granted, provided * that the above copyright notice appears in all copies and that both the * above copyright notice and this permission notice appear in supporting * documentation, and that the name of the University of Washington not be * used in advertising or publicity pertaining to distribution of the software * without specific, written prior permission. This software is made * available "as is", and * THE UNIVERSITY OF WASHINGTON DISCLAIMS ALL WARRANTIES, EXPRESS OR IMPLIED, * WITH REGARD TO THIS SOFTWARE, INCLUDING WITHOUT LIMITATION ALL IMPLIED * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE, AND IN * NO EVENT SHALL THE UNIVERSITY OF WASHINGTON BE LIABLE FOR ANY SPECIAL, * INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM * LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, TORT * (INCLUDING NEGLIGENCE) OR STRICT LIABILITY, ARISING OUT OF OR IN CONNECTION * WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. * */ /* * Copyright (c) 1988-1990 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 LlIMITATION, THE IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. */ #ifndef lint static char rcsid[] = "@(#) $Header: /usr/staff/martinh/tcpview/RCS/print-tcp.c,v 1.3 1993/04/22 20:33:23 martinh Exp $ (UW)"; #endif #include #include #include #include #include #include #include #include #include #ifdef TCPVIEW #include "tcpview.h" #endif #include "interface.h" #include "addrtoname.h" #ifndef TCPOPT_WSCALE #define TCPOPT_WSCALE 3 /* window scale factor (rfc1072) */ #endif #ifndef TCPOPT_SACKOK #define TCPOPT_SACKOK 4 /* selective ack ok (rfc1072) */ #endif #ifndef TCPOPT_SACK #define TCPOPT_SACK 5 /* selective ack (rfc1072) */ #endif #ifndef TCPOPT_ECHO #define TCPOPT_ECHO 6 /* echo (rfc1072) */ #endif #ifndef TCPOPT_ECHOREPLY #define TCPOPT_ECHOREPLY 7 /* echo (rfc1072) */ #endif struct tha { struct in_addr src; struct in_addr dst; u_int port; }; struct tcp_seq_hash { struct tcp_seq_hash *nxt; struct tha addr; tcp_seq seq; tcp_seq ack; u_short ackinit; u_short seqinit; }; #define TSEQ_HASHSIZE 919 static struct tcp_seq_hash tcp_seq_hash[TSEQ_HASHSIZE]; extern int aflag, zflag, Zflag, Xflag; static void assemble(); void tcp_print(tp, length, ip) register struct tcphdr *tp; register int length; register struct ip *ip; { register u_char flags; register int hlen; u_short sport, dport, win, urp; u_long seq, ack; if ((u_char *)(tp + 1) > snapend) { printf("[|tcp]"); return; } if (length < sizeof(struct tcphdr)) { (void)printf("truncated-tcp %d", length); return; } sport = ntohs(tp->th_sport); dport = ntohs(tp->th_dport); seq = ntohl(tp->th_seq); ack = ntohl(tp->th_ack); win = ntohs(tp->th_win); urp = ntohs(tp->th_urp); /* write TCP data */ if( zflag ) { u_char *data; int len = length - tp->th_off * 4; data = (u_char *)((u_long)tp + (u_long)tp->th_off * 4); if( len ) { if ( aflag ) assemble(seq, (u_long)len, data, (int)tp->th_flags&TH_SYN, ip->ip_src ); else { if ( Xflag ) default_print(data, len); else { fwrite(data, 1, len, stdout); } } } #ifndef TCPVIEW return; #endif } (void)printf("%s.%s > %s.%s: ", ipaddr_string(&ip->ip_src), tcpport_string(sport), ipaddr_string(&ip->ip_dst), tcpport_string(dport)); if (qflag) { (void)printf("tcp %d", length - tp->th_off * 4); goto checkout; } if ((flags = tp->th_flags) & (TH_SYN|TH_FIN|TH_RST|TH_PUSH)) { if (flags & TH_SYN) putchar('S'); if (flags & TH_FIN) putchar('F'); if (flags & TH_RST) putchar('R'); if (flags & TH_PUSH) putchar('P'); } else putchar('.'); if (!Sflag) { register struct tcp_seq_hash *th; register int rev; struct tha tha; /* * Find (or record) the initial sequence numbers for * this conversation. (we pick an arbitrary * collating order so there's only one entry for * both directions). */ if (sport < dport || (sport == dport && ip->ip_src.s_addr < ip->ip_dst.s_addr)) { tha.src = ip->ip_src, tha.dst = ip->ip_dst; tha.port = sport << 16 | dport; rev = 0; } else { tha.src = ip->ip_dst, tha.dst = ip->ip_src; tha.port = dport << 16 | sport; rev = 1; } for (th = &tcp_seq_hash[tha.port % TSEQ_HASHSIZE]; th->nxt; th = th->nxt) if (!bcmp((char *)&tha, (char *)&th->addr, sizeof(th->addr))) break; if (!th->nxt || flags & TH_SYN) { /* didn't find it or new conversation */ if (!th->nxt) th->nxt = (struct tcp_seq_hash *) calloc(1, sizeof (*th)); th->addr = tha; if (rev) { th->ack = seq; seq = 0; th->ackinit = 1; if( flags & TH_ACK) { th->seq = ack - 1; ack = 1; th->seqinit = 1; } else th->seqinit=0; } else { th->seq = seq; seq = 0; th->seqinit = 1; if( flags & TH_ACK) { th->ack = ack - 1; ack = 1; th->ackinit = 1; } else th->ackinit=0; } } else { if (rev) { if( th->ackinit ) seq -= th->ack; else { th->ack = seq; seq = 0; th->ackinit = 1; } if( th->seqinit ) ack -= th->seq; else if( flags & TH_ACK ) { th->seq = ack - 1; ack = 1; th->seqinit = 1; } } else { if( th->seqinit ) seq -= th->seq; else { th->seq = seq; seq = 0; th->seqinit = 1; } if( th->ackinit ) ack -= th->ack; else if( flags & TH_ACK ) { th->ack = ack - 1; ack = 1; th->ackinit = 1; } } } } hlen = tp->th_off * 4; length -= hlen; if (length > 0 || flags & (TH_SYN | TH_FIN | TH_RST)) (void)printf(" %lu:%lu(%d)", seq, seq + length, length); if (flags & TH_ACK) (void)printf(" ack %lu", ack); (void)printf(" win %d", win); if (flags & TH_URG) (void)printf(" urg %d", urp); /* * Handle any options. */ if ((hlen -= sizeof(struct tcphdr)) > 0) { register u_char *cp = (u_char *)tp + sizeof(struct tcphdr); int i; char ch = '<'; putchar(' '); while (--hlen >= 0) { putchar(ch); switch (*cp++) { case TCPOPT_MAXSEG: { u_short mss; #ifdef TCPDUMP_ALIGN bcopy((char *)cp + 1, (char *)&mss, sizeof(mss)); #else mss = *(u_short *)(cp + 1); #endif (void)printf("mss %d", ntohs(mss)); if (*cp != 4) (void)printf("[len %d]", *cp); cp += 3; hlen -= 3; break; } case TCPOPT_EOL: (void)printf("eol"); break; case TCPOPT_NOP: (void)printf("nop"); break; case TCPOPT_WSCALE: (void)printf("wscale %d", cp[1]); if (*cp != 3) (void)printf("[len %d]", *cp); cp += 2; hlen -= 2; break; case TCPOPT_SACKOK: (void)printf("sackOK"); if (*cp != 2) (void)printf("[len %d]", *cp); cp += 1; hlen -= 1; break; case TCPOPT_ECHO: { u_long v; #ifdef TCPDUMP_ALIGN bcopy((char *)cp + 1, (char *)&v, sizeof(v)); #else v = *(u_long *)(cp + 1); #endif (void)printf("echo %lu", v); if (*cp != 6) (void)printf("[len %d]", *cp); cp += 5; hlen -= 5; break; } case TCPOPT_ECHOREPLY: { u_long v; #ifdef TCPDUMP_ALIGN bcopy((char *)cp + 1, (char *)&v, sizeof(v)); #else v = *(u_long *)(cp + 1); #endif (void)printf("echoreply %lu", v); if (*cp != 6) (void)printf("[len %d]", *cp); cp += 5; hlen -= 5; break; } default: (void)printf("opt-%d:", cp[-1]); for (i = *cp++ - 2, hlen -= i + 1; i > 0; --i) (void)printf("%02x", *cp++); break; } ch = ','; } putchar('>'); } checkout: /* print both packets and data */ if( Zflag || ( aflag && !zflag ) ) { u_char *data; data = (u_char *)((u_long)tp + (u_long)tp->th_off * 4); /* if we printed the packet, send a newline too */ if( Zflag ) putchar('\n'); if( length ) { if( aflag ) assemble(seq, (u_long)length, data, (int)tp->th_flags&TH_SYN, ip->ip_src ); else { if ( Xflag ) default_print(data, length); else { fwrite(data, 1, length, stdout); } } } } } struct tcp_frag { u_long seq; u_long len; u_char *data; struct tcp_frag *next; }; static struct tcp_frag *Tfrag[2] = { NULL, NULL }; static u_long Sptr[2]; static u_long src[2] = { 0, 0 }; /* assemble a single bidirectional stream */ static int check_frags(); static void assemble( seq, len, data, syn, srcx ) u_long seq, len, srcx; u_char *data; int syn; { int i,j,first=0; u_long newptr; struct tcp_frag *t; /* fprintf(stderr,"assemble seq=%d len=%d syn=%d src=%lx\n",seq,len,syn,srcx); */ i = -1; for(j=0;j<2;j++) if( src[j] == srcx ) i = j; if( i < 0 ) { for(j=0;j<2;j++) { if( src[j] == 0 ) { /* fprintf(stderr,"src[%d]=%lx\n",j,srcx); */ src[j] = srcx; i = j; first = 1; break; } } } if( i < 0 ) { fprintf(stderr,"error: too many addresses seen during assembly\n"); return; } if (first) { Sptr[i] = seq+len; if(syn) Sptr[i]++; /* fprintf(stderr,"starting sequence at %d\n",Sptr[i]); */ if( zflag || Zflag ) if( Xflag ) default_print(data,len); else { #ifdef TCPVIEW fwrite(data,1,len,Xfile); #else fwrite(data,1,len,stdout); #endif } return; } /* fprintf(stderr,"Comparing %d to %d\n",seq,Sptr[i]); */ if ( seq < Sptr[i] ) { newptr = seq + len; if( newptr > Sptr[i] ) { data += (Sptr[i] - seq); seq = Sptr[i]; len = newptr - Sptr[i]; } } if( seq == Sptr[i] ) { Sptr[i] += len; if(syn) Sptr[i]++; if( Zflag || zflag ) if( Xflag ) default_print(data,len); else { #ifdef TCPVIEW fwrite(data,1,len,Xfile); #else fwrite(data,1,len,stdout); #endif } while (check_frags(i)) ; } else { /* out of order packet */ #ifdef TCPVIEW HighlightPacket = 1; #endif if( seq > Sptr[i] ) { /* fprintf(stderr,"out of order. seq=%d len=%d\n",seq,len); */ t = (struct tcp_frag *)malloc(sizeof(struct tcp_frag)); t->data = (u_char *)malloc(len); t->seq = seq; t->len = len; bcopy(data,t->data,len); if ( Tfrag[i] ) t->next = Tfrag[i]; else t->next = NULL; Tfrag[i] = t; } } } static int check_frags(i) int i; { struct tcp_frag *p = NULL; struct tcp_frag *t; t = Tfrag[i]; /* fprintf(stderr,"check frags for seq %d\n",Sptr[i]); */ while( t ) { /* fprintf(stderr,"t->seq = %d\n",t->seq); */ if ( t->seq == Sptr[i] ) { /* fprintf(stderr,"found seq=%d\n",Sptr[i]); */ if( Zflag || zflag ) if( Xflag ) default_print(t->data,t->len); else { #ifdef TCPVIEW fwrite(t->data,1,t->len,Xfile); #else fwrite(t->data,1,t->len,stdout); #endif } Sptr[i] += t->len; if ( p ) p->next = t->next; else Tfrag[i] = t->next; free(t->data); free(t); return 1; } p = t; t = t->next; } return 0; } #ifdef TCPVIEW void reset_tcp_assembly() { struct tcp_frag *t, *p; struct tcp_seq_hash *h1, *h2; int i; for(i=0;i<2;i++) { Sptr[i] = 0; src[i] = 0; t = Tfrag[i]; while(t) { p = t->next; free(t->data); free(t); t = p; } Tfrag[i] = NULL; } for(i=0;inxt; free( h1 ); h1 = h2; } } /* clear entire array */ bzero(&tcp_seq_hash[0],sizeof(struct tcp_seq_hash)*TSEQ_HASHSIZE); } #endif /* TCPVIEW */