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]; }; }; 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. ςArpaTCPOpsImpl.mesa Copyright (C) 1983 by Xerox Corporation. All rights reserved. The following program was created in 1983 but has not been published within the meaning of the copyright law, is furnished under license, and may not be used, copied and/or disclosed except in accordance with the terms of said license. Last Edited by: Nichols, September 1, 1983 4:27 pm Last Edited by: DCraft, November 26, 1983 6:59 pm Last Edited by: Taft, January 8, 1984 3:24 pm Last Edited by: HGM, April 23, 1984 4:58:48 pm PST Doug Terry, November 22, 1987 10:44:02 pm PST Hal Murray May 16, 1985 3:14:24 am PDT John Larson, October 11, 1987 9:40:04 pm PDT Statistics Routines used by ArpaTCPMain Attempts to open a new TCP connection. Returns a TCPHandle to use or raises ArpaTCP.OpenFailed. Shut down the connection normally. Shut down the connection abnormally. Wait until the listening connection on this socket is open. The current datagram in the handle is ready to send. Push is true if the TCP push option should be used. Empty datagrams are ignored unless push is true. We are through with the current input datagram and would like another. Only returns when new datagram has usable data. The urgent pointer is set to the current position in the output stream. Wait asynchronously for urgent data indication. Routines internal to the TCP stuff Find the checksum to be used in the TCP header. Call when the IP header has been properly filled in. Returns true if state is valid for receiving new segments. Returns a timeout time computed by adding delta milliseconds to the specified base time. If delta is negative, meaning "never time out", returns zero, which is specifically defined (in TimedOut) to mean "never time out" and is never otherwise returned. Returns true if the specified time has been reached. Private This procedure is called periodically to check the RexmitQueue on each handle on the handleList. It checks the first segment on each retransmit queue; if the segment has timed out, the connection is deleted, if it is time to retransmit the segment, the segment is retransmitted. Check the retransmit queues for a handle. Put here to get the handle lock. SequenceCompare: PROC [a, b: LONG CARDINAL] RETURNS [Basics.Comparison] ~ { Determines whether a is "less", "equal", or "greater" than b in mod 2^32 arithmetic. That is, a less, =0 => equal, >0 => greater, ENDCASE => ERROR]; }; Packets are only added to the ToNet queue if they fit within the current send window. If the window is smaller than the packet to be transmitted then the packet may be split into two or more packets such the the first piece can be sent immediately and the other pieces sent later (when the window opens up more). Send what I can now Send rest later by copying bytes from sendDatagram Copy data to new buffer Timeout if transmission not completed in a reasonable period Wait until send window is at least minWindow in size (or the probeTimeout expires). Wait for what we have sent to get acked Loops and calls CheckRexmitQueues every so often. Κ ˜– "cedar" style˜headšœ™Icode2šœ©™©J™2J™1J™-JšœΟkœ™2Icodešœ-™-Mšœ#™&Mšœ,™,J™š ˜ Mšœœ˜"Mšœœ ˜Lšœœq˜}Lšœœœ˜Lšœ œF˜UMšœœ ˜Lšœœ9˜FLšœœ*˜7Lšœ œ›˜«Lšœœ˜(Lšœœ˜,Lšœœ^˜qLšœœG˜\——š Οnœœœœœ˜BLšœ}˜„Lšœ ˜Lšœœœ ˜Lšœœœ˜?Lšœœœ"˜:Lšœœœœ˜#Lšœœ!˜8Lšœ œ˜"Lšœœ˜Lšœ œ ΟcP˜uLšœ œ Ÿ*˜OLšœœœ˜—™ Lšœ œœ˜Lšœ œœ˜Lšœœœ˜Lšœœœ˜Lšœœœ˜%Lšœœœ˜Lšœœœ˜Lšœœœ˜$—šœ™šžœœœœ˜LLšœœF™`Lšœ˜#L˜—šžœœœœ˜0L™"Lšœ˜L˜—šžœœœœ˜0L™$Lšœ œœ˜1L˜—š žœœœœœ˜LL™;Lšœ˜šœœ˜Lšœ'˜'šœœ ˜Lšœ>œ˜VLšœ/˜3—š˜Lšœ˜Lšœœœ˜#šœœ`˜‚Lšœ˜—Lšœ˜—L˜—Lšœ˜—š žœœœœœ˜JLšœJœN™›Lšœœœ˜Lšœ˜L˜šžœœ˜Lšœ6˜6Lšœ+˜+L˜JL˜L˜—Lšœ"˜"L˜šœœ˜šœ ˜ šœœœœ˜BLšœ˜—Lšœœ˜Lšœ7˜7Lšœ˜Lšœ2˜2Lšœ˜Lšœ!˜!L˜—Lšœ,œ˜1Lšœœ˜.—Lšœj˜jL˜L˜—šžœœœœ˜:L™wLšœœœ˜Lšœ"˜"šœœœ˜)Lšœ:˜:Lšœœ˜ L˜—šœœœ˜'Lšœ?˜?šœœ˜$šœœ˜'Lšœ˜#—šœœ˜Lšœ˜Lšœ-˜-L˜—Lšœ˜Lšœ˜—L˜—Lšœœ ˜BLšœ7˜7L˜HL˜—šž œœœœ˜4L™GLšœœœ˜Lšœ"˜"šœœœ˜Lšœœ˜Lšœ3˜3L˜—L˜—š žœœœœœœ˜WL™/Lšœœœ˜Lšœ"˜"šœœœ˜>Lšœ˜Lšœ˜—Lšœœ˜šœ˜#Lšœ˜—š˜Lšœ˜#—L˜——šœœ™"š ž œœœœœ˜]Lšœ$œ>™eš œœœ œœ˜/Lšœ˜Lšœ˜Lšœœ˜Lšœ'˜'Lšœ&˜&—Lšœ˜Lšœœ ˜-Lšœœ˜ Lšœ+˜+Lšœ'˜'L˜Lšœ+˜+LšœE˜ELšœ œœ5˜eLšœsœŸƒ˜“LšœKœ˜ašœ ˜Lšœœ˜—Lšœ œŸ*˜FL˜—š žœœœœœ˜FLš œ œ œ œ œ˜HL˜—š ž œœœœœ˜FLšœ:™:Lšœœ˜%L˜—š ž œœœ œ7œ˜xLšœό™όLš œœ œœ5œ˜YL˜—š žœœœ!œœ˜HL™4Lšœœœ)œ˜VL˜——™šžœœ˜Lšœ˜™˜Lš œœœœœ˜L˜šœ,œœ˜?Lšœœ ˜$Lšœ&œ˜0LšœŸ˜ —LšœŸ˜—šž œœœ˜/L™KLšœœœ˜Lšœ"˜"šœœ˜ALšœ4˜4—šœœœœ˜'Lšœœœ˜Gšœ(œŸ˜QLšœ3˜3Lšœ=˜=—šœ˜šœ'œ˜.šœœŸ#˜9Lšœ?˜?—šœ˜Lšœ1˜1———L˜—L˜—š žœœœœœ™KL™³š œœœœ™%L™ L™ L™Lšœœ™—L™—š žœœœBœœ˜…LšœΉ™ΉLšœ?˜?Lšœœ˜Lšœ™Lšœœ5˜Hšœ˜LšœF˜JLšœ˜—Lšœ2™2šœ˜&Lšœ œ˜L˜-Lšœ˜LšœœE˜VLšœœŸ˜EL™šœ$˜$Lšœ“˜“Lšœv˜v—LšœC˜CLšœ,Ÿ˜-L™<šœœ˜Lšœ˜Lšœ0˜0—Lšœ˜—Lšœ œ˜$LšœŸ˜LšŸ˜—š žœœœ œœ˜fMšœS™SLšœ9˜9šœ1˜8Lšœ˜šœ˜Lšœ,œ˜1Lšœ!˜(—Lšœœœ˜#Lšœ˜—M˜M˜—šž œœœœ˜=Lšœ'™'Lšœ5˜5Lšœ?˜?š œœœœ˜