#ifdef TRACE #include #endif #include "machdep.h" #include "timer.h" #include "mbuf.h" #include "netuser.h" #include "internet.h" #include "tcp.h" struct tcb *tcbs[NTCB]; /* Lookup connection, return TCB pointer or NULLTCB if nonexistant */ struct tcb * lookup_tcb(conn) struct connection *conn; { register struct tcb *tcb; int16 hash_tcb(); tcb = tcbs[hash_tcb(conn)]; while(tcb != NULLTCB){ /* Yet another structure compatibility hack */ if(conn->local.address == tcb->conn.local.address && conn->remote.address == tcb->conn.remote.address && conn->local.port == tcb->conn.local.port && conn->remote.port == tcb->conn.remote.port) break; tcb = tcb->next; } return tcb; } /* Create a TCB, return pointer. Return pointer if TCB already exists. */ struct tcb * create_tcb(conn) struct connection *conn; { char *calloc(); register struct tcb *tcb; void tcp_timeout(),tcp_msl(); void link_tcb(); if((tcb = lookup_tcb(conn)) != NULLTCB) return tcb; if((tcb = (struct tcb *)calloc(1,sizeof (struct tcb))) == NULLTCB) return NULLTCB; bcopy((char *)conn,(char *)&tcb->conn,sizeof(struct connection)); tcb->mss = DEF_MSS; tcb->srtt = DEF_RTT * MSPTICK; /* Initialize retransmission timeout */ tcb->timer.start = (BETA * tcb->srtt)/MSPTICK; tcb->timer.func = tcp_timeout; tcb->timer.arg = (int *)tcb; link_tcb(tcb); return tcb; } /* Close our TCB */ void close_self(tcb,reason) register struct tcb *tcb; char reason; { struct reseq *rp,*rp1; stop_timer(&tcb->timer); tcb->reason = reason; /* Flush reassembly queue; nothing more can arrive */ for(rp = tcb->reseq;rp != NULLRESEQ;rp = rp1){ rp1 = rp->next; free_p(rp->bp); free((char *)rp); } tcb->reseq = NULLRESEQ; setstate(tcb,CLOSED); } /* Determine initial sequence number */ #ifdef AMIGA /* * routine called at startup time with inital value of iss for system. This * is probably based on the time or something */ static int32 seq; void setiss(initval) int32 initval; { seq = initval; } #endif int32 iss() { #ifndef AMIGA static int32 seq; #endif seq += 250000; return seq; } /* Sequence number comparisons * Return true if x is between low and high inclusive, * false otherwise */ int seq_within(x,low,high) register int32 x,low,high; { if(low <= high){ if(low <= x && x <= high) return 1; } else { if(low >= x && x >= high) return 1; } return 0; } int seq_lt(x,y) register int32 x,y; { return (long)(x-y) < 0; } int seq_le(x,y) register int32 x,y; { return (long)(x-y) <= 0; } int seq_gt(x,y) register int32 x,y; { return (long)(x-y) > 0; } int seq_ge(x,y) register int32 x,y; { return (long)(x-y) >= 0; } /* Hash a connect structure into the hash chain header array */ static int16 hash_tcb(conn) struct connection *conn; { register int16 hval; /* Compute hash function on connection structure */ hval = hiword(conn->remote.address); hval ^= loword(conn->remote.address); hval ^= hiword(conn->local.address); hval ^= loword(conn->local.address); hval ^= conn->remote.port; hval ^= conn->local.port; hval %= NTCB; return hval; } /* Insert TCB at head of proper hash chain */ void link_tcb(tcb) register struct tcb *tcb; { register struct tcb **tcbhead; int16 hash_tcb(); char i_state; tcb->prev = NULLTCB; i_state = disable(); tcbhead = &tcbs[hash_tcb(&tcb->conn)]; tcb->next = *tcbhead; if(tcb->next != NULLTCB){ tcb->next->prev = tcb; } *tcbhead = tcb; restore(i_state); } /* Remove TCB from whatever hash chain it may be on */ void unlink_tcb(tcb) register struct tcb *tcb; { register struct tcb **tcbhead; int16 hash_tcb(); char i_state; i_state = disable(); tcbhead = &tcbs[hash_tcb(&tcb->conn)]; if(*tcbhead == tcb) *tcbhead = tcb->next; /* We're the first one on the chain */ if(tcb->prev != NULLTCB) tcb->prev->next = tcb->next; if(tcb->next != NULLTCB) tcb->next->prev = tcb->prev; restore(i_state); } void setstate(tcb,newstate) register struct tcb *tcb; register char newstate; { register char oldstate; oldstate = tcb->state; tcb->state = newstate; if(tcb->s_upcall){ (*tcb->s_upcall)(tcb,oldstate,newstate); } /* Notify the user that he can begin sending data */ if(tcb->t_upcall && newstate == ESTABLISHED){ (*tcb->t_upcall)(tcb,tcb->window - tcb->sndcnt); } } #ifdef TRACE /* TCP connection states */ char *tcpstates[] = { "Closed", "Listen", "SYN sent", "SYN received", "Established", "FIN wait 1", "FIN wait 2", "Close wait", "Closing", "Last ACK", "Time wait" }; /* TCP segment header flags */ char *tcpflags[] = { "FIN", /* 0x01 */ "SYN", /* 0x02 */ "RST", /* 0x04 */ "PSH", /* 0x08 */ "ACK", /* 0x10 */ "URG" /* 0x20 */ }; /* TCP closing reasons */ char *reasons[] = { "Normal", "Reset", "Timeout", "ICMP" }; /* Return 1 if arg is a valid TCB, 0 otherwise */ int tcpval(tcb) struct tcb *tcb; { register int i; register struct tcb *tcb1; for(i=0;inext){ if(tcb1 == tcb) return 1; } } return 0; } /* Dump TCP stats and summary of all TCBs /* &TCB Rcv-Q Snd-Q Local socket Remote socket State * 1234 0 0 xxx.xxx.xxx.xxx:xxxxx xxx.xxx.xxx.xxx:xxxxx Established */ int tcpstat() { register int i; register struct tcb *tcb; char *psocket(); printf("conout %u conin %u reset out %u runt %u chksum err %u bdcsts %u\r\n", tcp_stat.conout,tcp_stat.conin,tcp_stat.resets,tcp_stat.runt, tcp_stat.checksum,tcp_stat.bdcsts); #ifdef AMIGA printf("&TCB Rcv-Q Snd-Q Local socket Remote socket State\r\n"); #else printf("&TCB Rcv-Q Snd-Q Local socket Remote socket State\r\n"); #endif for(i=0;inext){ #ifdef AMIGA printf("%6lx%6u%6u ",(unsigned long)tcb, tcb->rcvcnt,tcb->sndcnt); #else printf("%4x%6u%6u ",(int)tcb,tcb->rcvcnt,tcb->sndcnt); #endif printf("%-23s",psocket(&tcb->conn.local)); printf("%-23s",psocket(&tcb->conn.remote)); printf("%-s\r\n",tcpstates[tcb->state]); } } fflush(stdout); return 0; } /* Dump a TCP control block */ void state_tcp(tcb) struct tcb *tcb; { int32 sent,recvd; 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; } printf("Local: %s",psocket(&tcb->conn.local)); printf(" Remote: %s",psocket(&tcb->conn.remote)); printf(" State: %s\r\n",tcpstates[tcb->state]); printf(" Init seq Unack Next WL1 WL2 Wind MSS Queue Total\r\n"); printf("Send:"); printf("%9lx",tcb->iss); printf("%9lx",tcb->snd.una); printf("%9lx",tcb->snd.nxt); printf("%9lx",tcb->snd.wl1); printf("%9lx",tcb->snd.wl2); printf("%6u",tcb->snd.wnd); printf("%6u",tcb->mss); printf("%6u",tcb->sndcnt); printf("%11lu\r\n",sent); printf("Recv:"); printf("%9lx",tcb->irs); printf(" "); printf("%9lx",tcb->rcv.nxt); printf(" "); printf(" "); printf("%6u",tcb->rcv.wnd); printf(" "); printf("%6u",tcb->rcvcnt); printf("%11lu\r\n",recvd); if(tcb->reseq != (struct reseq *)NULL){ register struct reseq *rp; printf("Reassembly queue:\r\n"); for(rp = tcb->reseq;rp != (struct reseq *)NULL; rp = rp->next){ printf(" seq x%lx %u bytes\r\n",rp->seg.seq,rp->length); } } printf("Retry %u",tcb->retry); switch(tcb->timer.state){ case TIMER_STOP: printf(" Timer stopped"); break; case TIMER_RUN: printf(" Timer running (%ld/%ld mS)", (long)MSPTICK * (tcb->timer.start - tcb->timer.count), (long)MSPTICK * tcb->timer.start); break; case TIMER_EXPIRE: printf(" Timer expired"); } printf(" Smoothed round trip time %ld mS\r\n",tcb->srtt); fflush(stdout); } /* Dump a TCP segment header. Assumed to be in network byte order */ void tcp_dump(bp,source,dest,check) struct mbuf *bp; int32 source,dest; /* IP source and dest addresses */ int check; /* 0 if checksum test is to be bypassed */ { int hdr_len,i; register struct tcp_header *tcph; struct pseudo_header ph; char tmpbuf; if(bp == NULLBUF) return; /* If packet isn't in a single buffer, make a temporary copy and * note the fact so we free it later */ if(bp->next != NULLBUF){ bp = copy_p(bp,len_mbuf(bp)); tmpbuf = 1; } else tmpbuf = 0; tcph = (struct tcp_header *)bp->data; hdr_len = hinibble(tcph->offset) * sizeof(int32); printf("TCP: %u->%u Seq x%lx", ntohs(tcph->source),ntohs(tcph->dest), ntohl(tcph->seq),ntohl(tcph->ack)); if(tcph->flags & ACK) printf(" Ack x%lx",ntohl(tcph->ack)); for(i=0;i<6;i++){ if(tcph->flags & 1 << i){ printf(" %s",tcpflags[i]); } } printf(" Wnd %u",ntohs(tcph->wnd)); if(tcph->flags & URG) printf(" UP x%x",ntohs(tcph->up)); if(hdr_len > sizeof(struct tcp_header)){ struct mss *mssp; mssp = (struct mss *)(tcph + 1); if(mssp->kind == MSS_KIND && mssp->length == MSS_LENGTH){ printf(" MSS %u",ntohs(mssp->mss)); } } /* Verify checksum */ if(check){ ph.source = source; ph.dest = dest; ph.protocol = TCP_PTCL; ph.length = len_mbuf(bp); ph.zero = 0; if((i = cksum(&ph,bp,ph.length)) != 0) printf(" CHECKSUM ERROR (%u)",i); } printf("\r\n"); if(tmpbuf) free_p(bp); } #endif