DIRECTORY Basics USING [BITNOT], BasicTime USING [GetClockPulses, MicrosecondsToPulses, Pulses, PulsesToMicroseconds], List USING [Nconc1], Process USING [Abort, DisableTimeout, MsecToTicks, Pause, SetTimeout], IPDefs USING [Byte, CreateIPHandle, DByte, Datagram, DatagramRec, DestroyIPHandle, Error, Address, InternetHandle, nullAddress, Receive, TCPProtocol], IPOps USING [OnesComplementAddBlock], TCP USING [Error, Reason, TCPInfo, Timeout], TCPOps USING [ConnectionState, recvBufferLength, sendBufferLength, TCPControlSet, TCPHandle, tcpHdrByteLength, TCPHeaderP, TCPSendBuffer], TCPLogging USING [PrintStateChange], TCPReceiving USING [ProcessRcvdSegment], TCPStates USING [Abort, Close, CloseConnection, CopyHandleList, GetInitialSequenceNumber, Open, ValidHandle], TCPTransmit USING [Rexmit, rexmitSleepTime, RepacketizeandRexmit, SendSYN, TryToSendData]; TCPOpsImpl: CEDAR MONITOR LOCKS handle USING handle: TCPHandle IMPORTS Basics, BasicTime, IPDefs, IPOps, List, Process, TCP, TCPLogging, TCPReceiving, TCPStates, TCPTransmit EXPORTS TCPOps ~ BEGIN OPEN TCPOps; defaultReceiveWindow: PUBLIC INT _ TCPOps.recvBufferLength; repacketizing: PUBLIC BOOL _ FALSE; ourLocalAddress: PUBLIC IPDefs.Address; tcpIPHandle: IPDefs.InternetHandle; retransmitProcess: PROCESS; receiverProcess: PROCESS; 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: TCP.TCPInfo] RETURNS [handle: TCPHandle] ~ { RETURN TCPStates.Open[tcpInfo]; }; Close: PUBLIC PROC [handle: TCPHandle] ~ { TCPStates.Close[handle]; }; Abort: PUBLIC PROC [handle: TCPHandle] ~ { TCPStates.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 _ NEW[IPDefs.DatagramRec]; handle.currentOutputPtr _ tcpHdrByteLength; handle.currentOutputLimit _ handle.currentOutputPtr+handle.maxSegmentSize; }; TCPStates.ValidHandle[handle]; control.psh _ push; SELECT handle.state FROM listen => IF NOT (handle.matchForeignPort AND handle.matchForeignAddr) THEN ERROR TCP.Error[neverOpen] ELSE { handle.active _ TRUE; handle.iss _ TCPStates.GetInitialSequenceNumber[]; handle.sndUna _ handle.iss; TCPLogging.PrintStateChange[handle, synSent]; handle.state _ synSent; TCPTransmit.SendSYN[handle]; QueueSendSegment[handle, handle.currentOutputDatagram, handle.currentOutputPtr-tcpHdrByteLength, control]; GetNewOutputDatagram[]; }; synSent, synRcvd, established, closeWait => { QueueSendSegment[handle, handle.currentOutputDatagram, handle.currentOutputPtr-tcpHdrByteLength, control]; GetNewOutputDatagram[]; }; ENDCASE => ERROR TCP.Error[handle.reason]; }; GetNextDatagram: PUBLIC ENTRY PROC [handle: TCPHandle] ~ { ENABLE UNWIND => NULL; TCPStates.ValidHandle[handle]; 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 TCP.Error[handle.reason]; IF TimedOut[timeoutTime] THEN { SIGNAL TCP.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; TCPStates.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; TCPStates.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 TCP.Error[handle.reason]; }; TCPChecksum: PUBLIC PROC [data: IPDefs.Datagram] RETURNS [checksum: IPDefs.DByte] ~ TRUSTED { PseudoHeader: TYPE ~ MACHINE DEPENDENT RECORD [ sourceAddr (0): IPDefs.Address, dstnAddr (2): IPDefs.Address, zeroes (4: 0..7): IPDefs.Byte, protocol (4: 8..15): IPDefs.Byte, tcpTotalByteCount (5): IPDefs.DByte]; pseudoHeader: PseudoHeader; tcpHdrPtr: TCPHeaderP _ LOOPHOLE[@data.data]; cs: CARDINAL; pseudoHeader.sourceAddr _ data.inHdr.source; pseudoHeader.dstnAddr _ data.inHdr.destination; pseudoHeader.zeroes _ 0; pseudoHeader.protocol _ data.inHdr.protocol; pseudoHeader.tcpTotalByteCount _ data.dataLength; IF data.dataLength MOD 2 # 0 THEN data.data[data.dataLength] _ 0; cs _ IPOps.OnesComplementAddBlock[ptr: tcpHdrPtr, count: (data.dataLength+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 _ IPOps.OnesComplementAddBlock[ptr: @pseudoHeader, count: PseudoHeader.SIZE, initialSum: cs]; RETURN [Basics.BITNOT[cs]]; -- return one's complement of computed sum }; ChecksumsMatch: PUBLIC PROC [c1, c2: IPDefs.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 _ TCPStates.CopyHandleList[], l.rest WHILE l # NIL DO handle: TCPHandle _ NARROW[l.first]; CheckRexmit[handle ! TCP.Error => CONTINUE]; ENDLOOP; -- get next connection }; -- CheckRexmitQueues CheckRexmit: ENTRY PROC [handle: TCPHandle] ~ { ENABLE UNWIND => NULL; TCPStates.ValidHandle[handle]; IF handle.state = timeWait AND TimedOut[handle.timeWaitTime] THEN TCPStates.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[]; TCPStates.CloseConnection[handle, transmissionTimeout]; } ELSE IF TimedOut[tcpSendBufferPtr.rexmitTime] THEN IF repacketizing THEN -- optionally repacketize on rexmit TCPTransmit.RepacketizeandRexmit[handle, tcpSendBufferPtr] ELSE TCPTransmit.Rexmit[handle, tcpSendBufferPtr]; }; }; QueueSendSegment: INTERNAL PROC [handle: TCPHandle, sendDatagram: IPDefs.Datagram, dataByteCount: INT, ctl: TCPControlSet] ~ TRUSTED { start: BasicTime.Pulses _ BasicTime.GetClockPulses[]; timeout: INT = handle.dataTimeout; windowSize: INT; tcpHdrPtr: TCPHeaderP _ LOOPHOLE[@sendDatagram.data]; windowSize _ IF repacketizing THEN MIN[sendBufferLength, handle.sndWnd] ELSE handle.sndWnd; IF windowSize - handle.nBytesToSend < dataByteCount THEN { timeoutTime: BasicTime.Pulses _ SetTimeout[handle.dataTimeout]; DO WAIT handle.windowAvailable; SELECT handle.state FROM synSent, synRcvd, established, closeWait => NULL; ENDCASE => TCP.Error[handle.reason]; windowSize _ IF repacketizing THEN MIN[sendBufferLength, handle.sndWnd] ELSE handle.sndWnd; IF windowSize - handle.nBytesToSend >= dataByteCount THEN EXIT; IF TimedOut[timeoutTime] THEN { now: BasicTime.Pulses _ BasicTime.GetClockPulses[]; duration: LONG CARDINAL = BasicTime.PulsesToMicroseconds[now-start]; SIGNAL TCP.Timeout; timeoutTime _ SetTimeout[handle.dataTimeout]; }; ENDLOOP; }; handle.nBytesToSend _ handle.nBytesToSend + dataByteCount; IF repacketizing THEN { -- if repacketizing data FOR i: INT IN [tcpHdrPtr.dataWordOffset*4..tcpHdrPtr.dataWordOffset*4+dataByteCount) DO handle.sendBuffer[handle.fillSlot] _ sendDatagram.data[i]; handle.fillSlot _ (handle.fillSlot + 1) MOD sendBufferLength; ENDLOOP; }; IF ctl.urg THEN { -- fill in urgent control and pointer tcpHdrPtr.urg _ TRUE; tcpHdrPtr.urgentPtr _ dataByteCount } ELSE { tcpHdrPtr.urg _ FALSE; tcpHdrPtr.urgentPtr _ 0; }; tcpHdrPtr.psh _ ctl.psh; tcpHdrPtr.ack _ FALSE; tcpHdrPtr.rst _ FALSE; tcpHdrPtr.syn _ FALSE; tcpHdrPtr.fin _ FALSE; tcpHdrPtr.checksum _ 0; sendDatagram.dataLength _ dataByteCount; -- set data length handle.toNetQueue _ List.Nconc1[handle.toNetQueue, sendDatagram]; -- put buffer on queue to send IF handle.state = established OR handle.state = closeWait THEN TCPTransmit.TryToSendData[handle]; -- send it if window permits IF ctl.psh THEN { -- Wait for what we have sent to get acked 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 => TCP.Error[handle.reason]; IF TimedOut[timeoutTime] THEN { now: BasicTime.Pulses _ BasicTime.GetClockPulses[]; duration: LONG CARDINAL = BasicTime.PulsesToMicroseconds[now-start]; SIGNAL TCP.Timeout; timeoutTime _ SetTimeout[handle.dataTimeout]; }; ENDLOOP; } }; -- QueueSendSegment RetransmitProcessProc: PROC ~ { DO Process.Pause[Process.MsecToTicks[TCPTransmit.rexmitSleepTime]]; CheckRexmitQueues[]; ENDLOOP; }; ReceiverProcessProc: PROC ~ { ENABLE IPDefs.Error => GO TO GiveUp; DO data: IPDefs.Datagram _ IPDefs.Receive[tcpIPHandle]; IF data # NIL THEN TCPReceiving.ProcessRcvdSegment[data]; ENDLOOP; EXITS GiveUp => NULL; }; StartupTCP: PROC ~ { tcpIPHandle _ IPDefs.CreateIPHandle[[matchProtocol~TRUE, protocol~IPDefs.TCPProtocol, matchAddr~FALSE, address~IPDefs.nullAddress, matchLocalAddr~TRUE, localAddress~IPDefs.nullAddress]]; ourLocalAddress _ tcpIPHandle.localAddress; TRUSTED { retransmitProcess _ FORK RetransmitProcessProc[]; receiverProcess _ FORK ReceiverProcessProc[]; }; }; ShutdownTCP: PROC ~ { TRUSTED { Process.Abort[receiverProcess]; Process.Abort[retransmitProcess]; }; IPDefs.DestroyIPHandle[tcpIPHandle]; }; StartupTCP[]; END. hCopyright (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. TCPOpsImpl.mesa 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 Hal Murray May 16, 1985 3:14:24 am PDT John Larson, April 14, 1986 11:23:52 pm PST Statistics Routines used by TCPMain Attempts to open a new TCP connection. Returns a TCPHandle to use or raises TCP.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 data.dataLength is the correct length of the packet. 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]; }; This function is called when there is data from the user to send. It fills in the TCP header, queues the packet on the TCB ToNet queue and if the connection is in a state in which data can be sent, it calls TryToSendData to try to send the data. This routine is used by SendCurrentDatagram. It should be in TCPTransmit, but it does a WAIT, so it's here. Make sure there is enough buffer space for the data. then copy data to TCP buffer byte following end of packet Loops and calls CheckRexmitQueues every so often. Get the incoming datagrams for TCP. Κ – "cedar" style˜Icode2šœ©™©headšœ™J™2J™1J™-J™2Icode™&M™+J™šΟk ˜ Kšœœœ˜Kšœ œF˜UKšœœ ˜Kšœœ9˜FK˜KšœœŠ˜–Kšœœ˜%Kšœœ#˜,Kšœœ~˜ŠKšœ œ˜$Kšœ œ˜(Kšœ œ^˜mKšœ œI˜Z——š Οn œœœœœ˜>Kšœ2œ2˜nKšœ˜Kšœœœ˜Kšœœœ˜;Kšœœœœ˜#Kšœœ˜'Kšœ#˜#Kšœœ˜Kšœœ˜—™ Kšœ œœ˜Kšœ œœ˜Kšœœœ˜Kšœœœ˜Kšœœœ˜%Kšœœœ˜Kšœœœ˜Kšœœœ˜$—™š žœœœ œ œ˜HKšœ\™\Kšœ˜K˜—šžœœœ˜*K™"K˜K˜—šžœœœ˜*K™$K˜K˜—š žœœœœœ˜LK™;Kšœ˜šœœ˜Kšœ'˜'šœœ ˜Kšœ>œ˜VKšœ/˜3—š˜Kšœ˜Kšœœœ˜#šœœ`˜‚Kšœ˜—Kšœ˜—K˜—Kšœ˜—š žœœœœœ˜JK™›Kšœœœ˜Kšœ˜K˜šžœœ˜Kšœœ˜7Kšœ+˜+K˜JK˜K˜—Kšœ˜K˜šœœ˜šœ ˜ šœœœœ˜BKšœœ˜—šœ˜Kšœœ˜Kšœ3˜3Kšœ˜Kšœ.˜.Kšœ˜Kšœ˜Kšœj˜jK˜K˜——šœ-˜-Kšœj˜jK˜K˜—šœ˜ Kšœœ˜——K˜—šžœœœœ˜:K™wKšœœœ˜Kšœ˜Kšœœ˜ šœœœ˜'Kšœ?˜?šœœ˜$šœœ˜'Kšœœ˜—šœœ˜Kšœœ ˜Kšœ-˜-K˜—Kšœ˜Kšœ˜—K˜—Kšœœ ˜BKšœ7˜7K˜HK˜—šž œœœœ˜4K™GKšœœœ˜Kšœ˜šœœœ˜Kšœœ˜Kšœ3˜3K˜—K˜—š žœœœœœœ˜WK™/Kšœœœ˜Kšœ˜šœœœ˜>Kšœ˜Kšœ˜—Kšœœ˜šœ˜#Kšœ˜—š˜Kšœœ˜—K˜——™"š ž œœœœœ˜]K™oš œœœ œœ˜/Kšœ ˜ Kšœ˜Kšœ˜Kšœ"˜"Kšœ%˜%—Kšœ˜Kšœœ ˜-Kšœœ˜ K˜,K˜/K˜K˜,K˜1Kšœœœ ˜AKšœcœΟcƒ˜ƒKšœJœ˜`Kšœ œŸ*˜FK˜—š žœœœœœ˜EKš œ œ œ œ œ˜HK˜—š ž œœœœœ˜FKšœ:™:Kšœœ˜%K˜—š ž œœœ œ7œ˜xKšœό™όKš œœ œœ5œ˜YK˜—š žœœœ!œœ˜HK™4Kšœœœ)œ˜VK˜——™šžœœ˜Kšœ˜™˜Kš œœœœœ˜K˜šœ(œœ˜;Kšœœ ˜$Kšœœ œ˜,KšœŸ˜ —KšœŸ˜—šž œœœ˜/K™KKšœœœ˜Kšœ˜šœœ˜AK˜0—šœœœœ˜'Kšœœœ˜Gšœ(œŸ˜QKšœ3˜3Kšœ9˜9—šœ˜šœ'œ˜.šœœŸ#˜9Kšœ;˜;—šœ˜Kšœ-˜-———K˜—K˜—š žœœœœœ™KK™³š œœœœ™%K™ K™ K™Kšœœ™—K™—š žœœœCœœ˜†Kšœχ™χK™lKšœ5˜5Kšœ œ˜"Kšœ œ˜Kšœœ˜5K™4Kš œ œœœ"œ˜[šœ2œ˜:Kšœ?˜?š˜Kšœ˜šœ˜Kšœ,œ˜1Kšœœ˜$—Kš œ œœœ"œ˜[Kšœ3œœ˜?šœœ˜Kšœ3˜3Kšœ œœ-˜DKšœœ ˜Kšœ0˜0—Kšœ˜—K˜—K˜Kšœ;˜;K˜šœœŸ˜1Kšœ™šœœœH˜WKšœ;˜;Kšœ(œ˜>Kšœ˜—˜K˜——šœ œŸ%˜7Kšœœ˜Kšœ#˜#Kšœ˜Kšœ™—šœ˜Kšœœ˜Kšœ˜K˜—Kšœ˜Kšœœ˜Kšœœ˜Kšœœ˜Kšœœ˜Kšœ˜Kšœ)Ÿ˜Kšœ#Ÿ˜@—šœ œŸ*˜