<> <> <> <> <> <> <> <> <> <<>> DIRECTORY Arpa USING [Address, nullAddress], ArpaBuf USING [Protocol], ArpaIP USING [AllocBuffers, Buffers, CreateHandle, DestroyHandle, FreeBuffers, GetUserBytes, Handle, OnesComplementAddBlock], Basics USING [BITNOT], BasicTime USING [GetClockPulses, MicrosecondsToPulses, Pulses, PulsesToMicroseconds], PrincOpsUtils USING [ByteBlt], Process USING [Abort, DisableTimeout, MsecToTicks, Pause, SetTimeout], ArpaTCP USING [DByte, Error, Reason, TCPInfo, Timeout], ArpaTCPOps USING [Buffer, ConnectionState, defaultProbeTimeout, recvBufferLength, sillyWindowLimit, TCPControlSet, TCPHandle, tcpHdrByteLength, TCPHeaderP, TCPSendBuffer], ArpaTCPLogging USING [PrintStateChange], ArpaTCPReceiving USING [ProcessRcvdSegment], ArpaTCPStates USING [Abort, Close, CloseConnection, CopyHandleList, GetInitialSequenceNumber, Open, ValidHandle], ArpaTCPTransmit USING [Rexmit, rexmitSleepTime, RepacketizeandRexmit, SendSegment, SendSYN]; ArpaTCPOpsImpl: CEDAR MONITOR LOCKS handle USING handle: TCPHandle IMPORTS ArpaIP, Basics, BasicTime, PrincOpsUtils, Process, ArpaTCP, ArpaTCPLogging, ArpaTCPReceiving, ArpaTCPStates, ArpaTCPTransmit EXPORTS ArpaTCPOps ~ BEGIN OPEN ArpaTCPOps; defaultReceiveWindow: PUBLIC INT _ ArpaTCPOps.recvBufferLength; probeTimeout: PUBLIC INT _ ArpaTCPOps.defaultProbeTimeout; repacketizing: PUBLIC BOOL _ FALSE; ourLocalAddress: PUBLIC Arpa.Address _ Arpa.nullAddress; tcpIPHandle: PUBLIC ArpaIP.Handle; retransmitProcess: PROCESS; sendBufferLength: PUBLIC INT _ 4000; -- max bytes of unacked data to queue for transmission or retransmission to net maxTCPDataLength: PUBLIC INT _ 536; -- default TCP Maximum Segment Size is 536 zeroChecksum: BOOLEAN _ FALSE; <> pktsSent: PUBLIC INT _ 0; pktsRcvd: PUBLIC INT _ 0; pktsRexmitted: PUBLIC INT _ 0; pktsDuplicate: PUBLIC INT _ 0; pktsWithNoConnection: PUBLIC INT _ 0; pktsFromFuture: PUBLIC INT _ 0; pktsFromPast: PUBLIC INT _ 0; pktsWithBadChecksum: PUBLIC INT _ 0; <> Open: PUBLIC PROC [tcpInfo: ArpaTCP.TCPInfo] RETURNS [handle: TCPHandle] ~ { <> RETURN ArpaTCPStates.Open[tcpInfo]; }; Close: PUBLIC ENTRY PROC [handle: TCPHandle] ~ { <> ArpaTCPStates.Close[handle]; }; Abort: PUBLIC ENTRY PROC [handle: TCPHandle] ~ { <> IF handle # NIL THEN ArpaTCPStates.Abort[handle]; }; WaitForListenerOpen: PUBLIC ENTRY PROC [handle: TCPHandle, timeout: INT] ~ { <> startTime: BasicTime.Pulses; IF handle.state = listen THEN { startTime _ BasicTime.GetClockPulses[]; TRUSTED {IF timeout>0 THEN Process.SetTimeout[@handle.notListening, Process.MsecToTicks[MIN[timeout, 5000]]] ELSE Process.DisableTimeout[@handle.notListening]}; DO WAIT handle.notListening; IF handle.state # listen THEN EXIT; IF handle.dataTimeout >= 0 AND BasicTime.GetClockPulses[]-startTime > BasicTime.MicrosecondsToPulses[handle.dataTimeout*1000] THEN EXIT; ENDLOOP; }; }; SendCurrentDatagram: PUBLIC ENTRY PROC [handle: TCPHandle, push: BOOL] ~ { <> ENABLE UNWIND => NULL; control: TCPControlSet; GetNewOutputDatagram: PROC ~ { handle.currentOutputDatagram _ ArpaIP.AllocBuffers[1]; handle.currentOutputPtr _ tcpHdrByteLength; handle.currentOutputLimit _ handle.currentOutputPtr+handle.maxSegmentSize; }; ArpaTCPStates.ValidHandle[handle]; control.psh _ push; SELECT handle.state FROM listen => { IF NOT (handle.matchForeignPort AND handle.matchForeignAddr) THEN ERROR ArpaTCP.Error[neverOpen]; handle.active _ TRUE; handle.iss _ ArpaTCPStates.GetInitialSequenceNumber[]; handle.sndUna _ handle.iss; ArpaTCPLogging.PrintStateChange[handle, synSent]; handle.state _ synSent; ArpaTCPTransmit.SendSYN[handle]; }; synSent, synRcvd, established, closeWait => NULL; ENDCASE => ERROR ArpaTCP.Error[handle.reason]; QueueSendSegment[handle, handle.currentOutputDatagram, handle.currentOutputPtr-tcpHdrByteLength, control]; GetNewOutputDatagram[]; }; GetNextDatagram: PUBLIC ENTRY PROC [handle: TCPHandle] ~ { <> ENABLE UNWIND => NULL; ArpaTCPStates.ValidHandle[handle]; IF handle.currentInputBuffer # NIL THEN { ArpaIP.FreeBuffers[handle.currentInputBuffer.datagramPtr]; handle.currentInputBuffer _ NIL; }; IF handle.readyToReadQueue = NIL THEN { timeoutTime: BasicTime.Pulses _ SetTimeout[handle.dataTimeout]; WHILE handle.readyToReadQueue=NIL DO IF NOT ValidRcvState[handle.state] THEN ERROR ArpaTCP.Error[handle.reason]; IF TimedOut[timeoutTime] THEN { SIGNAL ArpaTCP.Timeout; timeoutTime _ SetTimeout[handle.dataTimeout]; }; WAIT handle.dataAvailable; ENDLOOP; }; handle.currentInputBuffer _ NARROW[handle.readyToReadQueue.first]; handle.readyToReadQueue _ handle.readyToReadQueue.rest; handle.rcvWnd _ handle.rcvWnd + handle.currentInputBuffer.dataByteCount; }; SetUrgent: PUBLIC ENTRY PROC [handle: TCPHandle] ~ { <> ENABLE UNWIND => NULL; ArpaTCPStates.ValidHandle[handle]; IF NOT handle.sndUrgent THEN { handle.sndUrgent _ TRUE; handle.sndUp _ handle.sndNxt + handle.nBytesToSend; }; }; WaitForUrgentData: PUBLIC ENTRY PROC [handle: TCPHandle] RETURNS [urgentIndex: INT] ~ { <> ENABLE UNWIND => NULL; ArpaTCPStates.ValidHandle[handle]; WHILE NOT handle.urgentMode AND ValidRcvState[handle.state] DO WAIT handle.urgentAvailable; ENDLOOP; handle.urgentMode _ FALSE; IF ValidRcvState[handle.state] THEN RETURN [handle.rcvUp] ELSE ERROR ArpaTCP.Error[handle.reason]; }; <> TCPChecksum: PUBLIC PROC [data: ArpaIP.Buffers] RETURNS [checksum: ArpaTCP.DByte] ~ TRUSTED { <> PseudoHeader: TYPE ~ MACHINE DEPENDENT RECORD [ sourceAddr (0): Arpa.Address, dstnAddr (2): Arpa.Address, zeroes (4: 0..7): BYTE, protocol (4: 8..15): ArpaBuf.Protocol, tcpTotalByteCount (5): ArpaTCP.DByte]; pseudoHeader: PseudoHeader; tcpHdrPtr: TCPHeaderP _ LOOPHOLE[@data.body]; cs: CARDINAL; pseudoHeader.sourceAddr _ data.hdr1.source; pseudoHeader.dstnAddr _ data.hdr1.dest; pseudoHeader.zeroes _ 0; pseudoHeader.protocol _ data.hdr1.protocol; pseudoHeader.tcpTotalByteCount _ ArpaIP.GetUserBytes[data].bodyBytes; IF pseudoHeader.tcpTotalByteCount MOD 2 # 0 THEN data.body.bytes[pseudoHeader.tcpTotalByteCount] _ 0; cs _ ArpaIP.OnesComplementAddBlock[ptr: tcpHdrPtr, count: (pseudoHeader.tcpTotalByteCount+1)/2, initialSum: Basics.BITNOT[tcpHdrPtr.checksum]]; -- Start with negative of the checksum that's in the header so that we don't have to smash it to zero to compute the real checksum. cs _ ArpaIP.OnesComplementAddBlock[ptr: @pseudoHeader, count: PseudoHeader.SIZE, initialSum: cs]; IF zeroChecksum THEN RETURN[0] ELSE RETURN [Basics.BITNOT[cs]]; -- return one's complement of computed sum }; ChecksumsMatch: PUBLIC PROC [c1, c2: ArpaTCP.DByte] RETURNS [BOOL] ~ { RETURN [c1 = c2 OR ((c1 = 0 OR c1 = 65535) AND (c2 = 0 OR c2 = 65535))]; }; ValidRcvState: PROC [state: ConnectionState] RETURNS [BOOL] ~ INLINE { <> RETURN [state IN [listen..finWait2]]; }; SetTimeout: PUBLIC PROC [delta: INT, base: BasicTime.Pulses _ BasicTime.GetClockPulses[]] RETURNS [BasicTime.Pulses] ~ { <> RETURN [IF delta>=0 THEN MAX[base+BasicTime.MicrosecondsToPulses[delta*1000], 1] ELSE 0]; }; TimedOut: PUBLIC PROC [timeoutTime: BasicTime.Pulses] RETURNS [BOOL] ~ { <> RETURN [timeoutTime#0 AND LOOPHOLE[BasicTime.GetClockPulses[]-timeoutTime, INT] >= 0]; }; <> CheckRexmitQueues: PROC = { <> l: LIST OF REF ANY; FOR l _ ArpaTCPStates.CopyHandleList[], l.rest WHILE l # NIL DO handle: TCPHandle _ NARROW[l.first]; CheckRexmit[handle ! ArpaTCP.Error => CONTINUE]; ENDLOOP; -- get next connection }; -- CheckRexmitQueues CheckRexmit: ENTRY PROC [handle: TCPHandle] ~ { <> ENABLE UNWIND => NULL; ArpaTCPStates.ValidHandle[handle]; IF handle.state = timeWait AND TimedOut[handle.timeWaitTime] THEN ArpaTCPStates.CloseConnection[handle, handle.reason] ELSE IF handle.rexmitQueue # NIL THEN { tcpSendBufferPtr: REF TCPSendBuffer _ NARROW[handle.rexmitQueue.first]; IF TimedOut[tcpSendBufferPtr.timeoutTime] THEN { -- timed out, close connection now: BasicTime.Pulses _ BasicTime.GetClockPulses[]; ArpaTCPStates.CloseConnection[handle, transmissionTimeout]; } ELSE IF TimedOut[tcpSendBufferPtr.rexmitTime] THEN IF repacketizing THEN -- optionally repacketize on rexmit ArpaTCPTransmit.RepacketizeandRexmit[handle, tcpSendBufferPtr] ELSE ArpaTCPTransmit.Rexmit[handle, tcpSendBufferPtr]; }; }; <> <> <> <<<0 => less,>> <<=0 => equal,>> <<>0 => greater,>> < ERROR];>> <<};>> QueueSendSegment: INTERNAL PROC [handle: TCPHandle, sendDatagram: ArpaIP.Buffers, dataByteCount: INT, ctl: TCPControlSet] ~ TRUSTED { <> timeoutTime: BasicTime.Pulses _ SetTimeout[handle.dataTimeout]; bytesInWindow: INT; <> bytesInWindow _ MIN[handle.sndWnd - handle.nBytesToSend, dataByteCount]; IF bytesInWindow > 0 THEN ArpaTCPTransmit.SendSegment[handle, sendDatagram, bytesInWindow, ctl] ELSE bytesInWindow _ 0; <> WHILE bytesInWindow < dataByteCount DO bytesToSend: INT; newDatagram: Buffer _ ArpaIP.AllocBuffers[1]; WaitForOpenWindow[handle]; bytesToSend _ MIN[handle.sndWnd - handle.nBytesToSend, dataByteCount - bytesInWindow]; IF bytesToSend < 0 THEN bytesToSend _ 0; -- maybe the window shrunk? <> bytesToSend _ PrincOpsUtils.ByteBlt[ from: [blockPointer: @sendDatagram.body, startIndex: tcpHdrByteLength+bytesInWindow, stopIndexPlusOne: tcpHdrByteLength+bytesInWindow+bytesToSend], to: [blockPointer: @newDatagram.body, startIndex: tcpHdrByteLength, stopIndexPlusOne: tcpHdrByteLength+bytesToSend] ]; ArpaTCPTransmit.SendSegment[handle, newDatagram, bytesToSend, ctl]; bytesInWindow _ bytesInWindow + bytesToSend; <> IF TimedOut[timeoutTime] THEN { SIGNAL ArpaTCP.Timeout; timeoutTime _ SetTimeout[handle.dataTimeout]; }; ENDLOOP; IF ctl.psh THEN WaitForAcks[handle]; }; -- QueueSendSegment WaitForOpenWindow: INTERNAL PROC [handle: TCPHandle, minWindow: INT _ sillyWindowLimit] RETURNS [] ~ { <> timeoutTime: BasicTime.Pulses _ SetTimeout[probeTimeout]; WHILE handle.sndWnd - handle.nBytesToSend < minWindow DO WAIT handle.windowAvailable; SELECT handle.state FROM synSent, synRcvd, established, closeWait => NULL; ENDCASE => ArpaTCP.Error[handle.reason]; IF TimedOut[timeoutTime] THEN EXIT; ENDLOOP; }; WaitForAcks: INTERNAL PROC [handle: TCPHandle] RETURNS [] ~ { <> start: BasicTime.Pulses _ BasicTime.GetClockPulses[]; timeoutTime: BasicTime.Pulses _ SetTimeout[handle.dataTimeout]; WHILE handle.toNetQueue # NIL OR handle.rexmitQueue # NIL DO WAIT handle.windowAvailable; SELECT handle.state FROM synSent, synRcvd, established, closeWait => NULL; ENDCASE => ArpaTCP.Error[handle.reason]; IF TimedOut[timeoutTime] THEN { now: BasicTime.Pulses _ BasicTime.GetClockPulses[]; duration: LONG CARDINAL = BasicTime.PulsesToMicroseconds[now-start]; SIGNAL ArpaTCP.Timeout; timeoutTime _ SetTimeout[handle.dataTimeout]; }; ENDLOOP; }; RetransmitProcessProc: PROC ~ { <> DO Process.Pause[Process.MsecToTicks[ArpaTCPTransmit.rexmitSleepTime]]; CheckRexmitQueues[]; ENDLOOP; }; StartupTCP: PROC ~ { tcpIPHandle _ ArpaIP.CreateHandle[protocol: tcp, recvProc: ArpaTCPReceiving.ProcessRcvdSegment, recvErrorProc: NIL, acceptLongDatagrams: FALSE]; TRUSTED { retransmitProcess _ FORK RetransmitProcessProc[]; }; }; ShutdownTCP: PROC ~ { TRUSTED { Process.Abort[retransmitProcess]; }; ArpaIP.DestroyHandle[tcpIPHandle]; }; StartupTCP[]; END.