<> <> <> <> <> <> <> << >> DIRECTORY IPDefs USING [DByte, Datagram, DatagramRec, Address, SendSpecific, TCPProtocol], List USING [Nconc1], TCP USING [Error], TCPLogging USING [PrintTCPPacket], TCPOps USING [Flip, Flop, maxTCPDataLength, ourLocalAddress, pktsRexmitted, pktsSent, sendBufferLength, SetTimeout, TCPChecksum, TCPHandle, tcpHdrByteLength, tcpHdrWordLength, TCPHeaderP, tcpSegmentLife, TCPSendBuffer], TCPTransmit; TCPTransmitImpl: CEDAR PROGRAM IMPORTS IPDefs, List, TCP, TCPLogging, TCPOps EXPORTS TCPTransmit = BEGIN OPEN TCPOps, TCPTransmit; TCPSend: PROC [handle: TCPHandle, data: IPDefs.Datagram] ~ { <> data.inHdr.timeToLive _ tcpSegmentLife; data.inHdr.protocol _ IPDefs.TCPProtocol; data.inHdr.source _ ourLocalAddress; IF handle # NIL THEN data.inHdr.destination _ handle.foreignAddr; data.optionLength _ 0; data.dataLength _ data.dataLength + tcpHdrByteLength; TRUSTED { tcpHdrPtr: TCPHeaderP _ LOOPHOLE[@data.data]; IF handle # NIL THEN { tcpHdrPtr.sourcePort _ handle.localPort; tcpHdrPtr.dstnPort _ handle.foreignPort; }; tcpHdrPtr.unused _ 0; tcpHdrPtr.dataWordOffset _ tcpHdrWordLength; tcpHdrPtr.checksum _ TCPChecksum[data]; }; pktsSent _ pktsSent + 1; IPDefs.SendSpecific[data]; data.dataLength _ data.dataLength - tcpHdrByteLength; }; SendSegmentToNet: PROC [handle: TCPHandle, sendDatagram: IPDefs.Datagram, dataSegment: BOOL] ~ TRUSTED { <> tcpHdrPtr: TCPHeaderP; -- TCP header to send tcpSendBufferPtr: REF TCPSendBuffer; -- information about sent segment <> tcpHdrPtr _ LOOPHOLE[@sendDatagram.data]; tcpHdrPtr.seqNumber _ Flop[handle.sndNxt]; -- set sequence number tcpHdrPtr.window _ handle.rcvWnd; -- send latest window 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 + sendDatagram.dataLength; 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[handle, sendDatagram]; TCPLogging.PrintTCPPacket[handle, sendDatagram, toNet]; IF dataSegment THEN { -- if didn't send ack alone <> tcpSendBufferPtr _ NEW[TCPSendBuffer]; tcpSendBufferPtr.dataByteCount _ sendDatagram.dataLength; tcpSendBufferPtr.datagram _ sendDatagram; tcpSendBufferPtr.rexmitTime _ SetTimeout[rexmitInterval]; tcpSendBufferPtr.timeoutTime _ SetTimeout[rexmitTimeout]; IF tcpHdrPtr.syn THEN tcpSendBufferPtr.timeoutTime _ SetTimeout[initialTimeout]; handle.rexmitQueue _ List.Nconc1[handle.rexmitQueue, tcpSendBufferPtr]; }; }; -- SendSegmentToNet TryToSend: PUBLIC PROC [handle: TCPHandle] = { <> sendDatagram: IPDefs.Datagram; -- 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 TCP.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 _ NEW[IPDefs.DatagramRec]; tcpHdrPtr _ LOOPHOLE[@sendDatagram.data]; 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; }; sendDatagram.dataLength _ 0; SendSegmentToNet[handle, sendDatagram, FALSE]; -- send to net 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 sendDatagram _ NARROW[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 TCP.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 SendSYN: PUBLIC PROC [handle: TCPHandle] = TRUSTED { <> sendDatagram: IPDefs.Datagram _ NEW[IPDefs.DatagramRec]; tcpHdrPtr: TCPHeaderP _ LOOPHOLE[@sendDatagram.data]; tcpHdrPtr.urg _ FALSE; tcpHdrPtr.urgentPtr _ 0; tcpHdrPtr.psh _ FALSE; tcpHdrPtr.rst _ FALSE; tcpHdrPtr.syn _ FALSE; tcpHdrPtr.fin _ FALSE; tcpHdrPtr.ack _ FALSE; sendDatagram.dataLength _ 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: IPDefs.Datagram _ NEW[IPDefs.DatagramRec]; tcpHdrPtr: TCPHeaderP _ LOOPHOLE[@sendDatagram.data]; tcpHdrPtr.urg _ FALSE; tcpHdrPtr.urgentPtr _ 0; tcpHdrPtr.psh _ FALSE; tcpHdrPtr.rst _ FALSE; tcpHdrPtr.syn _ FALSE; tcpHdrPtr.fin _ TRUE; tcpHdrPtr.ack _ FALSE; sendDatagram.dataLength _ 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: IPDefs.DByte, Dstn: IPDefs.Address, seq, ack: INT] = TRUSTED { <> sendDatagram: IPDefs.Datagram; -- segment to send tcpHdrPtr: TCPHeaderP; -- header to send sendDatagram _ NEW[IPDefs.DatagramRec]; sendDatagram.dataLength _ 0; sendDatagram.inHdr.destination _ Dstn; tcpHdrPtr _ LOOPHOLE[@sendDatagram.data]; 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[handle, sendDatagram]; TCPLogging.PrintTCPPacket[handle, sendDatagram, toNet]; }; -- 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.data]; 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; 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: IPDefs.Datagram; tcpHdrPtr: TCPHeaderP; i, j: INT; bytesToSend: INT; bytesRexmitted: INT; tcpSendBufferPtr.rexmitTime _ SetTimeout[rexmitInterval]; IF handle.state = synSent OR handle.state = synRcvd THEN { <> sendDatagram _ tcpSendBufferPtr.datagram; pktsRexmitted _ pktsRexmitted + 1; tcpHdrPtr _ LOOPHOLE[@sendDatagram.data]; 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[handle, sendDatagram]; TCPLogging.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; DO -- allocate buffers to send and fill them sendDatagram _ NEW[IPDefs.DatagramRec]; i _ 0; <> WHILE i # bytesToSend AND i # maxTCPDataLength DO sendDatagram.data[i+tcpHdrByteLength] _ handle.sendBuffer[j]; j _ (j + 1) MOD sendBufferLength; i _ i + 1 ENDLOOP; bytesToSend _ bytesToSend - i; tcpHdrPtr _ LOOPHOLE[@sendDatagram.data]; 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; sendDatagram^.dataLength _ i; -- set data length tcpHdrPtr.seqNumber _ Flop[handle.sndUna + bytesRexmitted]; bytesRexmitted _ bytesRexmitted + i; -- to calculate sequence numbers tcpHdrPtr.window _ handle.rcvWnd; -- send latest window tcpHdrPtr.ack _ TRUE; tcpHdrPtr.ackNumber _ Flop[handle.rcvNxt]; <> pktsRexmitted _ pktsRexmitted + 1; TCPSend[handle, sendDatagram]; TCPLogging.PrintTCPPacket[handle, sendDatagram, rexmitToNet]; IF bytesToSend = 0 THEN EXIT ENDLOOP -- end filling send buffers }; -- RepacketizeandRexmit Rexmit: PUBLIC PROC [handle: TCPHandle, tcpSendBufferPtr: REF TCPSendBuffer] ~ TRUSTED { <> sendDatagram: IPDefs.Datagram; tcpHdrPtr: TCPHeaderP; tcpSendBufferPtr.rexmitTime _ SetTimeout[rexmitInterval]; sendDatagram _ tcpSendBufferPtr.datagram; pktsRexmitted _ pktsRexmitted + 1; tcpHdrPtr _ LOOPHOLE[@sendDatagram.data]; 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[handle, sendDatagram]; TCPLogging.PrintTCPPacket[handle, sendDatagram, rexmitToNet]; }; END.