DIRECTORY List USING [Nconc1], IPDefs USING [Datagram, DByte], TCPLogging USING [Direction, PrintMessage, PrintStateChange, PrintTCPPacket], TCPOps USING [ChecksumsMatch, Flip, pktsDuplicate, pktsFromFuture, pktsFromPast, pktsRcvd, pktsWithNoConnection, pktsWithBadChecksum, repacketizing, sendBufferLength, SetTimeout, TCPChecksum, TCPHandle, TCPHeaderP, TCPRcvBuffer, tcpSegmentLife], TCPReceiving USING [], TCPStates USING [CloseConnection, FindHandle, GetInitialSequenceNumber], TCPTransmit USING [RemoveAckedSegments, SendReset, SendSYN, TryToSend, TryToSendData]; TCPReceivingImpl: CEDAR MONITOR LOCKS handle USING handle: TCPHandle IMPORTS List, TCPLogging, TCPOps, TCPStates, TCPTransmit EXPORTS TCPReceiving = BEGIN OPEN TCPOps, TCPReceiving; SeqCode: TYPE = {pastSeq, okSeq, futureSeq}; ProcessRcvdSegment: PUBLIC PROC [rcvdDatagram: IPDefs.Datagram] = TRUSTED { tcpHdrPtr: TCPHeaderP; nSeqBytes: INT; handle: TCPHandle; checksum: IPDefs.DByte _ TCPOps.TCPChecksum[rcvdDatagram]; pktsRcvd _ pktsRcvd + 1; tcpHdrPtr _ LOOPHOLE[@rcvdDatagram.data]; IF ~TCPOps.ChecksumsMatch[checksum, tcpHdrPtr.checksum] THEN { pktsWithBadChecksum _ pktsWithBadChecksum + 1; TCPLogging.PrintMessage["Bad checksum"]; RETURN; }; nSeqBytes _ rcvdDatagram.dataLength - tcpHdrPtr.dataWordOffset * 4; IF tcpHdrPtr.syn THEN nSeqBytes _ nSeqBytes + 1; IF tcpHdrPtr.fin THEN nSeqBytes _ nSeqBytes + 1; handle _ TCPStates.FindHandle[rcvdDatagram]; TCPLogging.PrintTCPPacket[handle, rcvdDatagram, fromNet]; IF handle = NIL THEN { IF ~tcpHdrPtr.rst THEN -- if no matching TCB IF tcpHdrPtr.ack THEN -- if not a reset, then send a reset TCPTransmit.SendReset[NIL, tcpHdrPtr.dstnPort, tcpHdrPtr.sourcePort, rcvdDatagram.inHdr.source, Flip[tcpHdrPtr.ackNumber], 0] -- with no ack ELSE TCPTransmit.SendReset[NIL, tcpHdrPtr.dstnPort, tcpHdrPtr.sourcePort, rcvdDatagram.inHdr.source, 0, Flip[tcpHdrPtr.seqNumber] + nSeqBytes]; -- with ack pktsWithNoConnection _ pktsWithNoConnection + 1; TCPLogging.PrintMessage["No connection"]; RETURN; }; ContinueProcessingRcvdSegment[handle, rcvdDatagram]; }; ContinueProcessingRcvdSegment: ENTRY PROC [handle: TCPHandle, rcvdDatagram: IPDefs.Datagram] = { ENABLE UNWIND => NULL; tcpRcvBufferPtr: REF TCPRcvBuffer; -- info block for rcvd packet tcpHdrPtr: TCPHeaderP; -- pointer to tcp header ackNumber, seqNumber: INT; -- from tcpHdrPtr, Flipped for us seqResult: SeqCode; -- result of seq number check nSeqBytes: INT; -- number of data bytes + syn, fin continueProcessing: BOOL; -- true if more processing to do freeSegment: BOOL; -- true to dispose of segment finFlag: BOOL; -- true if segment contains fin sendAck: BOOL; -- set to send ack sendData: BOOL; -- set to send data connectionClosed: BOOL; -- set when close connection so that its not referenced again DisposeRcvdBuffer: PROC = { -- dispose of pkts on fromNet queue IF handle.fromNetQueue # NIL THEN handle.fromNetQueue _ handle.fromNetQueue.rest; }; ProcessReset: PROC = { SELECT handle.state FROM listen => freeSegment _ TRUE; -- ignore the reset synSent => TRUSTED { -- if segment has acceptable ack, close connection IF tcpHdrPtr.ack AND ackNumber-handle.iss > 0 AND ackNumber-handle.sndNxt <= 0 THEN { TCPStates.CloseConnection[handle, remoteAbort]; connectionClosed _ TRUE; }; }; synRcvd => -- if were listening, return to listen else reset IF ~handle.active THEN { TCPLogging.PrintStateChange[handle, listen]; handle.state _ listen; } ELSE { TCPStates.CloseConnection[handle, remoteAbort]; connectionClosed _ TRUE; }; established, finWait1, finWait2, closeWait, closing, lastAck, timeWait => { TCPStates.CloseConnection[handle, remoteAbort]; connectionClosed _ TRUE; }; ENDCASE; }; SignalUserUrgent: INTERNAL PROC = { NOTIFY handle.urgentAvailable; }; ProcessUrgent: INTERNAL PROC = TRUSTED { IF ~handle.urgentMode THEN { handle.urgentMode _ TRUE; handle.rcvUp _ tcpHdrPtr.urgentPtr + seqNumber; IF handle.state = established OR handle.state = finWait1 OR handle.state = finWait2 THEN SignalUserUrgent[] } ELSE -- else update urgent pointer IF handle.rcvUp < tcpHdrPtr.urgentPtr + seqNumber THEN handle.rcvUp _ tcpHdrPtr.urgentPtr + seqNumber; }; ProcessAck: INTERNAL PROC = { nDataBytesAcked: INT; SELECT handle.state FROM listen => {-- no acks are acceptable, send reset TRUSTED {TCPTransmit.SendReset[handle, handle.localPort, tcpHdrPtr.sourcePort, rcvdDatagram.inHdr.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 {TCPTransmit.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; }; IF (handle.sndWL1 - seqNumber < 0 OR handle.sndWL1 = seqNumber AND handle.sndWL2 - ackNumber <= 0) OR (handle.sndWL1 = 0 AND handle.sndWL2 = 0) THEN { TRUSTED {handle.sndWnd _ MIN[tcpHdrPtr.window, TCPOps.sendBufferLength]}; handle.sndWL1 _ seqNumber; handle.sndWL2 _ ackNumber; }; IF ackNumber - handle.sndUna <= 0 THEN -- duplicate ack RETURN; -- ignore ack 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 TCPTransmit.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 => { TCPLogging.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 TCPLogging.PrintStateChange[handle, finWait2]; handle.state _ finWait2; }; closeWait => { continueProcessing _ FALSE; freeSegment _ TRUE; sendData _ TRUE; -- try to send more data }; closing => { IF ackNumber > handle.finSequence THEN { -- if fin acked, state is timewait TCPLogging.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 TCPStates.CloseConnection[handle, handle.reason]; connectionClosed _ TRUE } ELSE freeSegment _ TRUE; continueProcessing _ FALSE } ENDCASE }; -- ProcessAck ProcessSyn: INTERNAL PROC = { SELECT handle.state FROM listen => { TCPLogging.PrintStateChange[handle, synRcvd]; handle.state _ synRcvd; handle.irs _ seqNumber; handle.rcvNxt _ seqNumber + 1; handle.iss _ TCPStates.GetInitialSequenceNumber[]; handle.sndUna _ handle.iss; NOTIFY handle.notListening; handle.matchForeignAddr _ TRUE; handle.matchForeignPort _ TRUE; handle.foreignAddr _ tcpRcvBufferPtr.datagramPtr.inHdr.source; TRUSTED {handle.foreignPort _ tcpHdrPtr.sourcePort}; TCPTransmit.SendSYN[handle]; -- send a syn and ack tcpRcvBufferPtr.dataByteCount _ tcpRcvBufferPtr.dataByteCount - 1; tcpRcvBufferPtr.offsetSeqNo _ tcpRcvBufferPtr.offsetSeqNo + 1; TRUSTED {tcpHdrPtr.syn _ FALSE}; -- don't reprocess syn continueProcessing _ FALSE; -- don't process data or fin until in established state }; synSent => { handle.irs _ seqNumber; -- initial rcv sequence handle.rcvNxt _ seqNumber + 1; tcpRcvBufferPtr.dataByteCount _ tcpRcvBufferPtr.dataByteCount - 1; tcpRcvBufferPtr.offsetSeqNo _ tcpRcvBufferPtr.offsetSeqNo + 1; IF handle.sndUna > handle.iss THEN { -- if our syn was acked then conn. established TCPLogging.PrintStateChange[handle, established]; handle.state _ established; } ELSE { -- if our syn not acked, state becomes synRcvd TCPLogging.PrintStateChange[handle, synRcvd]; handle.state _ synRcvd; continueProcessing _ FALSE; -- wait till in established state }; TRUSTED {tcpHdrPtr.syn _ FALSE}; -- don't reprocess syn sendAck _ TRUE; -- ack the syn received }; synRcvd => { TCPLogging.PrintStateChange[handle, listen]; handle.state _ listen; -- return to listen (why? -DN) continueProcessing _ FALSE; -- stop processing freeSegment _ TRUE; -- dispose of buffer } ENDCASE => { -- in any other state, reset connection TCPStates.CloseConnection[handle, protocolViolation]; connectionClosed _ TRUE; } }; -- ProcessSyn ProcessFin: INTERNAL PROC = { SELECT handle.state FROM synRcvd, established => { TCPLogging.PrintStateChange[handle, closeWait]; handle.state _ closeWait; handle.reason _ remoteClose; }; finWait1 => { TCPLogging.PrintStateChange[handle, closing]; handle.state _ closing; }; finWait2 => { TCPLogging.PrintStateChange[handle, timeWait]; handle.state _ timeWait; handle.timeWaitTime _ SetTimeout[tcpSegmentLife*1000*2]; -- tcpSegmentLife is in seconds; SetTimeout takes milliseconds; *2 is for round-trip }; ENDCASE; NOTIFY handle.dataAvailable; -- tell the user that that data is never coming now. }; -- ProcessFin TRUSTED { tcpHdrPtr _ LOOPHOLE[@rcvdDatagram.data]; ackNumber _ Flip[tcpHdrPtr.ackNumber]; seqNumber _ Flip[tcpHdrPtr.seqNumber]; nSeqBytes _ rcvdDatagram.dataLength - 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 => { TCPLogging.PrintMessage["Duplicate packet"]; pktsFromPast _ pktsFromPast + 1; }; futureSeq => { TCPLogging.PrintMessage["Future packet"]; pktsFromFuture _ pktsFromFuture + 1; }; ENDCASE => ERROR; IF ~tcpHdrPtr.rst THEN TCPTransmit.TryToSend[handle]; -- ~reset => send an ack RETURN; }; }; IF tcpHdrPtr.syn THEN nSeqBytes _ nSeqBytes + 1; IF tcpHdrPtr.fin THEN nSeqBytes _ nSeqBytes + 1; 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.ack THEN { -- if its an ack, process it ProcessAck[]; IF connectionClosed THEN RETURN; 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 { SIGNAL PacketTooBig; -- HGM want's to look at one TCPLogging.PrintMessage["Packet too big"]; nSeqBytes _ handle.rcvWnd; }; 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 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]; 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 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; }; IF finFlag THEN ProcessFin[]; ENDLOOP; EXITS -- repeat processing each segment received SendAndExit => SELECT TRUE FROM sendAck => TCPTransmit.TryToSend[handle] ;-- send ack and data sendData => TCPTransmit.TryToSendData[handle]; -- send only if data to send ENDCASE; }; }; -- ProcessRcvdSegment CheckSequenceNumber: INTERNAL PROC [seq, segLen, rcvNxt, rcvWnd: INT] RETURNS [result: SeqCode] = { rcvEnd: INT _ rcvWnd + rcvNxt; -- rcvwnd is rcvNxt to rcvEnd-1 inclusive segEnd: INT _ seq + segLen - 1; -- segment is seq to segEnd inclusive IF segLen = 0 AND rcvWnd = 0 THEN IF seq = rcvNxt THEN result _ okSeq ELSE IF seq - rcvNxt > 0 THEN result _ futureSeq ELSE result _ pastSeq ELSE IF segLen = 0 AND rcvWnd > 0 THEN IF seq - rcvNxt < 0 THEN result _ pastSeq ELSE IF seq - rcvEnd >= 0 THEN result _ futureSeq ELSE result _ okSeq ELSE IF rcvWnd = 0 THEN IF seq >= rcvNxt THEN result _ futureSeq ELSE result _ pastSeq ELSE IF segEnd < rcvNxt THEN result _ pastSeq ELSE IF seq >= rcvEnd THEN result _ futureSeq ELSE result _ okSeq }; -- CheckSequenceNumber QueueRcvdSegment: INTERNAL PROC [handle: TCPHandle, newBufferPtr: REF TCPRcvBuffer] = { newStart: INT; -- first seq number in new segment newEnd: INT; -- last seq number in new segment oldStart: INT; -- first seq number in segment on queue oldEnd: INT; -- last seq number in segment on queue oldBufferPtr: REF TCPRcvBuffer; -- buffer on from net queue l, prevL: LIST OF REF ANY; -- point to the cons cells as we walk the list IF ~(handle.state = listen OR handle.state = synSent) THEN -- if in synced state IF newBufferPtr.offsetSeqNo < handle.rcvNxt THEN { newBufferPtr.dataByteCount _ newBufferPtr.dataByteCount - (handle.rcvNxt - newBufferPtr.offsetSeqNo); newBufferPtr.dataOffset _ newBufferPtr.dataOffset + (handle.rcvNxt - newBufferPtr.offsetSeqNo); newBufferPtr.offsetSeqNo _ handle.rcvNxt; }; IF handle.fromNetQueue = NIL THEN { handle.fromNetQueue _ LIST[newBufferPtr]; RETURN; }; prevL _ NIL; l _ handle.fromNetQueue; newStart _ newBufferPtr.offsetSeqNo; newEnd _ newStart + newBufferPtr.dataByteCount - 1; WHILE l # NIL DO oldBufferPtr _ NARROW[l.first]; oldStart _ oldBufferPtr.offsetSeqNo; oldEnd _ oldStart + oldBufferPtr.dataByteCount - 1; SELECT TRUE FROM newStart < oldStart AND newEnd < oldStart => { -- new segment is before old one IF prevL = NIL THEN handle.fromNetQueue _ CONS[newBufferPtr, handle.fromNetQueue] ELSE prevL.rest _ CONS[newBufferPtr, prevL.rest]; RETURN; }; newStart > oldEnd => NULL; -- new segment is after old one, keep going newStart >= oldStart AND newEnd <= oldEnd => { pktsDuplicate _ pktsDuplicate + 1; RETURN; }; newStart <= oldStart AND newEnd >= oldEnd => { pktsDuplicate _ pktsDuplicate + 1; oldBufferPtr.datagramPtr _ NIL; IF prevL = NIL THEN handle.fromNetQueue _ l _ l.rest ELSE prevL.rest _ l _ l.rest; IF l = NIL THEN { -- if queue is now empty, just enqueue new buffer IF prevL = NIL THEN handle.fromNetQueue _ LIST[newBufferPtr] ELSE prevL.rest _ LIST[newBufferPtr]; RETURN; } ELSE LOOP; }; -- else continue searching queue newEnd < oldEnd => { -- tail of new overlaps old newBufferPtr.dataByteCount _ oldStart - newStart; IF prevL = NIL THEN handle.fromNetQueue _ CONS[newBufferPtr, handle.fromNetQueue] ELSE prevL.rest _ CONS[newBufferPtr, prevL.rest]; RETURN }; ENDCASE => { -- head of new overlaps old newStart _ oldEnd + 1; newBufferPtr.offsetSeqNo _ newStart; newBufferPtr.dataByteCount _ newEnd - oldEnd; newBufferPtr.dataOffset _ newBufferPtr.dataOffset + oldEnd - newStart + 1; }; prevL _ l; l _ l.rest; ENDLOOP; -- check for more overlapping segments -- IF prevL = NIL THEN handle.fromNetQueue _ LIST[newBufferPtr] ELSE prevL.rest _ LIST[newBufferPtr]; }; -- QueueRcvdSegment PacketTooBig: SIGNAL = CODE; NegativeWindow: SIGNAL = CODE; SendDataToUser: INTERNAL PROC [handle: TCPOps.TCPHandle, tcpRcvBufferPtr: REF TCPRcvBuffer] = TRUSTED { IF tcpRcvBufferPtr.tcpHdrPtr.urg THEN { tcpRcvBufferPtr.urg _ TRUE; tcpRcvBufferPtr.endUrgentData _ handle.rcvUp - tcpRcvBufferPtr.offsetSeqNo; handle.urgentMode _ FALSE} ELSE tcpRcvBufferPtr.urg _ FALSE; IF tcpRcvBufferPtr.dataByteCount > handle.rcvWnd THEN { SIGNAL PacketTooBig; -- HGM want's to look at one TCPLogging.PrintMessage["Packet too big."]; tcpRcvBufferPtr.dataByteCount _ handle.rcvWnd; }; handle.rcvWnd _ handle.rcvWnd - tcpRcvBufferPtr.dataByteCount; IF handle.rcvWnd < 0 THEN SIGNAL NegativeWindow; handle.readyToReadQueue _ List.Nconc1[handle.readyToReadQueue, tcpRcvBufferPtr]; NOTIFY handle.dataAvailable; }; END. lCopyright (C) 1983, 1985 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. TCPReceivingImpl.mesa Last Edited by: Nichols, September 1, 1983 4:25 pm Last Edited by: Taft, January 4, 1984 11:13 am Last Edited by: HGM, February 23, 1985 11:32:59 pm PST Hal Murray July 16, 1985 4:52:04 pm PDT John Larson, June 16, 1986 9:10:34 pm PDT This procedure does the processing of a datagram received from the network: It checks the checksum and if it is bad disposes of the packet received. It checks for a matching handle and if none is found, it sends a reset and disposes of the packet received. If it finds a handle, it calls ContinueProcessingRcvdSegment to process the segment with the handle lock held. check checksum find matching connection 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. not in urgent mode yet; set urgent ptr and signal user acceptable ack 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.). calculate number of data bytes acked, i.e. not including SYN and FIN, and number of unacked bytes to send or rexmit no data expected in following states, finished processing begin ContinueProcessingRcvdSegment initialize flags used in processing segment 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 put segment in sequence number order on queue of segments to be processed process SYN, data, and FIN, repeat until FromNetQueue is empty or next expected sequence number has not been received process SYN process data, send buffer to user, dispose of buffer if no data process fin Determine whether the packet is from the past or the future or whether it is in the current window (from next expected sequence number to next expected sequence number + window). Called when packet received from net contains SYN, FIN or data and has an acceptable sequence number (within the receive window). Packets are queued in sequence number order on the TCB FromNet queue. If packets overlap, then the DataPtr and DataByteCount fields in the TCPRcvBuffer are adjusted to "trim" the packet to contain only the non-overlapping data. Packets that are duplicates are discarded. buffer overlaps start of window trim buffer to start of window queue is empty => put new segment at head new segment is duplicate old segment is duplicate discard old segment use first part of new segment continue with last part of new segment Debugging crap Queues the datagram on the readyToRead queue and lets any waiting processes know that it's there. LLL-MFE used to do something that provoked a problem here. Truncating is slightly bogus if SendDataToUser is going to chop off part of the front Κω– "cedar" style˜Icode2šœ―™―head™Ibody™2M™.M™6Icode™'N™)šΟk ˜ Kšœœ ˜Kšœœ˜Kšœ œ=˜MKšœœι˜υKšœ œ˜Kšœ œ9˜HKšœ œE˜V——š Οnœœœœœ˜DKšœ1˜8Kšœ˜Kšœœ˜ Kšœ œ ˜-šžœœœ#œ˜KšœK™KKšœH™HKšœk™kK™nKšœ™K˜Kšœ œ˜K˜Kšœ:˜:K˜Kšœ˜Kšœ œ˜)šœ6œ˜>Kšœ/˜/Kšœ)˜)Kšœ˜ —K˜KšœD˜DKšœœ˜1Kšœœ˜1K˜Kšœ™Kšœ-˜-Kšœ:˜:šœ œœ˜šœœΟc˜,šœœŸ$˜:KšœœeŸ˜—š˜KšœœrŸ ˜——Kšœ1˜1K˜*Kšœ˜ ——Kšœ7˜7——šžœœœ7˜`šœO™OKšœ>™>Kšœ4™4KšœL™LKšœ™Kšœ™KšœH™HKšœa™aKšœ™Kšœ+™+Kšœ™—Kšœœœ˜KšœœŸ˜AKšœŸ˜/KšœœŸ!˜˜VK˜šžœœŸ$˜@šœœ˜!Kšœ2˜2—K˜—šž œœ˜šœœ˜KšœœŸ˜2šœ œŸ3˜Hšœœœœ˜UKšœ0˜0Kšœœ˜——šœ Ÿ2˜=šœœ˜Kšœ,˜,Kšœ˜—šœ˜Kšœ0˜0Kšœœ˜——šœK˜KKšœ0˜0Kšœœ˜—Kšœ˜ —K˜—šžœœœ˜#Kšœ˜"K˜—šž œœœœ˜(šœœ˜KšŸ8™8Kšœœ˜Kšœ0˜0šœœœ˜XK˜——šœŸ˜$šœ0œ˜7Kšœ3˜3———K˜šž œœœ˜Kšœœ˜šœœ˜šœ Ÿ&˜1Kšœr˜yKšœœ˜Kšœœ˜Kšœ˜Kšœ˜—šœ˜šœœœŸ'˜nKšœi˜pKšœœ˜Kšœœ˜Kšœ˜Kšœ˜——Kšœ˜ —šœœŸ6˜^Kšœ œ˜Kšœœ˜Kšœœ˜Kšœ˜Kšœ˜—Kšœ™š œ œœ!œœœ˜–Kšœ™Kšœœ-˜IKšœ˜Kšœ˜Kšœ˜—šœ œŸ˜7KšœŸ ˜—Kšœt™tKšœ-˜-šœœœ˜;Kšœ'˜'—šœœœ˜šœ2˜6Kšœœœ˜Kšœ$œ˜(Kšœœ˜—Kšœ'˜'—šœ(œŸ˜HKšœ=˜=—šœœŸ3˜Išœ6œ˜LK˜——Kšœ.Ÿ˜RKšœ)Ÿ.˜WKšœŸ/˜Nšœœ˜šœ ˜ Kšœ2˜2Kšœ˜šœœŸ'˜AKšœŸ˜*—Kšœ œ˜Kšœ˜—Kšœœ˜šœ ˜ šœ$œŸ,˜XKšœ.˜.Kšœ˜šœ˜K˜———Kšœ:™:šœ˜Kšœœ˜Kšœœ˜Kšœ œŸ˜)Kšœ˜—šœ ˜ šœ œŸ"˜KKšœ/˜/Kšœ˜Kšœ9ŸT˜Kšœ˜—Kšœœ˜Kšœœ˜Kšœ˜—šœ ˜ šœ œŸ ˜IKšœ1˜1Kšœ˜Kšœ˜—šœ˜Kšœœ˜—Kšœ˜Kšœ˜—Kš˜—KšœŸ˜K˜—šž œœœ˜šœœ˜šœ ˜ Kšœ.˜.Kšœ˜Kšœ˜Kšœ˜Kšœ3˜3Kšœ˜Kšœ˜Kšœœ˜ Kšœœ˜ Kšœ?˜?Kšœ.˜5KšœŸ˜3KšœC˜CKšœ?˜?KšœœŸ˜8KšœœŸ7˜SKšœ˜—šœ ˜ KšœŸ˜0Kšœ˜KšœC˜CKšœ?˜?šœœŸ/˜TKšœ1˜1Kšœ˜Kšœ˜—šœŸ.˜5Kšœ.˜.Kšœ˜KšœœŸ!˜=Kšœ˜—KšœœŸ˜8Kšœ œŸ˜'Kšœ˜—šœ ˜ Kšœ-˜-KšœŸ˜6KšœœŸ˜/KšœœŸ˜(Kšœ˜—šœŸ(˜5Kšœ6˜6Kšœœ˜Kšœ˜——KšœŸ ˜K˜—šž œœœ˜šœœ˜šœ˜Kšœ/˜/Kšœ˜K˜Kšœ˜—šœ ˜ Kšœ-˜-Kšœ˜Kšœ˜—šœ ˜ Kšœ.˜.Kšœ˜Kšœ9ŸT˜K˜—Kšœ˜—KšœŸ4˜QKšœŸ ˜—K™Kšœ$™$šœ˜ Kšœ œ˜)K˜&K˜&KšœD˜Dšœœœ˜Kšœ/Ÿ˜LKšœ˜ ———K˜—KšœŸ˜K˜K˜—š žœœœœœ˜cKšœ³™³KšœœŸ)˜HKšœœŸ&˜Fšœ œ ˜!Kšœœ˜$šœ˜Kšœœ˜,Kšœ˜——šœ˜šœ œ œ˜"Kšœœ˜*šœ˜Kšœœ˜-Kšœ˜——šœ˜šœ ˜Kšœœ˜)Kšœ˜—šœ˜Kšœœ˜)šœ˜Kšœœ˜)Kšœ˜————KšœŸ˜K˜Kšœ˜—šžœœœ#œ˜WKšœ”™”Kšœ œŸ#˜2KšœœŸ"˜/Kšœ œŸ(˜7KšœœŸ'˜4KšœœŸ˜;Kš œ œœœœŸ.˜IšœœœŸ˜Qšœ*œ˜2Kšœ?™?Kšœf˜fKšœ`˜`Kšœ-˜-——šœœœ˜#Kšœ*™*Kšœœ˜)Kšœ˜ —Kšœœ˜ Kšœ˜Kšœ%˜%Kšœ4˜4šœœ˜Kšœœ ˜Kšœ%˜%Kšœ4˜4šœœ˜šœœŸ ˜Ošœ œ˜Kšœœ#˜=—Kšœœ˜1Kšœ˜ —KšœœŸ+˜Fšœœ˜.Kšœ™Kšœ"˜"Kšœ˜ —šœœ˜.Kšœ-™-Kšœ"˜"Kšœœ˜Kšœ œœ!˜4Kšœ˜šœœœŸ1˜CKšœ œœœ˜