This procedure finishes the processing of a datagram received from the network:
It checks the sequence number and if it is outside the window,
it sends an ack and disposes of the packet received.
It processes resets; if the packet contains a reset, processing is finished.
It processes acks.
It processes urgents.
It queues the segment on the TCB FromNet queue in sequence number order.
It processes packets on the FromNet Queue until the next expected sequence number is not present:
It processes SYNs.
It processes data and sends it to the user.
It processes FINs.
ProcessAck:
INTERNAL
PROC = {
nDataBytesAcked: INT;
SELECT handle.state
FROM
listen => {
-- no acks are acceptable, send reset
TRUSTED {ArpaTCPTransmit.SendReset[handle, handle.localPort, tcpHdrPtr.sourcePort, rcvdDatagram.hdr1.source, ackNumber, 0]};
freeSegment ← TRUE;
continueProcessing ← FALSE;
RETURN;
};
synRcvd, synSent =>
IF ackNumber - handle.iss <= 0
OR ackNumber - handle.sndNxt > 0
THEN {
-- if unacceptable ack, then send reset
TRUSTED {ArpaTCPTransmit.SendReset[handle, handle.localPort, handle.foreignPort, handle.foreignAddr, ackNumber, 0]};
freeSegment ← TRUE;
continueProcessing ← FALSE;
RETURN;
}
ENDCASE;
IF ackNumber - handle.sndNxt > 0
THEN {
-- ack from future send an ack and discard the packet
sendAck ← TRUE;
freeSegment ← TRUE;
continueProcessing ← FALSE;
RETURN;
};
acceptable ack
IF (handle.sndWL1 - seqNumber < 0
OR handle.sndWL1 = seqNumber
AND handle.sndWL2 - ackNumber <= 0)
OR (handle.sndWL1 = 0
AND handle.sndWL2 = 0)
THEN {
if packet has new sequence number or most recent sequence number and new ack then update the window (also update the window if we've never set it before. It it fairly unlikely that sndWL1 and sndWL2 will both go to zero at the same time and even if they do it won't cause much harm.).
TRUSTED {handle.sndWnd ← MIN[tcpHdrPtr.window, ArpaTCPOps.sendBufferLength]};
handle.sndWL1 ← seqNumber;
handle.sndWL2 ← ackNumber;
};
IF ackNumber - handle.sndUna <= 0
THEN
-- duplicate ack
RETURN; -- ignore ack
calculate number of data bytes acked, i.e. not including SYN and FIN, and number of unacked bytes to send or rexmit
nDataBytesAcked ← ackNumber - handle.sndUna;
IF handle.sndUna = handle.iss
AND nDataBytesAcked # 0
THEN
nDataBytesAcked ← nDataBytesAcked - 1;
IF (
SELECT handle.state
FROM
finWait1, finWait2, closing, lastAck, timeWait =>
TRUE
ENDCASE => FALSE) AND
ackNumber - handle.finSequence >= 0 AND
nDataBytesAcked # 0 THEN
nDataBytesAcked ← nDataBytesAcked - 1;
IF handle.nBytesToSend >= nDataBytesAcked
THEN
-- Occasional BoundsFault
handle.nBytesToSend ← handle.nBytesToSend - nDataBytesAcked;
IF repacketizing
THEN
-- if repacketizing, update send ptr in TCP buffer
handle.sendSlot ← (handle.sendSlot + nDataBytesAcked)
MOD sendBufferLength;
TRUSTED {handle.sndUna ← Flip[tcpHdrPtr.ackNumber]}; -- update ack info in handle
ArpaTCPTransmit.RemoveAckedSegments[handle]; -- dispose of acked segments or return to user
NOTIFY handle.windowAvailable; -- tell waiters that there may be some room now
SELECT handle.state
FROM
synRcvd => {
ArpaTCPLogging.PrintStateChange[handle, established];
handle.state ← established;
IF handle.urgentMode
THEN
-- if rcvd urgent when in synrcvd state
SignalUserUrgent[]; -- then tell user now
sendData ← TRUE;
};
established => sendData ← TRUE;
finWait1 =>
IF ackNumber - handle.finSequence > 0
THEN {
-- if our fin acked, then state is finwait2
ArpaTCPLogging.PrintStateChange[handle, finWait2];
handle.state ← finWait2;
no data expected in following states, finished processing
closeWait => {
continueProcessing ← FALSE;
freeSegment ← TRUE;
sendData ← TRUE; -- try to send more data
};
closing => {
IF ackNumber > handle.finSequence
THEN {
-- if fin acked, state is timewait
ArpaTCPLogging.PrintStateChange[handle, timeWait];
handle.state ← timeWait;
handle.timeWaitTime ← SetTimeout[tcpSegmentLife*1000*2]; -- tcpSegmentLife is in seconds; SetTimeout takes milliseconds; *2 is for round-trip
};
continueProcessing ← FALSE;
freeSegment ← TRUE;
};
lastAck => {
IF ackNumber > handle.finSequence
THEN {
-- close connection if fin acked
ArpaTCPStates.CloseConnection[handle, handle.reason];
connectionClosed ← TRUE
}
continueProcessing ← FALSE
}
ENDCASE
}; -- ProcessAck
TRUSTED {
tcpHdrPtr ← LOOPHOLE[@rcvdDatagram.body];
ackNumber ← Flip[tcpHdrPtr.ackNumber];
seqNumber ← Flip[tcpHdrPtr.seqNumber];
nSeqBytes ← ArpaIP.GetUserBytes[rcvdDatagram].bodyBytes - tcpHdrPtr.dataWordOffset * 4;
IF ~(handle.state = listen
OR handle.state = synSent)
THEN {
seqResult ← CheckSequenceNumber[seqNumber, nSeqBytes, handle.rcvNxt, handle.rcvWnd];
IF seqResult # okSeq
THEN {
SELECT seqResult
FROM
pastSeq => {
ArpaTCPLogging.PrintMessageWithSrc["Packet from past", handle.foreignAddr];
pktsFromPast ← pktsFromPast + 1; };
futureSeq => {
ArpaTCPLogging.PrintMessageWithSrc["Future packet", handle.foreignAddr];
pktsFromFuture ← pktsFromFuture + 1; };
ENDCASE => ERROR;
IF ~tcpHdrPtr.rst THEN ArpaTCPTransmit.TryToSend[handle]; -- ~reset => send an ack
RETURN; }; };
IF tcpHdrPtr.syn THEN nSeqBytes ← nSeqBytes + 1;
IF tcpHdrPtr.fin THEN nSeqBytes ← nSeqBytes + 1;
initialize flags used in processing segment
continueProcessing ← TRUE; -- more info in packet to process
freeSegment ← FALSE; -- don't dispose of packet
sendAck ← FALSE; -- set to send an ack
sendData ← FALSE; -- set to try to send more data
connectionClosed ← FALSE; -- set if the connection closes
IF tcpHdrPtr.rst
THEN {
-- if its a reset, process it and exit
ProcessReset[];
RETURN; };
IF tcpHdrPtr.psh
THEN {
-- if its a push, process it
sendAck ← TRUE; -- at least ack a push since it is legit to push zero bytes
What else shoud we do with a push?
};
IF tcpHdrPtr.ack
THEN {
-- if its an ack, process it
ProcessAck[];
IF connectionClosed THEN RETURN;
IF nSeqBytes = 0
AND handle.zeroRcvWnd
THEN
-- received probe
sendAck ← TRUE; -- this might reopen the window
IF freeSegment OR (nSeqBytes = 0 AND ~tcpHdrPtr.urg) OR ~continueProcessing THEN GOTO SendAndExit; };
IF tcpHdrPtr.urg
THEN
-- if in state where data is acceptable, process urgent
SELECT handle.state
FROM
synRcvd, established, finWait1, finWait2 => ProcessUrgent[];
ENDCASE => NULL;
IF nSeqBytes > handle.rcvWnd
THEN {
SU-AI used to do something that provoked a problem way downstream.
Truncating is slightly bogus if SendDataToUser is going to chop off part of the front
SIGNAL PacketTooBig; -- HGM want's to look at one
ArpaTCPLogging.PrintMessageWithSrc["Packet too big", handle.foreignAddr];
nSeqBytes ← handle.rcvWnd; };
put segment in sequence number order on queue of segments to be processed
tcpRcvBufferPtr ← NEW[TCPRcvBuffer]; -- set up receive info block
tcpRcvBufferPtr.datagramPtr ← rcvdDatagram;
tcpRcvBufferPtr.dataByteCount ← nSeqBytes;
tcpRcvBufferPtr.dataOffset ← tcpHdrPtr.dataWordOffset * 4;
tcpRcvBufferPtr.offsetSeqNo ← Flip[tcpHdrPtr.seqNumber];
tcpRcvBufferPtr.tcpHdrPtr ← tcpHdrPtr;
QueueRcvdSegment[handle, tcpRcvBufferPtr]; -- put on handle fromNet queue
DO
process SYN, data, and FIN, repeat until FromNetQueue is empty or next expected sequence number has not been received
IF handle.fromNetQueue = NIL THEN GOTO SendAndExit;
tcpRcvBufferPtr ← NARROW[handle.fromNetQueue.first];
IF ~(handle.state = listen
OR handle.state = synSent)
THEN
-- if in synced state and
IF tcpRcvBufferPtr.offsetSeqNo # handle.rcvNxt
THEN
-- dont have next seq
GOTO SendAndExit ;
tcpHdrPtr ← tcpRcvBufferPtr.tcpHdrPtr;
ackNumber ← Flip[tcpHdrPtr.ackNumber];
seqNumber ← Flip[tcpHdrPtr.seqNumber];
process SYN
IF tcpHdrPtr.syn
THEN {
ProcessSyn[];
IF connectionClosed THEN RETURN;
IF freeSegment
OR tcpRcvBufferPtr.dataByteCount = 0
THEN {
DisposeRcvdBuffer[];
IF continueProcessing
THEN
-- dispose of processed or empty packets
LOOP; -- get next packet to process
};
IF ~continueProcessing THEN GOTO SendAndExit; };
IF handle.state = listen
OR handle.state = synSent
THEN {
-- can't do anything more in these states
DisposeRcvdBuffer[];
GOTO SendAndExit; };
IF ~tcpHdrPtr.ack
THEN {
-- ignore segments without acks (why? -DN)
DisposeRcvdBuffer[];
LOOP; }; -- get next packet to process
process data, send buffer to user, dispose of buffer if no data
handle.rcvNxt ← handle.rcvNxt + tcpRcvBufferPtr.dataByteCount;
IF tcpHdrPtr.fin
THEN {
-- remember if fin is set as buffer is sent to user
finFlag ← TRUE;
tcpRcvBufferPtr.dataByteCount ← tcpRcvBufferPtr.dataByteCount - 1; }
ELSE finFlag ← FALSE;
IF tcpRcvBufferPtr.dataByteCount > 0
THEN {
-- if there's data then send it to the user
handle.fromNetQueue ← handle.fromNetQueue.rest;
SendDataToUser[handle, tcpRcvBufferPtr]; -- send to user and update window
sendAck ← TRUE; } -- remember to send an ack
ELSE {
DisposeRcvdBuffer[]; -- if no data dispose of buffer
IF finFlag THEN sendAck ← TRUE; };
process fin
IF finFlag
THEN ProcessFin[];
ENDLOOP;
EXITS
-- repeat processing each segment received
SendAndExit =>
SELECT
TRUE
FROM
sendAck => ArpaTCPTransmit.TryToSend[handle ! ArpaTCP.Error => CONTINUE] ;-- send ack and data
sendData => ArpaTCPTransmit.TryToSendData[handle ! ArpaTCP.Error => CONTINUE]; -- send only if data to send
ENDCASE;
};