<> <> <> <> <> <> <> <> <> <> << >> DIRECTORY Arpa USING [Address], ArpaIP USING [AllocBuffers, Buffers, ChecksumProc, Error, FreeBuffers, GetUserBytes, SetUserBytes, Send], List USING [Nconc1], ArpaTCP USING [Error, Port], ArpaTCPLogging USING [PrintTCPPacket], ArpaTCPOps USING [Buffer, Flip, Flop, InitialTimeoutFromHandle, maxRTT, minRTT, pktsRexmitted, pktsSent, repacketizing, RexmitIntervalFromHandle, RexmitTimeoutFromHandle, sendBufferLength, SetTimeout, TCPChecksum, TCPControlSet, TCPHandle, tcpHdrByteLength, tcpHdrWordLength, TCPHeaderP, tcpIPHandle, TCPSendBuffer], ArpaTCPTransmit, BasicTime USING [GetClockPulses, PulsesToMicroseconds]; ArpaTCPTransmitImpl: CEDAR PROGRAM IMPORTS ArpaIP, List, ArpaTCP, ArpaTCPLogging, ArpaTCPOps, BasicTime EXPORTS ArpaTCPTransmit = BEGIN OPEN ArpaTCPOps, ArpaTCPTransmit; TCPSend: PROC [data: ArpaIP.Buffers, sourcePort, dstnPort: ArpaTCP.Port, dstn: Arpa.Address] ~ { <> TRUSTED { tcpHdrPtr: TCPHeaderP _ LOOPHOLE[@data.body]; tcpHdrPtr.sourcePort _ sourcePort; tcpHdrPtr.dstnPort _ dstnPort; tcpHdrPtr.unused _ 0; tcpHdrPtr.dataWordOffset _ tcpHdrWordLength; <> }; pktsSent _ pktsSent + 1; [] _ ArpaIP.Send[ArpaTCPOps.tcpIPHandle, data, dstn, TCPChecksumCallback ! ArpaIP.Error => CONTINUE]; }; TCPChecksumCallback: ArpaIP.ChecksumProc ~ TRUSTED { tcpHdrPtr: TCPHeaderP _ LOOPHOLE[@b.body]; tcpHdrPtr.checksum _ TCPChecksum[b]; }; SendSegmentToNet: PROC [handle: TCPHandle, sendDatagram: ArpaIP.Buffers, dataSegment: BOOL] ~ TRUSTED { <> bodyBytes: CARDINAL _ ArpaIP.GetUserBytes[sendDatagram].bodyBytes - tcpHdrByteLength; tcpHdrPtr: TCPHeaderP; -- TCP header to send tcpSendBufferPtr: REF TCPSendBuffer; -- information about sent segment <> tcpHdrPtr _ LOOPHOLE[@sendDatagram.body]; tcpHdrPtr.seqNumber _ Flop[handle.sndNxt]; -- set sequence number tcpHdrPtr.window _ handle.rcvWnd; -- send latest window handle.zeroRcvWnd _ handle.rcvWnd = 0; IF ~(handle.state = listen OR handle.state = synSent) THEN { -- if in synced state tcpHdrPtr.ack _ TRUE; -- send ack tcpHdrPtr.ackNumber _ Flop[handle.rcvNxt]; } ELSE { tcpHdrPtr.ack _ FALSE; tcpHdrPtr.ackNumber _ Flop[0]; }; <> handle.sndNxt _ handle.sndNxt + bodyBytes; IF tcpHdrPtr.syn THEN -- adjust seq number for SYN and FIN handle.sndNxt _ handle.sndNxt + 1; IF tcpHdrPtr.fin THEN { handle.sndNxt _ handle.sndNxt + 1; handle.finSequence _ Flip[tcpHdrPtr.seqNumber]; }; IF handle.sndUrgent THEN { tcpHdrPtr.urg _ TRUE; tcpHdrPtr.urgentPtr _ handle.sndUp - handle.sndNxt; } ELSE { tcpHdrPtr.urg _ FALSE; tcpHdrPtr.urgentPtr _ 0; }; TCPSend[sendDatagram, handle.localPort, handle.foreignPort, handle.foreignAddr]; ArpaTCPLogging.PrintTCPPacket[handle, sendDatagram, toNet]; IF dataSegment THEN { -- if didn't send ack alone <> <> IF bodyBytes#0 OR tcpHdrPtr.fin OR tcpHdrPtr.syn THEN { tcpSendBufferPtr _ NEW[TCPSendBuffer]; tcpSendBufferPtr.dataByteCount _ bodyBytes; tcpSendBufferPtr.datagram _ sendDatagram; IF (tcpSendBufferPtr.xmitTime _ BasicTime.GetClockPulses[]) = 0 THEN tcpSendBufferPtr.xmitTime _ 1; tcpSendBufferPtr.rexmitTime _ SetTimeout[RexmitIntervalFromHandle[handle]]; tcpSendBufferPtr.timeoutTime _ IF tcpHdrPtr.syn THEN SetTimeout[InitialTimeoutFromHandle[handle]] ELSE SetTimeout[RexmitTimeoutFromHandle[handle]]; handle.rexmitQueue _ List.Nconc1[handle.rexmitQueue, tcpSendBufferPtr]; }; }; }; -- SendSegmentToNet TryToSend: PUBLIC PROC [handle: TCPHandle] = { <> sendDatagram: ArpaIP.Buffers; -- datagram to send sendData: BOOL; -- set if sending data, not ack only IF handle.state = listen OR handle.state = synSent THEN -- if in unsynced state RETURN WITH ERROR ArpaTCP.Error[handle.reason]; -- Trying to send data in unsynced state IF handle.sndWnd = 0 THEN-- in synced state, if send window is zero IF handle.rexmitQueue = NIL THEN sendData _ TRUE -- if no pkts sent, send pkt to probe window ELSE sendData _ FALSE -- if pkts sent and unacked, send ack ELSE IF handle.sndNxt >= handle.sndUna + handle.sndWnd THEN sendData _ FALSE -- if non-zero window is full, send ack ELSE sendData _ TRUE; -- else send data IF -- if can send data, but no data to send sendData AND handle.toNetQueue = NIL THEN sendData _ FALSE; -- just send an ack IF NOT sendData THEN TRUSTED { -- if sending only an ack <> tcpHdrPtr: TCPHeaderP; -- TCP header to send sendDatagram _ ArpaIP.AllocBuffers[1]; tcpHdrPtr _ LOOPHOLE[@sendDatagram.body]; tcpHdrPtr.psh _ FALSE; tcpHdrPtr.syn _ FALSE; tcpHdrPtr.fin _ FALSE; tcpHdrPtr.rst _ FALSE; IF handle.sndUrgent THEN { tcpHdrPtr.urg _ TRUE; tcpHdrPtr.urgentPtr _ handle.sndUp - handle.sndNxt; } ELSE { tcpHdrPtr.urg _ FALSE; tcpHdrPtr.urgentPtr _ 0; }; ArpaIP.SetUserBytes[sendDatagram, tcpHdrByteLength+0]; SendSegmentToNet[handle, sendDatagram, FALSE]; -- send to net ArpaIP.FreeBuffers[sendDatagram]; RETURN; -- finished trying to send }; DO -- send data until send window is full or no more data to send IF handle.toNetQueue = NIL THEN RETURN; -- out of data <> TRUSTED{sendDatagram _ LOOPHOLE[handle.toNetQueue.first]}; handle.toNetQueue _ handle.toNetQueue.rest; SendSegmentToNet[handle, sendDatagram, TRUE]; -- send segment to net IF handle.sndNxt >= handle.sndUna + handle.sndWnd THEN EXIT ENDLOOP }; -- TryToSend TryToSendData: PUBLIC PROC [handle: TCPHandle] = { <> sendData: BOOL; IF handle.state = listen OR handle.state = synSent THEN -- if in unsynced state RETURN WITH ERROR ArpaTCP.Error[handle.reason]; -- Trying to send data in unsynced state IF handle.sndWnd = 0 THEN -- if send window is zero IF handle.rexmitQueue = NIL THEN sendData _ TRUE -- if no pkts sent, send pkt to probe window ELSE sendData _ FALSE -- if pkts sent and unacked, send ack ELSE IF handle.sndNxt >= handle.sndUna + handle.sndWnd THEN sendData _ FALSE -- if non-zero window is full, send ack ELSE sendData _ TRUE; -- else send data IF sendData AND handle.toNetQueue # NIL THEN -- if allowed to send data and data to send TryToSend[handle] -- then send it }; -- TryToSendData SendSegment: PUBLIC PROC [handle: TCPHandle, sendDatagram: Buffer, dataByteCount: INT, ctl: TCPControlSet] ~ TRUSTED { <> tcpHdrPtr: TCPHeaderP _ LOOPHOLE[@sendDatagram.body]; IF repacketizing THEN { -- if repacketizing data then copy data to TCP buffer FOR i: INT IN [tcpHdrPtr.dataWordOffset*4..tcpHdrPtr.dataWordOffset*4+dataByteCount) DO handle.sendBuffer[handle.fillSlot] _ sendDatagram.body.bytes[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; ArpaIP.SetUserBytes[sendDatagram, tcpHdrByteLength+dataByteCount]; -- set data length handle.nBytesToSend _ handle.nBytesToSend + dataByteCount; handle.toNetQueue _ List.Nconc1[handle.toNetQueue, sendDatagram]; -- put buffer on queue to send IF handle.state = established OR handle.state = closeWait THEN TryToSendData[handle]; -- send it if window permits }; -- SendSegment SendSYN: PUBLIC PROC [handle: TCPHandle] = TRUSTED { <> sendDatagram: ArpaIP.Buffers _ ArpaIP.AllocBuffers[1]; tcpHdrPtr: TCPHeaderP _ LOOPHOLE[@sendDatagram.body]; tcpHdrPtr.urg _ FALSE; tcpHdrPtr.urgentPtr _ 0; tcpHdrPtr.psh _ FALSE; tcpHdrPtr.rst _ FALSE; tcpHdrPtr.syn _ FALSE; tcpHdrPtr.fin _ FALSE; tcpHdrPtr.ack _ FALSE; ArpaIP.SetUserBytes[sendDatagram, tcpHdrByteLength+0]; -- set data length tcpHdrPtr.syn _ TRUE; tcpHdrPtr.seqNumber _ Flop[handle.iss]; handle.sndNxt _ handle.iss; SendSegmentToNet[handle, sendDatagram, TRUE]; }; SendFIN: PUBLIC PROC [handle: TCPHandle] = TRUSTED { <> sendDatagram: ArpaIP.Buffers _ ArpaIP.AllocBuffers[1]; tcpHdrPtr: TCPHeaderP _ LOOPHOLE[@sendDatagram.body]; tcpHdrPtr.urg _ FALSE; tcpHdrPtr.urgentPtr _ 0; tcpHdrPtr.psh _ FALSE; tcpHdrPtr.rst _ FALSE; tcpHdrPtr.syn _ FALSE; tcpHdrPtr.fin _ TRUE; tcpHdrPtr.ack _ FALSE; ArpaIP.SetUserBytes[sendDatagram, tcpHdrByteLength+0]; handle.toNetQueue _ List.Nconc1[handle.toNetQueue, sendDatagram]; TryToSend[handle] -- try to send data and fin on to net queue }; -- SendFIN SendReset: PUBLIC PROC [handle: TCPHandle, sourcePort, dstnPort: ArpaTCP.Port, Dstn: Arpa.Address, seq, ack: INT] = TRUSTED { <> sendDatagram: ArpaIP.Buffers; -- segment to send tcpHdrPtr: TCPHeaderP; -- header to send sendDatagram _ ArpaIP.AllocBuffers[1]; ArpaIP.SetUserBytes[sendDatagram, tcpHdrByteLength+0]; <> tcpHdrPtr _ LOOPHOLE[@sendDatagram.body]; tcpHdrPtr.sourcePort _ sourcePort; tcpHdrPtr.dstnPort _ dstnPort; tcpHdrPtr.seqNumber _ Flop[seq]; tcpHdrPtr.ackNumber _ Flop[ack]; tcpHdrPtr.urg _ FALSE; tcpHdrPtr.psh _ FALSE; tcpHdrPtr.syn _ FALSE; tcpHdrPtr.fin _ FALSE; tcpHdrPtr.rst _ TRUE; IF ack # 0 THEN tcpHdrPtr.ack _ TRUE ELSE tcpHdrPtr.ack _ FALSE; tcpHdrPtr.window _ 0; tcpHdrPtr.checksum _ 0; tcpHdrPtr.urgentPtr _ 0; TCPSend[sendDatagram, sourcePort, dstnPort, Dstn]; ArpaTCPLogging.PrintTCPPacket[handle, sendDatagram, toNet]; ArpaIP.FreeBuffers[sendDatagram]; }; -- SendReset RemoveAckedSegments: PUBLIC PROC [handle: TCPHandle] ~ TRUSTED { IF handle.sndUna >= handle.sndUp THEN { handle.sndUrgent _ FALSE; handle.sndUp _ 0; }; WHILE handle.rexmitQueue # NIL DO tcpSendBufferPtr: REF TCPSendBuffer _ NARROW[handle.rexmitQueue.first]; tcpHdrPtr: TCPHeaderP _ LOOPHOLE[@tcpSendBufferPtr.datagram.body]; lastSeqNumber: INT _ Flip[tcpHdrPtr.seqNumber] + tcpSendBufferPtr.dataByteCount - 1; IF tcpHdrPtr.syn THEN lastSeqNumber _ lastSeqNumber + 1; IF tcpHdrPtr.fin THEN lastSeqNumber _ lastSeqNumber + 1; IF handle.sndUna > lastSeqNumber THEN { beNiceToGC: LIST OF REF ANY _ handle.rexmitQueue; <> IF tcpSendBufferPtr.xmitTime # 0 THEN { delta: CARD; sampleRTT: INT; delta _ BasicTime.GetClockPulses[] - tcpSendBufferPtr.xmitTime; sampleRTT _ BasicTime.PulsesToMicroseconds[delta] / 1000; sampleRTT _ MIN[MAX[minRTT, sampleRTT], maxRTT]; handle.rtt _ (7*handle.rtt + sampleRTT)/8; }; ArpaIP.FreeBuffers[tcpSendBufferPtr.datagram]; handle.rexmitQueue _ handle.rexmitQueue.rest; beNiceToGC.first _ NIL; beNiceToGC.rest _ NIL; } ELSE RETURN; -- segment at head of queue not acked ENDLOOP; }; -- RemoveAckedSegments RepacketizeandRexmit: PUBLIC PROC [handle: TCPHandle, tcpSendBufferPtr: REF TCPSendBuffer] ~ TRUSTED { <> sendDatagram: ArpaIP.Buffers; tcpHdrPtr: TCPHeaderP; i, j: INT; bytesToSend: INT; bytesRexmitted: INT; tcpSendBufferPtr.xmitTime _ 0; tcpSendBufferPtr.rexmitTime _ SetTimeout[RexmitIntervalFromHandle[handle]]; IF handle.state = synSent OR handle.state = synRcvd THEN { <> sendDatagram _ tcpSendBufferPtr.datagram; pktsRexmitted _ pktsRexmitted + 1; handle.rexmits _ handle.rexmits + 1; tcpHdrPtr _ LOOPHOLE[@sendDatagram.body]; IF NOT (handle.state = listen OR handle.state = synSent) THEN IF NOT tcpHdrPtr.ack OR Flip[tcpHdrPtr.ackNumber] # handle.rcvNxt THEN { tcpHdrPtr.ack _ TRUE; tcpHdrPtr.ackNumber _ Flop[handle.rcvNxt]; }; TCPSend[sendDatagram, handle.localPort, handle.foreignPort, handle.foreignAddr]; ArpaTCPLogging.PrintTCPPacket[handle, sendDatagram, rexmitToNet]; RETURN }; IF handle.nBytesToSend = 0 AND NOT (SELECT handle.state FROM finWait1, closing, lastAck => TRUE ENDCASE => FALSE) THEN RETURN; -- if no data or fin to send, then exit bytesToSend _ handle.nBytesToSend; j _ handle.sendSlot; bytesRexmitted _ 0; sendDatagram _ ArpaIP.AllocBuffers[1]; DO -- allocate buffers to send and fill them i _ 0; <> WHILE i # bytesToSend AND i # handle.maxSegmentSize DO sendDatagram.body.bytes[i+tcpHdrByteLength] _ handle.sendBuffer[j]; j _ (j + 1) MOD sendBufferLength; i _ i + 1 ENDLOOP; bytesToSend _ bytesToSend - i; tcpHdrPtr _ LOOPHOLE[@sendDatagram.body]; tcpHdrPtr.urg _ FALSE; tcpHdrPtr.urgentPtr _ 0; tcpHdrPtr.psh _ FALSE; tcpHdrPtr.rst _ FALSE; tcpHdrPtr.syn _ FALSE; IF handle.sndUna + i = handle.finSequence THEN tcpHdrPtr.fin _ TRUE ELSE tcpHdrPtr.fin _ FALSE; tcpHdrPtr.checksum _ 0; ArpaIP.SetUserBytes[sendDatagram, tcpHdrByteLength+i]; -- set data length tcpHdrPtr.seqNumber _ Flop[handle.sndUna + bytesRexmitted]; bytesRexmitted _ bytesRexmitted + i; -- to calculate sequence numbers tcpHdrPtr.window _ handle.rcvWnd; -- send latest window handle.zeroRcvWnd _ handle.rcvWnd = 0; tcpHdrPtr.ack _ TRUE; tcpHdrPtr.ackNumber _ Flop[handle.rcvNxt]; <> pktsRexmitted _ pktsRexmitted + 1; handle.rexmits _ handle.rexmits + 1; TCPSend[sendDatagram, handle.localPort, handle.foreignPort, handle.foreignAddr]; ArpaTCPLogging.PrintTCPPacket[handle, sendDatagram, rexmitToNet]; IF bytesToSend = 0 THEN EXIT ENDLOOP; -- end filling send buffers ArpaIP.FreeBuffers[sendDatagram]; }; -- RepacketizeandRexmit Rexmit: PUBLIC PROC [handle: TCPHandle, tcpSendBufferPtr: REF TCPSendBuffer] ~ TRUSTED { <> sendDatagram: ArpaIP.Buffers; tcpHdrPtr: TCPHeaderP; tcpSendBufferPtr.xmitTime _ 0; tcpSendBufferPtr.rexmitTime _ SetTimeout[RexmitIntervalFromHandle[handle]]; sendDatagram _ tcpSendBufferPtr.datagram; pktsRexmitted _ pktsRexmitted + 1; handle.rexmits _ handle.rexmits + 1; tcpHdrPtr _ LOOPHOLE[@sendDatagram.body]; IF NOT (handle.state = listen OR handle.state = synSent) THEN -- update ack field IF NOT tcpHdrPtr.ack OR Flip[tcpHdrPtr.ackNumber] # handle.rcvNxt THEN { tcpHdrPtr.ack _ TRUE; tcpHdrPtr.ackNumber _ Flop[handle.rcvNxt]; }; TCPSend[sendDatagram, handle.localPort, handle.foreignPort, handle.foreignAddr]; ArpaTCPLogging.PrintTCPPacket[handle, sendDatagram, rexmitToNet]; }; END.