# # include "tcpstru.h" /* Written by: Michael A. Wingfield Bolt Beranek & Newman Cambridge, MA 02138 */ /* data declarations */ struct PortLdr PortHdr; char CmdUsrName[] CMDUSRNM; /* user cmd port name */ char CmdTcpName[] CMDTCPNM; /* tcp cmd port name */ char SendName[] SENDNM; /* send data port name */ char RecvName[] RECVNM; /* recv data port name */ char CmdTcp[CMDTCPSIZE]; /* tcp command out buffer */ char CmdUsr[CMDUSRSIZE]; /* user command out buffer */ struct WorkBlk WorkAvl[WORKSIZE], *WrkAvlPtr, *WorkAvail, *WorkPtr; struct WorkBlk WorkEnds[2], *WorkHead, *WorkTail; struct FdsBlk FdsBffer[FDSSIZE]; /* index relating TCB to port */ struct TcpTcb TCBList[NUMBTCBS], /* the TCBs */ *TcbAvl, *RetranHd, *TcbHd; int PortMap[PORTCNT]; /* bit map for selecting local port */ char TcpOut[TCPOTSZ]; /* tcp output buffer */ char TcpIn[TCPINSZ]; /* tcp input buffer */ char FlkBuff[TCPOTSZ]; /* buffer for flaky gateway/2 */ /* the next two tables used to simulate Stop/Resume */ long StopSkt[NSTOP]; /* foreign skts from which to discard segs */ int StopTcc[NSTOP]; /* list of tcc from which to discard segs */ int NetFdsR; /* fds for net in traffic */ int NetFdsW; /* fds for net out traffic */ int EarFds; /* fds for ear port */ int PartWr; /* 1 => part of segment needs to be written to net */ char *OutPtr; /* ptr to next byte to send */ int OutCnt; /* count of remaining bytes */ int *TimePtr; /* ptr to relative time */ int RTime; /* relative time for flaky netout */ int FlkFlag; /* 1 => dup or out of order seg in progress */ int SegNum; /* segment identification number */ int ShortIn; /* 1 => PsipIn: segment in TcpIn buffer */ int ShortCkt; /* 1 => local traffic not sent to net */ int ANLngth; /* Arpanet leader size (2 or 6 only ) */ int Debug; /* 1 => turn on debug output */ int NoRetran; /* 1 => disable retransmissions */ int SegData; /* max amount of data in segment */ int RWindow; /* maximum receive window size */ int WindNum; /* numerator of window size calc */ int WindDenom; /* denominator of window size calc */ int LocHost; /* local host number */ int LocImp; /* local imp number */ int Auth; /* 1 => do authorization */ int LocNet; /* the local net number */ int InLink; /* the internet link number */ int DoChkSum; /* 1 => checksum the segments */ int NetTable[NUMNET]; /* map between dest net and gateway */ int RetFast; /* time between fast retransmissions */ int RetSlow; /* time between slow retransmissions */ int RetCount; /* number of retransmissions before slow mode */ /* parameters for the flaky gateway/2 */ char NumLost; /* % of segments discarded */ char NumBad; /* % of segments smashed */ char NumDup; /* % of segments duplicated */ char TimeDup; /* time until duped segment is sent */ char NumReOrd; /* % of segments reordered */ char TimeReOrd; /* time until saved segment is sent */ /* The main program */ main(argc, argv) /* arguments (if present): 1. Debug parameter : values 1 to 4 - see below 2. ShortCkt parameter : 1 => allow local traffic to be shorted 3. Auth parameter : 1 => authorize the user 4. DoChkSum parameter : 1 => checksum the segments 5. NoRetran parameter : 1 => no retransmissions today 6. SegData parameter : maximum data bytes in segment 7. RWindow parameter : maximum receive window size 8. WindNum parameter : numerator - window size 9. WindDenom parameter : denominator - window size 10. RetFast parameter : retransmission time - fast 11. RetSlow parameter : retransmission time - slow 12. RetCount parameter : max retransmissions before slow mode Debug: 1 => turn on all debugging including printfs 2 => only net out log 3 => only net in log 4 => net out and net in log */ int argc; char *argv[]; { extern sg1, sg2, sg3, sg8, sg13; int WorkType; struct TcpTcb *TempPtr, *XPtr, *NxtPtr, *SavePtr; char AwaitBuffer[AWTLENGTH]; char *AwaitPtr; struct FdsBlk *FdsBffPtr; int Error; int Err; int TimeOut; int i; int tvec[2]; char *ctime(); int TcbTm; int Stop; int TimeSw; int Time1; int Time2; int **ITimePtr; int OldTime; struct PMask1 *LPtr; int n; int SaveFds; int Host; struct { int ZMode; /* 0 => read, 1 => write, 2 => read/write */ long ZHost; /* 0 => any host */ int ZLoMsg; /* low end of msg id range */ int ZHiMsg; /* high end: 0 => set to ZLoMsg */ } RawParams; Debug = 0; /* default: no trace */ ShortCkt = 1; /* default: allow local echoing */ Auth = 0; /* default: don't authorize */ DoChkSum = 0; /* default: don't compute checksums */ NoRetran = 0; /* default: retransmissions allowed */ SegData = 600; /* default: data in TCP segment */ RWindow = 1000; /* default: maximum receive window size */ WindNum = 1; /* default: numerator of window calculation */ WindDenom = 1; /* default: denominator of window calculation */ RetFast = 2; /* default: seconds */ RetSlow = 5; /* default: seconds */ RetCount = 6; /* default: number of retransmissions */ /* process the arguments */ for (i = 1; i < argc; i++) { switch(i) { case 1: { Debug = atoi(argv[i]); break; } case 2: { ShortCkt = atoi(argv[i]); break; } case 3: { Auth = atoi(argv[i]); break; } case 4: { DoChkSum = atoi(argv[i]); break; } case 5: { NoRetran = atoi(argv[i]); break; } case 6: { SegData = atoi(argv[i]); break; } case 7: { RWindow = atoi(argv[i]); break; } case 8: { WindNum = atoi(argv[i]); break; } case 9: { WindDenom = atoi(argv[i]); break; } case 10: { RetFast = atoi(argv[i]); break; } case 11: { RetSlow = atoi(argv[i]); break; } case 12: { RetCount = atoi(argv[i]); break; } default: { printf("too many args\n"); } } } time(tvec); /* print the time tcp started */ printf("%s", ctime(tvec)); printf("Startup Parameters\n"); printf("Debug: %d\n", Debug); printf("ShortCkt: %d\n", ShortCkt); printf("Auth: %d\n", Auth); printf("DoChkSum: %d\n", DoChkSum); printf("NoRetran: %d\n", NoRetran); printf("SegData: %d\n", SegData); printf("RWindow: %d\n", RWindow); printf("WindNum: %d\n", WindNum); printf("WindDenom: %d\n", WindDenom); printf("RetFast: %d\n", RetFast); printf("RetSlow: %d\n", RetSlow); printf("RetCount: %d\n", RetCount); /* get the "tcptable" contents */ /* the tcptable essentially tells tcp where in the internet it is executing and how it can talk to hosts on other networks. See ReadTbl for table format. */ if (ReadTbl() == -1) { printf("bad tcptable entry\n"); exit(); } if ((ANLngth != 2)&&(ANLngth != 6)) { printf("bad leader length parameter - (2 or 6 only)\n"); exit(); /* die */ } /* set up the signal routines */ if ((signal(1,&sg1) == -1)|| (signal(2,&sg2) == -1)|| (signal(3,&sg3) == -1)|| (signal(8,&sg8) == -1)|| (signal(13,&sg13) == -1)) { printf("bad signal call\n"); exit(); } /* close unused file descriptors */ close(0); /* standard input */ close(2); /* error output */ /* initialize the work avail list */ for (WrkAvlPtr = WorkAvl;WrkAvlPtr < &WorkAvl[WORKSIZE-1];) { WorkPtr = WrkAvlPtr; WorkPtr -> NextWork = ++WrkAvlPtr; } WrkAvlPtr -> NextWork = 0; /* null pointer */ WorkAvail = WorkAvl; /* inz the two ends of the work queue */ WorkHead = &WorkEnds[0]; WorkTail = &WorkEnds[1]; WorkHead -> NextWork = WorkTail; WorkHead -> LastWork = 0; WorkTail -> LastWork = WorkHead; WorkTail -> NextWork = 0; SegNum = 0; PartWr = 0; RetranHd = 0; FlkFlag = 0; ShortIn = 0; /* clear the FdsBffer which relate Ports to TCBs */ for (i = 0; i < FDSSIZE; i++) { FdsBffer[i].BuffFlags = 0; FdsBffer[i].BuffFds = 0; FdsBffer[i].BuffTcbPtr = 0; } /* set up TCB avail list */ for (i = 0; i < (NUMBTCBS-1); i++) { TCBList[i].NxtActTcb = &TCBList[i+1]; } TCBList[i].NxtActTcb = 0; TcbAvl = TCBList; TcbHd = 0; /* clear the bit map for selecting local ports */ for (i = 0; i < PORTCNT; i++) { PortMap[i] = 0; } /* clear the tables used for Stop/Resume */ for (i = 0;i < NSTOP; i++) { StopSkt[i] = 0; StopTcc[i] = 0; } /* create the "ear" port */ unlink (EAR); EarFds = port (EAR,0170222,0); if (EarFds == -1) { /* couldn't open ear port */ printf("Tcp: couldn't open ear port\n"); } else if (EarFds > (FDSSIZE-1)) { /* too big */ printf("Tcp: fds1 too big\n"); } else { /* set up corresponding entry to FdsBffer */ FdsBffPtr = &FdsBffer[EarFds]; FdsBffPtr -> BuffFlags = EARIN; FdsBffPtr -> BuffFds = EarFds; FdsBffPtr -> BuffTcbPtr = 0; awtenb(EarFds); } /* open connection to raw message facility */ RawParams.ZMode = 0; /* read */ RawParams.ZHost = 0; /* any host */ RawParams.ZLoMsg = InLink << 4; /* set link field */ RawParams.ZHiMsg = 0; /* set to ZLoMsg */ NetFdsR = open("/dev/net/rmi", &RawParams); if (NetFdsR == -1) { /* can't open read connection to raw msg */ printf("can't connect to ncp - read\n"); exit(); /* halt */ } else if (NetFdsR > (FDSSIZE-1)) { /* too big */ printf("Tcp: fds2 too big\n"); exit(); /* halt */ } else { /* set up corresponding entry in FdsBffer */ FdsBffPtr = &FdsBffer[NetFdsR]; FdsBffPtr -> BuffFlags = NETINHP; FdsBffPtr -> BuffFds = NetFdsR; FdsBffPtr -> BuffTcbPtr = 0; awtenb(NetFdsR); /* enable the file descriptor */ } RawParams.ZMode = 1; /* write */ NetFdsW = open("/dev/net/rmi", &RawParams); if (NetFdsW == -1) { /* can't open write connection to raw msg */ printf("can't connect to ncp - write\n"); exit(); /* halt */ } else if (NetFdsW > (FDSSIZE-1)) { /* too big */ printf("Tcp: fds20 too big\n"); exit(); /* halt */ } else { /* set up corresponding entry in FdsBffer */ FdsBffPtr = &FdsBffer[NetFdsW]; FdsBffPtr -> BuffFlags = NETOUTHP; FdsBffPtr -> BuffFds = NetFdsW; FdsBffPtr -> BuffTcbPtr = 0; awtenb(NetFdsW); /* enable the file descriptor */ } if (Debug > 0) /* don't waste fds unless debugging */ { Error = LogInit("net.log"); if (Error == -1) { printf("error in LogInit\n"); } } TimeSw = 1; Time2 = 0; ITimePtr = &TimePtr; TimePtr = &Time2; OldTime = 0; itime(ITimePtr); while(1) /* infinite loop */ { /* process the work-to-do list */ WorkPtr = WorkHead -> NextWork; while ((WorkPtr -> NextWork) != 0) { /* process the quantum of work */ WorkType = (WorkPtr -> PortFlags)&037; switch (WorkType) { case EARIN: /* a message to the "ear" */ { Error = EarIn(WorkPtr); break; } case USRCMD: { Error = CmdIn(WorkPtr); break; } case NETOUTHP: { Error = PsipOut(WorkPtr); break; } case NETINHP: { Error = PsipIn(WorkPtr); break; } case USERINHP: { Error = DataIn(WorkPtr); break; } default: { /* something bad */ printf("Sched: bad opcode: %d\n",WorkType); Error = 0; } } switch(Error) { /* determine what to do with the work block */ case 0: /* remove the work entry */ { TempPtr = WorkPtr -> NextWork; (WorkPtr -> NextWork) -> LastWork = WorkPtr -> LastWork; (WorkPtr -> LastWork) -> NextWork = WorkPtr -> NextWork; WorkPtr -> NextWork = WorkAvail; WorkAvail = WorkPtr; break; } case 1: /* do not remove the work block */ { TempPtr = WorkPtr -> NextWork; break; } case 2: /* move work block to end of queue */ { TempPtr = WorkPtr -> LastWork; MakeWrk(0, WorkPtr -> PortFlags, WorkPtr -> TCBPtr, WorkPtr -> PortFds); TempPtr = TempPtr -> NextWork; break; } case 3: /* connection is to be aborted */ { /* move WorkPtr to next block not associated with this TCB */ XPtr = WorkPtr -> TCBPtr; while((WorkPtr -> NextWork != 0)&&(WorkPtr -> TCBPtr == XPtr)) { WorkPtr = WorkPtr -> NextWork; } /* remove work blocks, etc assoc with XPtr */ Abort(XPtr, 0); TempPtr = WorkPtr; break; } case 4: /* abort all connections associated with user */ { SaveFds = WorkPtr -> PortFds; /* delete work block entry */ TempPtr = WorkPtr -> NextWork; (WorkPtr -> NextWork) -> LastWork = WorkPtr -> LastWork; (WorkPtr -> LastWork) -> NextWork = WorkPtr -> NextWork; WorkPtr -> NextWork = WorkAvail; WorkAvail = WorkPtr; /* find associated TCBs */ XPtr = TcbHd; while(XPtr != 0) { SavePtr = XPtr -> NxtActTcb; if (XPtr -> UserCmdPort == SaveFds) { /* found an associated TCB */ switch(XPtr -> State) { case CLOSED: case OPEN: case SYNSENT: { /* clean up and close out the connection */ /* move TempPtr to next block not associated with this TCB */ while((TempPtr -> NextWork != 0)&&(TempPtr -> TCBPtr == XPtr)) { TempPtr = TempPtr -> NextWork; } Abort(XPtr, 1); break; } default: { /* every other connection state */ /* set the RST flag and let PsipOut finish the abortion */ XPtr -> TCPFlags =| RST + ACK; XPtr -> Flags =| 020; /* abort connection */ MakeWrk(0, NETOUTHP, XPtr, NetFdsW); break; } } } XPtr = SavePtr; } break; } default: { /* this looks bad */ TempPtr = WorkPtr -> NextWork; printf("Sched: bad work return: %d\n",Error); } } WorkPtr = TempPtr; } if (FlkFlag == 0) { RTime = 1000; /* seconds */ } if (RetranHd != 0) { if ((RetranHd -> RetranTm - *TimePtr) < (RTime - *TimePtr)) { TimeOut = RetranHd -> RetranTm - *TimePtr; } else { TimeOut = RTime - *TimePtr; } } else { TimeOut = RTime - *TimePtr; } if (TimeOut < 0) { TimeOut = 0; } if (Debug == 1) { printf("Sched: TimeOut = %d\n",TimeOut); } /* wait for some port activity */ await(TimeOut, AwaitBuffer, AWTLENGTH); if (FlkFlag == 1) { RTime =- *TimePtr; if (RTime <= 0) { /* schedule sending of dup or out of order segment */ MakeWrk(0, NETOUTHP, &FlkFlag, NetFdsW); } } n = 0; AwaitPtr = AwaitBuffer; /* search the await buffer, putting items on work list */ while (*AwaitPtr != -1) { FdsBffPtr = &FdsBffer[*AwaitPtr]; /* put file descriptor blocks in work queue */ if (WorkAvail == 0) /* test free list */ { /* no more blocks */ printf("Sched: no more work blocks\n"); } else { /* put work block in work queue */ switch((FdsBffPtr -> BuffFlags)&037) { case USEROUTHP: { /* no entry in work queue */ break; } case TCPCMD: { /* no entry in work queue */ break; } case NETOUTHP: { /* no entry in work queue */ break; } case 0: { /* the FdsBffer entry was deleted! */ break; } case USERINHP: { /* fall through */ } default: { /* put entry in work queue */ MakeWrk(0, FdsBffPtr -> BuffFlags, FdsBffPtr -> BuffTcbPtr, FdsBffPtr -> BuffFds); } } } AwaitPtr++; n++; } if (n == 0) { /* printf("empty wakeup\n"); */ } /* switch time counts */ if (TimeSw == 0) { Time2 = 0; TimePtr = &Time2; OldTime = Time1; TimeSw = 1; } else { Time1 = 0; TimePtr = &Time1; OldTime = Time2; TimeSw = 0; } if (RetranHd != 0) { /* schedule retransmissions based on OldTime */ Stop = 0; XPtr = RetranHd; TcbTm = -OldTime; n = 0; do { XPtr -> RetranTm =+ TcbTm; if (XPtr -> RetranTm <= 0) { /* schedule the TCB for retransmission */ /* put ptrs back to last acked byte */ XPtr -> SendSeq = XPtr -> LeftSeq; XPtr -> DataNxt = XPtr -> DataHd; if (Debug == 1) { printf("Sched: retran\n"); } if (Debug == 4) { Log(5, "si", "retran", XPtr, -1); } /* indicate that a retransmission is being scheduled */ XPtr -> Flags =| XRETRANOK; XPtr -> NumRetran =+ 1; XPtr -> RetranSeg =+ 1; if (XPtr -> NumRetran == RetCount) { /* foreign process not responding */ /* send notice to user */ LPtr = CmdTcp; LPtr -> YOpCode = OPROCNR; LPtr -> YHandle = XPtr -> ConnHandle; write(XPtr -> TCPCmdPort, CmdTcp, 4); XPtr -> Flags =| 4; /* set retransmission mode flag */ } NxtPtr = XPtr -> NxtRetran; /* remove TCB from retransmission queue */ UnQTcb(XPtr); if (RetranHd == 0) { /* UnQTcb removed the only entry */ Stop = 1; } /* make work for PsipOut */ MakeWrk(0, NETOUTHP, XPtr, NetFdsW); TcbTm = XPtr -> RetranTm; XPtr -> RetranTm = 0; XPtr = NxtPtr; } else { Stop = 1; } n++; } while((Stop == 0)&&(XPtr != RetranHd)&&(n < NUMBTCBS)); } } } /* */ /* subroutine which acts on messages to the "ear" */ EarIn(WrkPtr) struct WorkBlk *WrkPtr; { extern char NumLost; extern char NumBad; extern char NumDup; extern char TimeDup; extern char NumReOrd; extern char TimeReOrd; int UserPID; int i; int FDesc; int Cap[2]; int UsrFds; struct PMask1 *AckPtr; int xx; struct FdsBlk *FdsBffPtr; struct PMask1 *InitPtr; if (Debug == 1) { printf("EarIn: entered\n"); } capac(WrkPtr -> PortFds,Cap,4); if (Cap[0] == 0) { /* no real work here */ return(0); } do { /* flush zero length messages */ /* these occur when processes close the ear port */ read(WrkPtr -> PortFds, &PortHdr, HDRLNGTH); /* get message header */ Cap[0] =- HDRLNGTH; } while (((PortHdr.DCount&~0100000) == 0)&&(Cap[0] >= HDRLNGTH)); if (Cap[0] == 0) { /* no real messages here */ return(0); } /* get rest of message */ read(WrkPtr -> PortFds, CmdUsr, PortHdr.DCount&~0100000); InitPtr = CmdUsr; switch(InitPtr -> YOpCode) { case OINIT: { /* create names of command in, command out ports */ UserPID = PortHdr.UsrPID; for (i = PRTPREFIX+2; i > PRTPREFIX-1; i--) { CmdUsrName[i] = '0' + (UserPID&07); CmdTcpName[i] = '0' + (UserPID&07); UserPID =>> 3; } unlink(CmdUsrName); UsrFds = port(CmdUsrName,0170222,0); /* create user command port */ if (UsrFds == -1) { /* couldn't create user command port */ printf("EarIn: couldn't create user command port\n"); return(0); } FDesc = open(CmdTcpName,1); /* open the tcp command port */ if (FDesc == -1) { /* ignore the clown */ printf("EarIn: couldn't open tcp command port\n"); close(UsrFds); return(0); } unlink(CmdTcpName); if (UsrFds > (FDSSIZE-1)) { /* too big */ printf("EarIn: fds3 too big\n"); } else { /* enter file descriptors in FdsBffer */ FdsBffPtr = &FdsBffer[UsrFds]; FdsBffPtr -> BuffTcbPtr = FDesc; /* saved for Open response */ FdsBffPtr -> BuffFlags = USRCMD; FdsBffPtr -> BuffFds = UsrFds; awtenb(UsrFds); } if (FDesc > (FDSSIZE-1)) { /* too big */ printf("EarIn: fds4 too big\n"); } else { FdsBffPtr = &FdsBffer[FDesc]; FdsBffPtr -> BuffTcbPtr = 0; FdsBffPtr -> BuffFlags = TCPCMD; FdsBffPtr -> BuffFds = FDesc; awtenb(FDesc); } /* send back the acknowledge message */ AckPtr = CmdTcp; AckPtr -> YOpCode = OACK; /* acknowledge */ AckPtr -> YHandle = 0; xx = write(FDesc,CmdTcp,4); break; } case OSTAT: { /* special command to print status */ Status(); break; } case OFLAKY: { /* new parameters for flaky gateway/2 */ NumLost = CmdUsr[2]; NumBad = CmdUsr[3]; NumDup = CmdUsr[4]; TimeDup = CmdUsr[5]; NumReOrd = CmdUsr[6]; TimeReOrd = CmdUsr[7]; printf("NL: %d NB: %d ND: %d TD: %d NR: %d TR: %d\n", CmdUsr[2], CmdUsr[3], CmdUsr[4], CmdUsr[5], CmdUsr[6], CmdUsr[7]); break; } default: { printf("EarIn: some bad opcode %d\n", InitPtr -> YOpCode); break; } } Cap[0] =- PortHdr.DCount&~0100000; if (Cap[0] > 0) { /* more ear commands */ return(2); } return(0); } /* */ /* Module which handles commands sent from user to TCP */ CmdIn(WrkPtr) struct WorkBlk *WrkPtr; { register struct TcpTcb *XPtr, *Handle, *TPtr; int i; int x; int j; int k; int T; int Found; int Stop; int SndFds; int RcvFds; int Error; int Cap[2]; int UserPID; int R1; int UserFds; int ParamVal[20]; struct WorkBlk *WPtr; struct PMask1 *ChngPtr; struct PMask1 *AckPtr; struct PMask1 *MovePtr; struct PMask1 *ErrPtr; struct MOpen *OpenPtr; struct MUrgent *UrgPtr; struct MClose *ClosePtr; struct PMask1 *AbortPtr; struct FdsBlk *FdsBffPtr; struct MUpdate *UPtr; struct MStatus *SPtr; if (Debug == 1) { printf("CmdIn: entered\n"); } /* get port capacity */ capac(WrkPtr -> PortFds, Cap, 4); while(1) /* exit by return */ { if (Cap[0] == 0) { /* no more commands */ return(0); } if (Cap[0] == HDRLNGTH) { /* user closed his command port */ UserFds = WrkPtr -> TCBPtr; awtdis(WrkPtr -> PortFds); awtdis(UserFds); close(WrkPtr -> PortFds); close(UserFds); FdsBffer[WrkPtr -> PortFds].BuffFlags = 0; FdsBffer[UserFds].BuffFlags = 0; return(4); /* abort all connections for this process */ } if (Cap[0] < HDRLNGTH) { /* can't happen!? */ printf("CmdIn: huh?\n"); return(4); /* abort all connections */ } read(WrkPtr -> PortFds, &PortHdr, HDRLNGTH); /* get msg header */ /* read in the text */ read(WrkPtr -> PortFds, CmdUsr, (PortHdr.DCount&~0100000)); Cap[0] =- HDRLNGTH + PortHdr.DCount&~0100000; OpenPtr = CmdUsr; switch (OpenPtr -> OpCode) { case OOPEN: { /* find an unused TCB */ if (TcbAvl == 0) { /* preempt TCB if authorization bit on */ if (Auth == 1) { /* try to find a non-CAT-I TCB to steal */ T = -1; XPtr = TcbHd; /* find the lowest precedence TCB */ while(XPtr != 0) { if ((XPtr -> SndPrec > T)&&(XPtr -> SndPrec < 12)) { /* CAT-I traffic has precedences of 12-15 */ T = XPtr -> SndPrec; TPtr = XPtr; } } if (T == -1) { /* no TCB available */ ErrPtr = CmdTcp; ErrPtr -> YOpCode = OERR; ErrPtr -> YMisc = ESYSC; write(WrkPtr -> TCBPtr, CmdTcp, 6); break; } /* abort connection of this TCB */ CmdOut(TPtr, ORESET, 0, 0); CmdOut(TPtr, OCHNG, CLOSED, 0); Abort(TPtr, 1); } else { /* no TCB available */ ErrPtr = CmdTcp; ErrPtr -> YOpCode = OERR; ErrPtr -> YMisc = ESYSC; write(WrkPtr -> TCBPtr, CmdTcp, 6); break; } } /* got a TCB */ Handle = TcbAvl; TcbAvl = TcbAvl -> NxtActTcb; /* link it on the active list */ Handle -> NxtActTcb = TcbHd; TcbHd = Handle; /* set up TCB for possible error handling */ Handle -> ConnHandle = OpenPtr -> OHandle; Handle -> TCPCmdPort = WrkPtr -> TCBPtr; if (Auth == 1) { /* verify that user can do an Open */ Error = Authorize(PortHdr.RealUserID, OpenPtr, Handle); if (Error != 0) { /* user is not authorized in some way */ CmdOut(Handle, OERR, Error, 0); /* return the TCB */ TcbHd = Handle -> NxtActTcb; Handle -> NxtActTcb = TcbAvl; TcbAvl = Handle; break; } } /* check validity of local TCP port number */ if (OpenPtr -> SrcPrt == 0) { /* create a unique local port number */ i = 0; /* row index */ Stop = 0; while ((i < PORTCNT)&&(Stop == 0)) { k = 1; j = 0; /* column index */ while ((j < 16)&&(Stop == 0)) { if ((PortMap[i]&k) == 0) { Stop = 1; } else { k =<< 1; j++; } } if (Stop == 0) { i++; } } if (Stop == 0) { /* no unused entries */ CmdOut(Handle, OERR, ESYSC, 0); /* return the TCB */ TcbHd = Handle -> NxtActTcb; Handle -> NxtActTcb = TcbAvl; TcbAvl = Handle; break; } PortMap[i] =| k; /* set the used bit */ /* make up the port number */ OpenPtr -> SrcPrt = BASEPORT + (i << 4) + j; } else if (OpenPtr -> SrcPrt < 0400) { /* check authorization to use first 256 ports */ if (Auth == 1) { k = GetFields(PortHdr.RealUserID, &ParamVal); if ((k == -1)||(ParamVal[0] != 1)) { /* unauthorized use of these ports */ CmdOut(Handle, OERR, EUNAUTH, 0); /* return the TCB */ TcbHd = Handle -> NxtActTcb; Handle -> NxtActTcb = TcbAvl; TcbAvl = Handle; break; } } } else if ((OpenPtr -> SrcPrt >= BASEPORT)&& (OpenPtr -> SrcPrt < BASEPORT + 0400)) { /* ports reserved for tcp use */ CmdOut(Handle, OERR, EBADPARAM, 0); /* return the TCB */ TcbHd = Handle -> NxtActTcb; Handle -> NxtActTcb = TcbAvl; TcbAvl = Handle; break; } /* check for Open of connection mode */ if (OpenPtr -> OpenMode == 0) { /* check for fully specified foreign socket */ if ((OpenPtr -> Net == 0)||((OpenPtr -> HostH == 0)&& (OpenPtr -> HostL == 0))||(OpenPtr -> DstPrt == 0)) { /* parameter unspecified: return error */ CmdOut(Handle, OERR, EUNSPEC, 0); /* return the TCB */ TcbHd = Handle -> NxtActTcb; Handle -> NxtActTcb = TcbAvl; TcbAvl = Handle; break; } } /* derive the data send and receive ports */ UserPID = PortHdr.UsrPID; for (i = PRTPREFIX+2; i > PRTPREFIX-1; i--) { SendName[i] = '0' + (UserPID&07); RecvName[i] = '0' + (UserPID&07); UserPID =>> 3; } j = OpenPtr -> OHandle; for (i = PRTPREFIX+5; i > PRTPREFIX+2; i--) { SendName[i] = '0' + (j&07); RecvName[i] = '0' + (j&07); j =>> 3; } /* create the send data port */ unlink(SendName); SndFds = port(SendName,0170222,0); if (SndFds == -1) { /* couldn't create send data port */ /* send error message */ CmdOut(Handle, OERR, ESNDPRT, 0); /* return the TCB */ TcbHd = Handle -> NxtActTcb; Handle -> NxtActTcb = TcbAvl; TcbAvl = Handle; break; } /* open the receive data port */ RcvFds = open(RecvName,1); if (RcvFds == -1) { /* couldn't open recv data port */ CmdOut(Handle, OERR, ERCVPRT, 0); /* return the TCB */ TcbHd = Handle -> NxtActTcb; Handle -> NxtActTcb = TcbAvl; TcbAvl = Handle; close(SndFds); break; } unlink(RecvName); /* fill in the relevant TCB parameters */ Handle -> State = OPEN; Handle -> Flags = 0; Handle -> TMode = OpenPtr -> OpenMode; Handle -> ProcID = PortHdr.UsrPID; Handle -> UserCmdPort = WrkPtr -> PortFds; Handle -> SendPort = SndFds; Handle -> RecvPort = RcvFds; Handle -> DataHd = 0; Handle -> DataNxt = 0; Handle -> DataTl = 0; Handle -> VerHdr = (INLNGTH/4)|(INVERNO << 4); Handle -> TCPFlags = (TCPLNGTH/4) << 12; Handle -> DataState = 0; /* select an initial sequence number */ /* say, zero! */ z = &(Handle -> SndISN); z -> bb[0] = 0; z -> bb[1] = 0; Handle -> LeftSeq = Handle -> SndISN; Handle -> SendSeq = Handle -> SndISN; Handle -> MaxSend = Handle -> SndISN; Handle -> SndFSN = 0; Handle -> OpnNet = OpenPtr -> Net; Handle -> OpnHstH = OpenPtr -> HostH; Handle -> OpnHstL = OpenPtr -> HostL; Handle -> OpnPort = OpenPtr -> DstPrt; Handle -> DstNet = OpenPtr -> Net; Handle -> DstHstH = OpenPtr -> HostH; Handle -> DstHstL = OpenPtr -> HostL; Handle -> DestPort = OpenPtr -> DstPrt; Handle -> SrcePort = OpenPtr -> SrcPrt; Handle -> SrcNet = LocNet; Handle -> SrcHstH = LocHost; Handle -> SrcHstL = LocImp; Handle -> Window = (PORTSIZE*WindNum)/WindDenom; if (Handle -> Window > RWindow) { Handle -> Window = RWindow; } Handle -> SndWindow = 1; /* allow one byte */ Handle -> NumRetran = 0; Handle -> NxtRetran = 0; Handle -> TimeToLive = 10; /* 10 seconds...? */ Handle -> Protocol = TCPVERNO; Handle -> Options[0] = OPTSPT; Handle -> Options[1] = 4; Handle -> SegRecv = 0; Handle -> DupRecv = 0; Handle -> BusyRecv = 0; Handle -> RetranSeg = 0; /* put user send and receive data ports in FdsBffer */ if (SndFds > (FDSSIZE-1)) { /* too big */ printf("CmdIn: fds5 too big\n"); } else { FdsBffPtr = &FdsBffer[SndFds]; FdsBffPtr -> BuffFlags = USERINHP; FdsBffPtr -> BuffFds = SndFds; FdsBffPtr -> BuffTcbPtr = Handle; x = awtenb(SndFds); } if (RcvFds > (FDSSIZE-1)) { /* too big */ printf("CmdIn: fds6 too big\n"); } else { FdsBffPtr = &FdsBffer[RcvFds]; FdsBffPtr -> BuffFlags = USEROUTHP; FdsBffPtr -> BuffFds = RcvFds; FdsBffPtr -> BuffTcbPtr = Handle; awtenb(RcvFds); } if ((OpenPtr -> OpenMode) == 0) { /* user wants to Open a connection */ /* check that socket pair is unique */ Found = 0; XPtr = TcbHd; while(XPtr != 0) { if (XPtr != Handle) { if ((XPtr -> SrcePort == OpenPtr -> SrcPrt)&& (XPtr -> DestPort == OpenPtr -> DstPrt)&& (XPtr -> DstNet == OpenPtr -> Net)&& (XPtr -> DstHstH == OpenPtr -> HostH)&& (XPtr -> DstHstL == OpenPtr -> HostL)) { /* socket pair duplication */ CmdOut(Handle, OERR, EBADPARAM, 0); /* return the TCB */ TcbHd = Handle -> NxtActTcb; Handle -> NxtActTcb = TcbAvl; TcbAvl = Handle; close(SndFds); close(RcvFds); /* remove entries from FdsBffer */ FdsBffer[SndFds].BuffFlags = 0; FdsBffer[RcvFds].BuffFlags = 0; Found = 1; break; /* break out of while */ } } XPtr = XPtr -> NxtActTcb; } if (Found == 1) { break; /* break out of case */ } /* create some work for PsipOut */ MakeWrk(0, NETOUTHP, Handle, NetFdsW); } /* send back an acknowledge */ AckPtr = CmdTcp; AckPtr -> YOpCode = OACK; /* return the actual ptr to TCB this time only */ /* all other acks return the index into the usertcb */ AckPtr -> YHandle = Handle; write(Handle -> TCPCmdPort, CmdTcp, 4); break; } case OCLOSE: { /* begin connection closing procedure */ ClosePtr = CmdUsr; XPtr = ClosePtr -> CHandle; Error = ChkPtr(XPtr, WrkPtr -> PortFds); if (Error == -1) { break; /* user is doing something dumb */ } switch (XPtr -> State) { case OPEN: case SYNSENT: { /* move connection state to CLOSED */ XPtr -> State = CLOSED; /* send user a change notice */ CmdOut(XPtr, OCHNG, CLOSED, 0); break; } case SYNRECD: case ESTAB: case CLOSEWAIT: { XPtr -> Flags =| 010; /* send FIN after data */ XPtr -> SndFSN = XPtr -> SndISN + ClosePtr -> CFinNum + 1; MakeWrk(0, NETOUTHP, XPtr, NetFdsW); break; } default: { break; /* ignore request */ } } /* return an ack message to user */ CmdOut(XPtr, OACK, 0, 0); break; } case OABORT: { /* abort the connection */ AbortPtr = CmdUsr; XPtr = AbortPtr -> YHandle; Error = ChkPtr(XPtr, WrkPtr -> PortFds); if (Error == -1) { break; /* user is doing something dumb */ } /* verify that abort is allowed here */ switch(XPtr -> State) { case CLOSED: case OPEN: case SYNSENT: { /* clean up and close out the connection */ Abort(XPtr, 0); break; } default: { /* every other connection state */ /* set the RST flag and let PsipOut finish the abortion */ XPtr -> TCPFlags =| RST + ACK; XPtr -> Flags =| 020; /* abort connection */ MakeWrk(0, NETOUTHP, XPtr, NetFdsW); break; } } break; } case OURGENT: { /* send urgent condition */ UrgPtr = CmdUsr; XPtr = UrgPtr -> UHandle; Error = ChkPtr(XPtr, WrkPtr -> PortFds); if (Error == -1) { break; /* user is doing something dumb */ } XPtr -> SndUrgNo = (UrgPtr -> UByteNo) + (XPtr -> SndISN) + 1; /* check if everything has been ACKed */ R1 = ULCompare(&(XPtr -> SndUrgNo), &(XPtr -> LeftSeq)); if (R1 != 1) /* SndUrgNo <= LeftSeq */ { break; /* ignore it - already ACKed */ } /* set urgent work bit */ XPtr -> Flags =| 01; if ((XPtr -> State == ESTAB)||(XPtr -> State == CLOSEWAIT)) { /* make work for PsipOut */ MakeWrk(0, NETOUTHP, XPtr, NetFdsW); } break; } case OMOVE: { MovePtr = CmdUsr; TPtr = MovePtr -> YHandle; Error = ChkPtr(TPtr, WrkPtr -> PortFds); if (Error == -1) { break; /* user is doing something dumb */ } if ((TPtr -> State != ESTAB)&& (TPtr -> State != CLOSEWAIT)&& (TPtr -> State != FINWAIT)) { CmdOut(TPtr, OERR, EMVSTATE, 0); break; } /* find a TCB in ACCEPT mode */ Found = 0; XPtr = TcbHd; while((XPtr != 0)&&(Found == 0)) { /* TMode = 2 => ACCEPT */ if ((XPtr -> TMode == 2)&&(XPtr -> State == OPEN)) { /* check process ID */ if ((MovePtr -> YMisc == 0 /* unspecified */ )|| (MovePtr -> YMisc == XPtr -> ProcID)) { /* check for SPT data */ if (Auth == 1) { /* validate S/P parameters */ if ((TPtr -> ScMaxOut <= XPtr -> ScMaxOut)&& (TPtr -> ScMinOut >= XPtr -> ScMinOut)&& (TPtr -> ScMaxIn <= XPtr -> ScMaxIn)&& (TPtr -> ScMinIn >= XPtr -> ScMinIn)&& (TPtr -> SndPrec <= XPtr -> ATPrec)&& (TPtr -> MxRcvPrec <= XPtr -> MxRcvPrec)&& (TPtr -> MnRcvPrec >= XPtr -> MnRcvPrec)) { /* so far so good */ /* check for matching TCC */ for (i = 0; i < XPtr -> TCCCnt; i++) { if (TPtr -> TccList[0] == XPtr -> TccList[i]) { Found = 1; /* at last! */ } } } } else { Found = 1; } } } if (Found == 0) { XPtr = XPtr -> NxtActTcb; } } if (Found == 0) { CmdOut(TPtr, OERR, EBADPARAM, 0); break; } /* exchange the four port fds */ T = TPtr -> TCPCmdPort; TPtr -> TCPCmdPort = XPtr -> TCPCmdPort; XPtr -> TCPCmdPort = T; T = TPtr -> UserCmdPort; TPtr -> UserCmdPort = XPtr -> UserCmdPort; XPtr -> UserCmdPort = T; T = TPtr -> SendPort; TPtr -> SendPort = XPtr -> SendPort; XPtr -> SendPort = T; T = TPtr -> RecvPort; TPtr -> RecvPort = XPtr -> RecvPort; XPtr -> RecvPort = T; /* reset state of TCB */ XPtr -> State = CLOSED; /* exchange connection handles */ T = TPtr -> ConnHandle; TPtr -> ConnHandle = XPtr -> ConnHandle; XPtr -> ConnHandle = T; /* reset FSM of DataIn routine */ TPtr -> DataState = 0; /* set process ID */ TPtr -> ProcID = XPtr -> ProcID; /* fix up FdsBffer */ for (i = 0; i < FDSSIZE; i++) { if (FdsBffer[i].BuffFlags != 0) { if (FdsBffer[i].BuffTcbPtr == XPtr) { FdsBffer[i].BuffTcbPtr = TPtr; } else if (FdsBffer[i].BuffTcbPtr == TPtr) { FdsBffer[i].BuffTcbPtr = XPtr; } } } /* fix up work list */ WPtr = WorkHead -> NextWork; while(WPtr -> NextWork != 0) { if (WPtr -> TCBPtr == XPtr) { WPtr -> TCBPtr = TPtr; } else if (WPtr -> TCBPtr == TPtr) { WPtr -> TCBPtr = XPtr; } WPtr = WPtr -> NextWork; } /* ack the move command */ CmdOut(XPtr, OACK, 0, 0); /* update the library UTCB parameters of mover */ UPtr = CmdTcp; UPtr -> ZOpCode = OUPDATE; UPtr -> ZHandle = XPtr -> ConnHandle; UPtr -> ZConnIndex = XPtr; i = XPtr -> DataTl - XPtr -> DataHd; if (i < 0) { i =+ RBUFFSIZE; } UPtr -> ZSndCnt = TPtr -> LeftSeq + i - TPtr -> SndISN - 1; UPtr -> ZRcvCnt = TPtr -> AckNo - TPtr -> RcvISN - 1; write(XPtr -> TCPCmdPort, CmdTcp, 14); /* update the library UTCB parameters of movee */ UPtr -> ZHandle = TPtr -> ConnHandle; UPtr -> ZConnIndex = TPtr; write(TPtr -> TCPCmdPort, CmdTcp, 14); /* send CLOSED to the mover */ CmdOut(XPtr, OCHNG, CLOSED, 0); /* send state to movee */ CmdOut(TPtr, OCHNG, TPtr -> State, 0); /* make work in case movee has sent data */ MakeWrk(0, USERINHP, TPtr, TPtr -> SendPort); break; } case OSTAT: { /* send back status information to user */ SPtr = CmdUsr; XPtr = SPtr -> YHandle; Error = ChkPtr(XPtr, WrkPtr -> PortFds); if (Error == -1) { break; /* user is doing something dumb */ } SPtr = CmdTcp; SPtr -> SOpCode = OSTAT; SPtr -> SState = XPtr -> State; SPtr -> SNet = XPtr -> DstNet; SPtr -> SHost = XPtr -> DstHstH; SPtr -> SImp = XPtr -> DstHstL; SPtr -> SLocPrt = XPtr -> SrcePort; SPtr -> SForPrt = XPtr -> DestPort; SPtr -> SScMxOt = XPtr -> ScMaxOut; SPtr -> SScMnOt = XPtr -> ScMinOut; SPtr -> SScMxIn = XPtr -> ScMaxIn; SPtr -> SScMnIn = XPtr -> ScMinIn; SPtr -> SSndPrec = XPtr -> SndPrec; SPtr -> SRcvPrec = XPtr -> MnRcvPrec; SPtr -> SNTcc = XPtr -> TCCCnt; for (i = 0;i < XPtr -> TCCCnt; i++) { SPtr -> STcc[i] = XPtr -> TccList[i]; } SPtr -> SSndWind = XPtr -> SndWindow; SPtr -> SRcvWind = XPtr -> Window; SPtr -> SAckData = XPtr -> DataNxt - XPtr -> DataHd; if (SPtr -> SAckData < 0) { SPtr -> SAckData =+ RBUFFSIZE; } SPtr -> SSndBuff = XPtr -> DataTl - XPtr -> DataHd; if (SPtr -> SSndBuff < 0) { SPtr -> SSndBuff =+ RBUFFSIZE; } SPtr -> SSegRecv = XPtr -> SegRecv; SPtr -> SDupSeg = XPtr -> DupRecv; SPtr -> SBusyRecv = XPtr -> BusyRecv; SPtr -> SRetran = XPtr -> RetranSeg; write (XPtr -> TCPCmdPort, CmdTcp, 48); break; } case OSTOP: { /* stop traffic from host/tcc */ ErrPtr = CmdTcp; if (Auth == 0) { /* this command not allowed */ ErrPtr -> YOpCode = OERR; ErrPtr -> YMisc = EUNAUTH; write(WrkPtr -> TCBPtr, CmdTcp, 6); break; } /* verify that user is priviledged */ k = GetFields(PortHdr.RealUserID, &ParamVal); if ((k == -1)||(ParamVal[0] != 1)) { /* unauthorized action */ ErrPtr -> YOpCode = OERR; ErrPtr -> YMisc = EUNAUTH; write(WrkPtr -> TCBPtr, CmdTcp, 6); break; } ClosePtr = CmdUsr; if (ClosePtr -> CHandle == 1) { /* foreign socket number */ /* check if already in table */ k = -1; for (i = 0; i < NSTOP; i++) { if (StopSkt[i] != 0) { if (ClosePtr -> CFinNum == StopSkt[i]) { /* already in table */ AckPtr = CmdTcp; AckPtr -> YOpCode = OACK; write(WrkPtr -> TCBPtr, CmdTcp, 2); break; } } else { k = i; /* save index */ } } if (k == -1) { /* no space */ ErrPtr = CmdTcp; ErrPtr -> YOpCode = OERR; ErrPtr -> YMisc = EBADPARAM; write(WrkPtr -> TCBPtr, CmdTcp, 6); break; } else { StopSkt[k] = ClosePtr -> CFinNum; AckPtr = CmdTcp; AckPtr -> YOpCode = OACK; write(WrkPtr -> TCBPtr, CmdTcp, 2); break; } } else { /* TCC specified */ /* check if already in table */ k = -1; j = ClosePtr -> CFinNum; for (i = 0; i < NSTOP; i++) { if (StopTcc[i] < 0) { if (j == (StopTcc[i]&0377)) { /* already in table */ AckPtr = CmdTcp; AckPtr -> YOpCode = OACK; write(WrkPtr -> TCBPtr, CmdTcp, 2); break; } } else { k = i; /* save index */ } } if (k == -1) { /* no space */ ErrPtr = CmdTcp; ErrPtr -> YOpCode = OERR; ErrPtr -> YMisc = EBADPARAM; write(WrkPtr -> TCBPtr, CmdTcp, 6); break; } else { StopTcc[k] = j + 0100000; AckPtr = CmdTcp; AckPtr -> YOpCode = OACK; write(WrkPtr -> TCBPtr, CmdTcp, 2); break; } } } case ORESUME: { /* resume traffic from host/tcc */ if (Auth == 0) { /* this command not allowed */ ErrPtr = CmdTcp; ErrPtr -> YOpCode = OERR; ErrPtr -> YMisc = EUNAUTH; write(WrkPtr -> TCBPtr, CmdTcp, 6); break; } k = GetFields(PortHdr.RealUserID, &ParamVal); if ((k == -1)||(ParamVal[0] != 1)) { /* unauthorized action */ ErrPtr -> YOpCode = OERR; ErrPtr -> YMisc = EUNAUTH; write(WrkPtr -> TCBPtr, CmdTcp, 6); break; } ClosePtr = CmdUsr; if (ClosePtr -> CHandle == 1) { /* foreign socket number */ /* find in table */ for (i = 0; i < NSTOP; i++) { if (StopSkt[i] != 0) { if (ClosePtr ->CFinNum == StopSkt[i]) { StopSkt[i] = 0; AckPtr = CmdTcp; AckPtr -> YOpCode = OACK; write(WrkPtr -> TCBPtr, CmdTcp, 2); break; } } } /* must not have been there */ ErrPtr = CmdTcp; ErrPtr -> YOpCode = OERR; ErrPtr -> YMisc = EBADPARAM; write(WrkPtr -> TCBPtr, CmdTcp, 6); break; } else { /* TCC specified */ /* find in table */ j = ClosePtr -> CFinNum; for (i = 0; i < NSTOP; i++) { if (StopTcc[i] < 0) { if (j == (StopTcc[i]&0377)) { StopTcc[i] = 0; AckPtr = CmdTcp; AckPtr -> YOpCode = OACK; write(WrkPtr -> TCBPtr, CmdTcp, 2); break; } } } /* must not have been there */ ErrPtr = CmdTcp; ErrPtr -> YOpCode = OERR; ErrPtr -> YMisc = EBADPARAM; write(WrkPtr -> TCBPtr, CmdTcp, 6); break; } } default: { printf("CmdIn: bad opcode\n"); break; } } } } /* */ /* Subroutine which handles data from the user */ DataIn(WrkPtr) struct WorkBlk *WrkPtr; { struct MSend *SendPtr; struct PMask1 *SecPtr; int DatCnt; int Cap[2]; struct TcpTcb *XPtr; char *InAddr; int SpcAvl; int TailSpc; struct WorkBlk *APtr; int Error; int d; int RemData; int Secur; int i; int NewData; /* DataState format +--------+----+----+ ! secur !eol !sta ! (bits: 8, 4, 4) +--------+----+----+ */ /* FlagBuff format +-----+---+ !secur!eol! (bits: 5, 3) +-----+---+ */ XPtr = WrkPtr -> TCBPtr; if (XPtr -> State == CLOSED) { return(0); } capac(WrkPtr -> PortFds, Cap, 4); if (Debug == 1) { printf("DataIn: entered\n"); /* printf("DataIn: %d %d %d %d %d\n", Cap[0], XPtr -> DataState, XPtr -> DataHd, XPtr -> DataTl, XPtr -> DataNxt); */ } if (Cap[0] == 0) { return(0); /* no data to read */ } /* if (Debug == 1) { XPtr = WorkPtr -> TCBPtr; printf("%d %d %d %d\n", XPtr -> DataState, XPtr -> DataHd, XPtr -> DataTl, XPtr -> DataNxt); for (i = 0; i < RBUFFSIZE; i++) { printf("%o\n", XPtr -> FlagBuff[i]); } } */ /* calculate space available in input buffer */ SpcAvl = (XPtr -> DataHd) - (XPtr -> DataTl) -1; if (SpcAvl < 0) { /* wrap around */ SpcAvl =+ RBUFFSIZE; } if (SpcAvl == 0) { /* input buffer full */ return(1); /* leave work block on queue */ } NewData = 0; /* check if FIN flag set */ if (((XPtr -> Flags)&010) > 0) { /* check if all data has been received from user */ RemData = XPtr -> DataTl - XPtr -> DataHd; if (RemData < 0) { RemData =+ RBUFFSIZE; } if ((XPtr -> LeftSeq + RemData) == (XPtr -> SndFSN)) { /* incoming data is beyond the close point! */ return(0); /* flush it */ } } /* read in all data possible */ while((SpcAvl > 0)&&(Cap[0] > 0)) { /* determine state of input stream */ switch ((XPtr -> DataState)&017) { case 0: /* read control message */ { /* read in message header */ d = read(WrkPtr -> PortFds, &PortHdr, HDRLNGTH); DatCnt = PortHdr.DCount&~0100000; if (DatCnt == 0) { /* user closed his data port */ /* printf("DataIn: user closed data port to tcp\n"); */ return(0); } /* read in the rest of the message */ d = read(WrkPtr -> PortFds, CmdUsr, DatCnt); /* extract the byte count of data message */ SendPtr = CmdUsr; Cap[0] =- (DatCnt + HDRLNGTH); /* set state = 1; save EOL, security info */ XPtr -> DataState = ((SendPtr -> SnSecur) << 8) + ((SendPtr -> SnFlags) << 4) + 1; /* check if security level is within range */ XPtr -> Flags =& ~0200; if (Auth == 1) { if ((SendPtr -> SnSecur > XPtr -> ScMaxOut)|| (SendPtr -> SnSecur < XPtr -> ScMinOut)) { /* set flag to make data be discarded */ XPtr -> Flags =| 0200; /* discard data */ } } break; } case 1: /* read in the data message */ { /* read in the header */ d = read(WrkPtr -> PortFds, &PortHdr, HDRLNGTH); /* set state = 2, resid data */ /* need to increment to preserve security/flag info */ XPtr -> DataState =+ 1; XPtr -> DataLeft = PortHdr.DCount&~0100000; Cap[0] =- HDRLNGTH; break; } case 2: /* read some residual data in port */ { DatCnt = XPtr -> DataLeft; /* check if need to discard */ if ((Auth == 1)&&(((XPtr -> Flags)&0200) > 0)) { /* security level wrong - discard data */ /* save the count of discarded data */ SecPtr = CmdTcp; SecPtr -> YMisc = XPtr -> DataLeft; Cap[0] =- DatCnt; /* flush the data */ while ((XPtr -> DataLeft) > TCPINSZ) { read(WrkPtr -> PortFds, TcpIn, TCPINSZ); XPtr -> DataLeft =- TCPINSZ; } read(WrkPtr -> PortFds, TcpIn, XPtr -> DataLeft); /* inform user of illegal security level */ SecPtr -> YOpCode = OSECERR; SecPtr -> YHandle = XPtr -> ConnHandle; write(XPtr -> TCPCmdPort, CmdTcp, 6); /* return datain state to zero */ XPtr -> DataState = 0; } else { /* read in the residual data */ /* read in only enough to fill buffer */ InAddr = &(XPtr -> RetranBuff[XPtr -> DataTl]); /* calculate space remaining in tail of circular buffer */ if (XPtr -> DataTl >= XPtr -> DataHd) { TailSpc = RBUFFSIZE - (XPtr -> DataTl); if (XPtr -> DataHd == 0) { TailSpc =- 1; /* special case */ } } else { TailSpc = (XPtr -> DataHd) - (XPtr -> DataTl) - 1; } if (DatCnt <= TailSpc) { d = read(WrkPtr -> PortFds, InAddr, DatCnt); Cap[0] =- DatCnt; SpcAvl =- DatCnt; NewData = 1; /* set EOL, security levels here */ if (Auth == 1) { Secur = ((XPtr -> DataState) >> 5)&0170; for (i = XPtr -> DataTl; i < (XPtr -> DataTl) + DatCnt; i++) { XPtr -> FlagBuff[i] = Secur; } /* check for EOL */ if ((((XPtr -> DataState) >> 4)&01) == 1) { /* set EOL Flag */ XPtr -> FlagBuff[i-1] =| 1; } } (XPtr -> DataTl) =+ DatCnt; if ((XPtr -> DataTl) == RBUFFSIZE) { XPtr -> DataTl = 0; } /* return data-in state to zero */ XPtr -> DataState = 0; } else { d = read(WrkPtr -> PortFds, InAddr, TailSpc); Cap[0] =- TailSpc; SpcAvl =- TailSpc; NewData = 1; /* set security level */ if (Auth == 1) { Secur = ((XPtr -> DataState) >> 5)&0170; for (i = XPtr -> DataTl; i < (XPtr -> DataTl) + TailSpc; i++) { XPtr -> FlagBuff[i] = Secur; } } XPtr -> DataLeft =- TailSpc; XPtr -> DataTl =+ TailSpc; if ((XPtr -> DataTl) == RBUFFSIZE) { XPtr -> DataTl = 0; } } } break; } } } if (NewData == 1) { if ((XPtr -> State == ESTAB)|(XPtr -> State == CLOSEWAIT)) { MakeWrk(0, NETOUTHP, XPtr, NetFdsW); } } if (Cap[0] == 0) { return(0); /* remove work block */ } return(1); /* leave work on queue */ } /* */ /* Signal trapping subroutines */ sg1() { printf("signal 1\n"); signal(1,&sg1); } sg2() { printf("signal 2\n"); signal(2,&sg2); } sg3() { printf("signal 3\n"); signal(3,&sg3); } sg8() { printf("signal 8\n"); LogEnd(); Status(); exit(); /* halt */ } sg13() { printf("signal 13\n"); signal(13,&sg13); } /* */ /* Subroutine for parsing "tcptable" */ ReadTbl() { extern int LocNet; extern int LocHost; extern int LocImp; extern int InLink; extern int ANLngth; extern int NetTable[NUMNET]; int GtHost; int GtImp; int State; int i, k; char Chr; char Numb[12]; int Param[NUMTCPTBL]; struct buf { int fildes; int nunused; char *xfree; char buff[512]; } ibuf; /* first line of table has the following example format : Net Host Imp Link ANLngth GtHost GtImp : 10 0 63 155 2 2 5 where: Net : the local net tcp lives on Host : the local host tcp lives on Imp : the local imp tcp host is attached to Link : the link number for internet traffic ANLngth : the Arpanet leader length in words GtHost : the smart gateway host to forward to unknown nets GtImp : the imp that the smart gateway host is attached to */ /* The next N lines contain gateway routing information. Example: arpanet -> rccnet :1: 10 3 2 5 where: the parameter in colon brackets is the line enable bit, with a 1 => use this line and 0 => ignore this line. format of following parameters: 1. from net 2. to net 3. gateway host 4. gateway imp */ i = fopen("tcptable", &ibuf); if (i == -1) { printf("make me a tcptable!\n"); exit(); /* die */ } State = 6; /* read the first line */ while((Chr = getc(&ibuf)) != -1) { switch(State) { case 0: /* looking for colon */ { if (Chr == ':') { State = 1; } break; } case 1: /* get enable bit */ { if (Chr == '0') { /* this line is disabled */ State = 2; /* flush line */ } else { State = 3; /* look for colon */ } break; } case 2: /* flush line */ { if (Chr == '\n') { State = 0; } break; } case 3: /* look for terminating colon */ { if (Chr == ':') { State = 4; /* get parameters */ i = 0; k = 0; } else { return(-1); } break; } case 4: /* get parameters */ { if ((Chr != ' ')&&(Chr != '\t')) { State = 5; /* stop at next blank/tab */ } Numb[i] = Chr; i++; if (i == 12) { return(-1); } break; } case 5: /* wait for blank/tab/nl */ { if ((Chr == ' ')||(Chr == '\t')||(Chr == '\n')) { Numb[i] = '\0'; Param[k] = atoi(Numb); if (k == 0) { if (Param[0] != LocNet) { State = 2; /* flush line */ break; } } k++; if ((k == 4)||(Chr == '\n')) { /* enter in gateway table */ if (Param[1] >= NUMNET) { return(-1); } NetTable[Param[1]] = (Param[2] << 8) + Param[3]; if (Chr == '\n') { State = 0; break; } State = 2; /* flush line */ break; } i = 0; State = 4; /* get next parameter */ } else { Numb[i] = Chr; i++; if (i == 12) { return(-1); } } break; } case 6: /* looking for first line colon */ { if (Chr == ':') { State = 7; for (i = 0; i < NUMTCPTBL; i++) { Param[i] = 0; } i = 0; k = 0; } break; } case 7: /* getting parameters */ { if ((Chr != ' ')&&(Chr != '\t')) { State = 8; /* stop at next blank/tab */ } Numb[i] = Chr; i++; if (i == 12) { return(-1); } break; } case 8: /* wait for blank/tab/nl */ { if ((Chr == ' ')||(Chr == '\t')||(Chr == '\n')) { Numb[i] = '\0'; i = 0; Param[k] = atoi(Numb); k++; if ((k == NUMTCPTBL)||(Chr == '\n')) { /* set up tcp network values */ LocNet = Param[0]; LocHost = Param[1]; LocImp = Param[2]; InLink = Param[3]; ANLngth = Param[4]; GtHost = Param[5]; GtImp = Param[6]; if (Chr == '\n') { State = 0; /* get gateway connectivity values */ if (k != NUMTCPTBL) { return(-1); } break; } State = 2; /* flush line */ } else { State = 7; /* get next parameter */ } } else { Numb[i] = Chr; i++; if (i == 12) { return(-1); } } break; } } } if (NetTable[LocNet] == 0) { /* set flag sending net output directly to host */ NetTable[LocNet] = -1; } /* for unknown nets - send to more intelligent gateway */ for (i = 0; i < NUMNET; i++) { if (NetTable[i] == 0) { NetTable[i] = (GtHost << 8) + GtImp; } } close(ibuf.fildes); return(0); } /* */ /* Subroutine for putting a work block on a work queue */ MakeWrk(Queue, WFlags, WTcbPtr, WFds) int Queue; /* specifies the particular work queue */ int WFlags; struct TcpTcb *WTcbPtr; int WFds; { register struct WorkBlk *TempPtr; int Stop; /* check if duplicate entry */ TempPtr = WorkHead -> NextWork; Stop = 0; while ((TempPtr -> NextWork != 0)&&(Stop == 0)) { if ((TempPtr -> PortFlags == WFlags)&& (TempPtr -> TCBPtr == WTcbPtr)&&(TempPtr -> PortFds == WFds)) { /* found duplicate entry */ /* delete old block */ (TempPtr -> NextWork) -> LastWork = TempPtr -> LastWork; (TempPtr -> LastWork) -> NextWork = TempPtr -> NextWork; TempPtr -> NextWork = WorkAvail; WorkAvail = TempPtr; Stop = 1; } TempPtr = TempPtr -> NextWork; } if (WorkAvail == 0) { /* no more blocks */ printf("MakeWrk: no more work blocks\n"); return(ENOBUFS); } TempPtr = WorkAvail; WorkAvail = WorkAvail -> NextWork; TempPtr -> LastWork = WorkTail -> LastWork; TempPtr -> NextWork = WorkTail; (WorkTail -> LastWork) -> NextWork = TempPtr; WorkTail -> LastWork = TempPtr; /* fillin the block */ TempPtr -> PortFlags = WFlags; TempPtr -> TCBPtr = WTcbPtr; TempPtr -> PortFds = WFds; return(0); } /* */ /* Subroutine for unsigned long compare */ ULCompare(A, B) long *A, *B; { /* returns: 0 = equal */ /* 1 = A > B */ /* 2 = A < B */ if (*A == *B) { return(0); } if ((*A - *B) < 0) { return(2); } return(1); } /* */ /* Subroutine for putting a TCB on the retransmission queue */ QTcb(XPtr) struct TcpTcb *XPtr; { int First; int Time; struct TcpTcb *SPtr; int Stop; int AbsTime; /* check debug parameter */ if (NoRetran == 1) { /* no retransmissions allowed today */ return(0); } /* check if TCB is already on retransmission queue */ if (XPtr -> NxtRetran != 0) { /* already linked in */ return(0); } /* determine next retransmission time */ if (XPtr -> NumRetran >= RetCount) { /* go into slow mode */ Time = *TimePtr + RetSlow; } else { /* still in fast mode */ Time = *TimePtr + RetFast; } if (RetranHd == 0) { /* empty queue */ RetranHd = XPtr; XPtr -> NxtRetran = XPtr; XPtr -> LstRetran = XPtr; XPtr -> RetranTm = Time; return(0); } /* find insertion point */ First = 1; SPtr = RetranHd; AbsTime = 0; Stop = 0; do { AbsTime =+ SPtr -> RetranTm; if (AbsTime < Time) { First = 0; SPtr = SPtr -> NxtRetran; } else { Stop = 1; } } while((Stop == 0)&&(SPtr != RetranHd)); XPtr -> NxtRetran = SPtr; XPtr -> LstRetran = SPtr -> LstRetran; (SPtr -> LstRetran) -> NxtRetran = XPtr; SPtr -> LstRetran = XPtr; if (First == 1) { /* TCB is at head of queue */ XPtr -> RetranTm = Time; SPtr -> RetranTm =- XPtr -> RetranTm; RetranHd = XPtr; return(0); } if (SPtr == RetranHd) { /* TCB is at end of queue */ XPtr -> RetranTm = Time - AbsTime; return(0); } /* TCB is in middle of queue */ XPtr -> RetranTm = Time - AbsTime + (SPtr -> RetranTm); SPtr -> RetranTm =- XPtr -> RetranTm; return(0); } /* */ /* Subroutine for removing TCB from retransmission queue */ UnQTcb(XPtr) struct TcpTcb *XPtr; { /* check if not queued */ if (XPtr -> NxtRetran == 0) { return(0); /* not queued */ } /* check for single entry */ if (XPtr -> NxtRetran == XPtr) { /* just one entry in queue */ RetranHd = 0; XPtr -> NxtRetran = 0; return(0); } (XPtr -> NxtRetran) -> LstRetran = XPtr -> LstRetran; (XPtr -> LstRetran) -> NxtRetran = XPtr -> NxtRetran; if (XPtr -> NxtRetran != RetranHd) { /* update time increment */ (XPtr -> NxtRetran) -> RetranTm =+ XPtr -> RetranTm; } if (XPtr == RetranHd) { /* deleted head of queue */ RetranHd = XPtr -> NxtRetran; } XPtr -> NxtRetran = 0; } /* */ /* Subroutine for cleaning up an aborted connection */ Abort(XPtr, Flag) struct TcpTcb *XPtr; int Flag; { struct TcpTcb *TempPtr; struct PMask1 *AckPtr; struct WorkBlk *WrkPtr; int Stop; int i,j,k; /* disable wakeups on data ports */ awtdis(XPtr -> SendPort); awtdis(XPtr -> RecvPort); /* close the ports */ close(XPtr -> SendPort); close(XPtr -> RecvPort); /* clear FdsBffer entries */ FdsBffer[XPtr -> SendPort].BuffFlags = 0; FdsBffer[XPtr -> RecvPort].BuffFlags = 0; /* remove bit in PortMap if applicable */ if ((XPtr -> SrcePort >= BASEPORT)&& (XPtr -> SrcePort < BASEPORT + 0400)) { /* clear the Port Map bit */ k = 1; /* bit index */ j = (XPtr -> SrcePort)&017; for (i = 0; i < j; i++) { k =<< 1; } PortMap[((XPtr -> SrcePort) >> 4)&017] =& ~k; } /* remove TCB from retransmission Q if applicable */ UnQTcb(XPtr); /* remove work queue entries associated with TCB */ WrkPtr = WorkHead -> NextWork; while(WrkPtr -> NextWork != 0) { TempPtr = WrkPtr -> NextWork; if (WrkPtr -> TCBPtr == XPtr) { /* found an entry to remove */ (WrkPtr -> NextWork) -> LastWork = WrkPtr -> LastWork; (WrkPtr -> LastWork) -> NextWork = WrkPtr -> NextWork; WrkPtr -> NextWork = WorkAvail; WorkAvail = WrkPtr; } WrkPtr = TempPtr; } if (Flag == 0) { /* send ACK to user */ AckPtr = CmdTcp; AckPtr -> YOpCode = OACK; AckPtr -> YHandle = XPtr -> ConnHandle; write(XPtr -> TCPCmdPort, CmdTcp, 4); } /* delete TCB */ if (XPtr == TcbHd) { TcbHd = XPtr -> NxtActTcb; XPtr -> NxtActTcb = TcbAvl; TcbAvl = XPtr; } else { Stop = 0; TempPtr = TcbHd; while((TempPtr -> NxtActTcb != 0)&&(Stop == 0)) { if (TempPtr -> NxtActTcb == XPtr) { Stop = 1; } else { TempPtr = TempPtr -> NxtActTcb; } } if (Stop == 1) { /* found it, as expected */ TempPtr -> NxtActTcb = XPtr -> NxtActTcb; XPtr -> NxtActTcb = TcbAvl; TcbAvl = XPtr; } } } /* Subroutine which verifies that ptr to TCB is valid */ ChkPtr(XPtr, Fds) struct TcpTcb *XPtr; int Fds; { struct TcpTcb *TPtr; TPtr = TcbHd; while (TPtr != 0) { if (XPtr == TPtr) { /* check correct file desc */ if (Fds == TPtr -> UserCmdPort) { return(0); /* everything okay */ } else { printf("ChkPtr: bad fds\n"); return(-1); } } else { TPtr = TPtr -> NxtActTcb; } } printf("ChkPtr: bad ptr\n"); return(-1); } Status() { extern int Auth; extern long StopSkt[NSTOP]; extern int StopTcc[NSTOP]; struct TcpTcb *XPtr; int Cap0[2]; int Cap1[2]; int Cap2[2]; int Cap3[2]; int i; struct WorkBlk *WPtr; struct { char XHost; char XNet; int XImp; } *SktPtr; /* print the capacity of the ear port */ capac(EarFds, Cap2, 4); printf("Ear: %d\n", Cap2[0]); /* print the work queue */ WPtr = WorkHead -> NextWork; while(WPtr -> NextWork != 0) { printf("WrkQ: %d %d %o\n", WPtr -> PortFlags, WPtr -> PortFds, WPtr -> TCBPtr); WPtr = WPtr -> NextWork; } /* print active entries in FdsBffer */ for (i = 0; i < FDSSIZE; i++) { if (FdsBffer[i].BuffFlags != 0) { printf("Fds: %d %d %o\n", FdsBffer[i].BuffFlags, FdsBffer[i].BuffFds, FdsBffer[i].BuffTcbPtr); } } /* print the active TCB list */ XPtr = TcbHd; while (XPtr != 0) { capac(XPtr -> TCPCmdPort, Cap0, 4); capac(XPtr -> UserCmdPort, Cap1, 4); capac(XPtr -> SendPort, Cap2, 4); capac(XPtr -> RecvPort, Cap3, 4); printf("TCB: %o %d %d %d %d S: %d N: %o H: %o I: %o, PID: %d\n", XPtr, Cap0[1], Cap1[0], Cap2[0], Cap3[1], XPtr -> State, XPtr -> DstNet, XPtr -> DstHstH, XPtr -> DstHstL, XPtr -> ProcID); XPtr = XPtr -> NxtActTcb; } /* print the retransmission queue */ if (RetranHd != 0) { XPtr = RetranHd; do { printf("RQ: %o %d\n", XPtr, XPtr -> RetranTm); XPtr = XPtr -> NxtRetran; } while(XPtr != RetranHd); } if (Auth == 1) { /* print the Stop/Resume entries */ printf("Stop Table\n"); for (i = 0; i < NSTOP; i++) { if (StopSkt[i] != 0) { SktPtr = &StopSkt[i]; printf("%d %d %d\n", SktPtr -> XNet, SktPtr -> XHost, SktPtr -> XImp); } } for (i = 0; i < NSTOP; i++) { if (StopTcc[i] < 0) { printf("%d ", StopTcc[i]&0377); } } printf("\n"); } } /* Subroutine for sending commands to user process */ CmdOut(XPtr, OpCode, Param1, Param2) struct TcpTcb *XPtr; int OpCode; int Param1; int Param2; { struct PMask1 *CPtr; struct PMask1 *EPtr; int DatCnt; CPtr = CmdTcp; CPtr -> YOpCode = OpCode; CPtr -> YHandle = XPtr -> ConnHandle; switch(OpCode) { case OERR: case OCHNG: case ONETMSG: { CPtr -> YMisc = Param1; DatCnt = 6; break; } case ORESET: case OACK: { DatCnt = 4; break; } default: { printf("CmdOut: bad opcode\n"); } } write(XPtr -> TCPCmdPort, CmdTcp, DatCnt); }