#include #include #include "sokname.h" /* for SOKNAME */ #include "global.h" #include "timer.h" #include "mbuf.h" #include "netuser.h" #include "internet.h" #include "tcp.h" #include "cmdparse.h" long htol(),atol(); int atoi(),tcpval(); extern char notval[]; /* TCP connection states - reduced size of several for formatting - K5JB */ char *tcpstates[] = { "Closed", "Listen", "SYN sent", "SYN rcvd", "Estab.", "FINwait1", "FINwait2", "ClosWait", "Closing", "Last ACK", "Timewait" }; /* TCP closing reasons */ char *reasons[] = { "Normal", "Reset", "Timeout", "ICMP" }; /* TCP subcommand table */ int domss(),doirtt(),dortt(),dotcpstat(),dowindow(),dotcpkick(),dotcpreset(), dotcptimer(); struct cmds tcpcmds[] = { "irtt", doirtt, 0, NULLCHAR, NULLCHAR, "kick", dotcpkick, 2, "tcp kick ", notval, "mss", domss, 0, NULLCHAR, NULLCHAR, "reset", dotcpreset, 2, "tcp reset ", notval, "rtt", dortt, 3, "tcp rtt ", notval, "status", dotcpstat, 0, NULLCHAR, notval, "timertype", dotcptimer, 0, NULLCHAR, NULLCHAR, "window", dowindow, 0, NULLCHAR, NULLCHAR, NULLCHAR, NULLFP, 0, "tcp subcommands: irtt kick mss reset rtt status timertype window", NULLCHAR }; int dotcp(argc,argv) int argc; char *argv[]; { int subcmd(); return subcmd(tcpcmds,argc,argv); } #define MAX_TCBTAB 24 /* Arbitrary */ struct tcb *tcbtable[MAX_TCBTAB + 1]; struct tcb * simple_get(arg) int arg; { int i,j; struct tcb *tcb; if(arg == -1){ /* fill table */ tcbtable[0] = NULLTCB; /* [0] will contain # of entries */ for(i=0,j=1;inext){ tcbtable[j++] = tcb; if(j > MAX_TCBTAB) /* because of nested loops, this is */ goto quit; /* one place where a goto makes sense */ } quit: tcbtable[0] = (struct tcb *)(--j); return tcbtable[0]; } /* the last test is the reason for making a static table */ if(!arg || arg > (int)tcbtable[0] || !tcpval(tcbtable[arg])) return NULLTCB; else return tcbtable[arg]; } /* Eliminate a TCP connection */ static int dotcpreset(argc,argv) int argc; char *argv[]; { register struct tcb *tcb; extern int noprompt; /* something we need to clean up prompts */ if((tcb = simple_get(atoi(argv[1]))) == NULLTCB) return -1; cmdmode(); /* k35 */ noprompt = 1; /* to control prompts k35 */ close_self(tcb,RESET); return 0; } /* Force a retransmission */ static int dotcpkick(argc,argv) int argc; char *argv[]; { int kick_tcp(); register struct tcb *tcb; if((tcb = simple_get(atoi(argv[1]))) == NULLTCB || kick_tcp(tcb) == -1) return -1; return 0; } /* Note that in the four functions that follow, there is no constraint * against the user entering unrealistic numbers. We will incorrectly * assume that the user wouldn't be here if he didn't know what he was * doing -- K5JB */ /* Set default maximum segment size */ static int domss(argc,argv) int argc; char *argv[]; { if(argc < 2) printf("%u\n",tcp_mss); else tcp_mss = (int16)atoi(argv[1]); return 0; } /* Set default window size */ static int dowindow(argc,argv) int argc; char *argv[]; { if(argc < 2) printf("%u\n",tcp_window); else tcp_window = (int16)atoi(argv[1]); return 0; } /* Set initial round trip time for new connections */ static int doirtt(argc,argv) int argc; char *argv[]; { if(argc < 2) printf("%lu\n",tcp_irtt); else tcp_irtt = (int32)atol(argv[1]); return 0; } /* Set smoothed round trip time for specified TCB */ static int dortt(argc,argv) int argc; char *argv[]; { register struct tcb *tcb; if((tcb = simple_get(atoi(argv[1]))) == NULLTCB) return -1; tcb->srtt = (int32)atol(argv[2]); return 0; } /* Display status of TCBs */ static int dotcpstat(argc,argv) int argc; char *argv[]; { int tstat(); struct tcb *tcb; if(argc < 2) tstat(); else if((tcb = simple_get(atoi(argv[1]))) == NULLTCB) return -1; else state_tcp(tcb); return 0; } /* Dump TCP stats and summary of all TCBs * &TCB Rcv-Q Snd-Q Local socket Remote socket State * If SOKNAME defined: * 1234 0 0 k5jb.okla.ampr:xxxxxxx n5owk.okla.ampr:xxxxxxx Estab. * Else: * 1234 0 0 44.78.0.2:xxxx 44.78.0.12:xxxx Estab. */ static int tstat() { register int i,j; register struct tcb *tcb; #ifdef SOKNAME char *puname(); #else char *psocket(); #endif printf("conout %u, conin %u, reset out %u, runt %u, chksum err %u, bdcsts %u\n", tcp_stat.conout,tcp_stat.conin,tcp_stat.resets,tcp_stat.runt, tcp_stat.checksum,tcp_stat.bdcsts); printf("TCB# RcvQ SndQ Local socket Remote socket State\n"); j = (int)simple_get(-1); /* fill the table */ for(i=1;i <= j;i++){ tcb = tcbtable[i]; printf("%3d%6u%5u ",i,tcb->rcvcnt,tcb->sndcnt); #ifdef SOKNAME /* note that puname and psocket use a static buffer for return */ printf("%-24s",puname(&tcb->conn.local)); printf("%-24s%-8s%s\n", puname(&tcb->conn.remote), tcpstates[tcb->state], tcb->state == LISTEN && (tcb->flags & CLONE) ? " (S)" : ""); #else printf("%-24s",psocket(&tcb->conn.local)); printf("%-24s%-8s%s\n", psocket(&tcb->conn.remote), tcpstates[tcb->state], tcb->state == LISTEN && (tcb->flags & CLONE) ? " (S)" : ""); #endif } fflush(stdout); return 0; } /* Dump a TCP control block in detail */ static void state_tcp(tcb) struct tcb *tcb; { int32 sent,recvd; #ifdef SOKNAME char *puname(); #else char *psocket(); #endif #ifdef TIMEZOMBIE int32 timenow; int32 time(); #endif if(tcb == NULLTCB) return; /* Compute total data sent and received; take out SYN and FIN */ sent = tcb->snd.una - tcb->iss; /* Acknowledged data only */ recvd = tcb->rcv.nxt - tcb->irs; switch(tcb->state){ case LISTEN: case SYN_SENT: /* Nothing received or acked yet */ sent = recvd = 0; break; case SYN_RECEIVED: recvd--; /* Got SYN, no data acked yet */ sent = 0; break; case ESTABLISHED: /* Got and sent SYN */ case FINWAIT1: /* FIN not acked yet */ sent--; recvd--; break; case FINWAIT2: /* Our SYN and FIN both acked */ sent -= 2; recvd--; break; case CLOSE_WAIT: /* Got SYN and FIN, our FIN not yet acked */ case CLOSING: case LAST_ACK: sent--; recvd -= 2; break; case TIME_WAIT: /* Sent and received SYN/FIN, all acked */ sent -= 2; recvd -= 2; break; } #ifdef SOKNAME printf("Local: %s ",puname(&tcb->conn.local)); printf("Remote: %s", puname(&tcb->conn.remote)); #else printf("Local: %s ",psocket(&tcb->conn.local)); printf("Remote: %s",psocket(&tcb->conn.remote)); #endif printf( " State: %s\n Init seq Unack Next Resent CWind Thrsh Wind MSS Queue Total\n", tcpstates[tcb->state]); printf("Send:%9lx%9lx%9lx%7lu%6u%6u%6u%5u%6u%8lu\n", tcb->iss,tcb->snd.una,tcb->snd.nxt,tcb->resent,tcb->cwind, tcb->ssthresh,tcb->snd.wnd,tcb->mss,tcb->sndcnt,sent); printf("Recv:%9lx%18lx%7lu%18u%11u%8lu\n", tcb->irs,tcb->rcv.nxt,tcb->rerecv,tcb->rcv.wnd,tcb->rcvcnt,recvd); if(tcb->reseq != (struct reseq *)0){ /* K5JB, also below */ register struct reseq *rp; printf("Reassembly queue:\n"); for(rp = tcb->reseq;rp != (struct reseq *)0; rp = rp->next){ printf(" seq x%lx %u bytes\n",rp->seg.seq,rp->length); } } if(tcb->backoff > 0) printf("Backoff %u ",tcb->backoff); if(tcb->flags & RETRAN) printf("Retrying "); switch(tcb->timer.state){ case TIMER_STOP: printf("Timer stopped"); break; case TIMER_RUN: printf("Timer running (%lu/%lu ms)", (int32)MSPTICK * (tcb->timer.start - tcb->timer.count), (int32)MSPTICK * tcb->timer.start); break; case TIMER_EXPIRE: printf("Timer expired"); } #ifdef TIMEZOMBIE time(&timenow); printf("\nSRTT %lu ms. Mean dev %lu ms. Last input %lu min. ago.\n", tcb->srtt,tcb->mdev,(timenow - tcb->lastheard)/60); #else printf("\nSRTT %lu ms Mean dev %lu ms\n",tcb->srtt,tcb->mdev); #endif fflush(stdout); } int32 backoffcap = 1800000L/(int32)MSPTICK; /* initially 30 minutes */ /* tcp timers type - linear v exponential */ static int dotcptimer(argc,argv) int argc ; char *argv[] ; { extern int tcptimertype; int32 bkoff; if (argc < 2) { printf("Tcp timer type is %s. Max backoff is %lu min.\n", tcptimertype ? "linear" : "exponential", #ifdef UNIX (backoffcap * (int32)MSPTICK)/60000L); #else ((backoffcap * (int32)MSPTICK)/60000L) + 1); /* white lie here */ #endif return 0 ; } switch (argv[1][0]) { case 'l': case 's': tcptimertype = 1 ; break ; case 'e': tcptimertype = 0 ; break ; default: printf("use: tcp timertype [linear|exponential] []\n"); return -1 ; } /* interpret arg as minutes and keep it reasonable */ if(argc == 3 && (bkoff = (int32)atol(argv[2])) > 4){ backoffcap = (bkoff * 60000L)/(int32)MSPTICK; if(argv[1][0] == 's') /* a hustler for testing datagram mode */ backoffcap >>= 6; } return 0 ; }