#include <TCPConnection.h>
Public Member Functions | |
virtual void | sendAck () |
virtual bool | sendData (bool fullSegmentsOnly, uint32 congestionWindow) |
virtual bool | sendProbe () |
virtual void | retransmitOneSegment (bool called_at_rto) |
virtual void | retransmitData () |
virtual void | sendRst (uint32 seqNo) |
virtual void | sendRst (uint32 seq, IPvXAddress src, IPvXAddress dest, int srcPort, int destPort) |
virtual void | sendRstAck (uint32 seq, uint32 ack, IPvXAddress src, IPvXAddress dest, int srcPort, int destPort) |
virtual void | sendFin () |
virtual void | sendSegment (uint32 bytes) |
virtual void | sendToIP (TCPSegment *tcpseg) |
virtual TCPSegment * | createTCPSegment (const char *name) |
virtual void | startSynRexmitTimer () |
virtual void | signalConnectionTimeout () |
void | scheduleTimeout (cMessage *msg, simtime_t timeout) |
virtual void | printConnBrief () |
virtual void | updateRcvQueueVars () |
virtual unsigned short | updateRcvWnd () |
virtual void | updateWndInfo (TCPSegment *tcpseg, bool doAlways=false) |
TCPConnection (TCP *mod, int appGateIndex, int connId) | |
TCPConnection () | |
virtual | ~TCPConnection () |
virtual void | segmentArrivalWhileClosed (TCPSegment *tcpseg, IPvXAddress src, IPvXAddress dest) |
virtual bool | processTimer (cMessage *msg) |
virtual bool | processTCPSegment (TCPSegment *tcpSeg, IPvXAddress srcAddr, IPvXAddress destAddr) |
virtual bool | processAppCommand (cMessage *msg) |
virtual bool | isLost (uint32 seqNum) |
virtual void | setPipe () |
virtual uint32 | nextSeg () |
virtual void | sendDataDuringLossRecoveryPhase (uint32 congestionWindow) |
virtual void | sendSegmentDuringLossRecoveryPhase (uint32 seqNum) |
virtual void | sendOneNewSegment (bool fullSegmentsOnly, uint32 congestionWindow) |
virtual uint32 | convertSimtimeToTS (simtime_t simtime) |
virtual simtime_t | convertTSToSimtime (uint32 timestamp) |
virtual bool | isSendQueueEmpty () |
Various getters | |
int | getFsmState () const |
TCPStateVariables * | getState () |
TCPSendQueue * | getSendQueue () |
TCPSACKRexmitQueue * | getRexmitQueue () |
TCPReceiveQueue * | getReceiveQueue () |
TCPAlgorithm * | getTcpAlgorithm () |
TCP * | getTcpMain () |
Static Public Member Functions | |
static void | printSegmentBrief (TCPSegment *tcpseg) |
static const char * | stateName (int state) |
static const char * | eventName (int event) |
static const char * | indicationName (int code) |
static const char * | optionName (int option) |
Public Attributes | |
int | appGateIndex |
int | connId |
IPvXAddress | localAddr |
IPvXAddress | remoteAddr |
int | localPort |
int | remotePort |
TCPSACKRexmitQueue * | rexmitQueue |
Protected Member Functions | |
virtual TCPConnection * | cloneListeningConnection () |
virtual void | initConnection (TCPOpenCommand *openCmd) |
virtual void | configureStateVariables () |
virtual void | selectInitialSeqNum () |
virtual bool | isSegmentAcceptable (TCPSegment *tcpseg) |
virtual void | sendSyn () |
virtual void | sendSynAck () |
virtual void | readHeaderOptions (TCPSegment *tcpseg) |
virtual TCPSegment | writeHeaderOptions (TCPSegment *tcpseg) |
virtual TCPSegment | addSacks (TCPSegment *tcpseg) |
virtual uint32 | getTSval (TCPSegment *tcpseg) |
virtual uint32 | getTSecr (TCPSegment *tcpseg) |
cMessage * | cancelEvent (cMessage *msg) |
virtual void | sendToApp (cMessage *msg) |
virtual void | sendIndicationToApp (int code) |
virtual void | sendEstabIndicationToApp () |
FSM transitions: analysing events and executing state transitions | |
virtual TCPEventCode | preanalyseAppCommandEvent (int commandCode) |
virtual bool | performStateTransition (const TCPEventCode &event) |
virtual void | stateEntered (int state) |
Processing app commands. Invoked from processAppCommand(). | |
virtual void | process_OPEN_ACTIVE (TCPEventCode &event, TCPCommand *tcpCommand, cMessage *msg) |
virtual void | process_OPEN_PASSIVE (TCPEventCode &event, TCPCommand *tcpCommand, cMessage *msg) |
virtual void | process_SEND (TCPEventCode &event, TCPCommand *tcpCommand, cMessage *msg) |
virtual void | process_CLOSE (TCPEventCode &event, TCPCommand *tcpCommand, cMessage *msg) |
virtual void | process_ABORT (TCPEventCode &event, TCPCommand *tcpCommand, cMessage *msg) |
virtual void | process_STATUS (TCPEventCode &event, TCPCommand *tcpCommand, cMessage *msg) |
Processing TCP segment arrivals. Invoked from processTCPSegment(). | |
virtual bool | tryFastRoute (TCPSegment *tcpseg) |
virtual TCPEventCode | process_RCV_SEGMENT (TCPSegment *tcpseg, IPvXAddress src, IPvXAddress dest) |
virtual TCPEventCode | processSegmentInListen (TCPSegment *tcpseg, IPvXAddress src, IPvXAddress dest) |
virtual TCPEventCode | processSegmentInSynSent (TCPSegment *tcpseg, IPvXAddress src, IPvXAddress dest) |
virtual TCPEventCode | processSegment1stThru8th (TCPSegment *tcpseg) |
virtual TCPEventCode | processRstInSynReceived (TCPSegment *tcpseg) |
virtual bool | processAckInEstabEtc (TCPSegment *tcpseg) |
Processing of TCP options. Invoked from readHeaderOptions(). Return value indicates whether the option was valid. | |
virtual bool | processMSSOption (TCPSegment *tcpseg, const TCPOption &option) |
virtual bool | processWSOption (TCPSegment *tcpseg, const TCPOption &option) |
virtual bool | processSACKPermittedOption (TCPSegment *tcpseg, const TCPOption &option) |
virtual bool | processSACKOption (TCPSegment *tcpseg, const TCPOption &option) |
virtual bool | processTSOption (TCPSegment *tcpseg, const TCPOption &option) |
Processing timeouts. Invoked from processTimer(). | |
virtual void | process_TIMEOUT_2MSL () |
virtual void | process_TIMEOUT_CONN_ESTAB () |
virtual void | process_TIMEOUT_FIN_WAIT_2 () |
virtual void | process_TIMEOUT_SYN_REXMIT (TCPEventCode &event) |
Static Protected Member Functions | |
static void | sendToIP (TCPSegment *tcpseg, IPvXAddress src, IPvXAddress dest) |
Protected Attributes | |
TCP * | tcpMain |
cFSM | fsm |
TCPStateVariables * | state |
TCPSendQueue * | sendQueue |
TCPReceiveQueue * | receiveQueue |
TCPAlgorithm * | tcpAlgorithm |
cMessage * | the2MSLTimer |
cMessage * | connEstabTimer |
cMessage * | finWait2Timer |
cMessage * | synRexmitTimer |
cOutVector * | sndWndVector |
cOutVector * | rcvWndVector |
cOutVector * | rcvAdvVector |
cOutVector * | sndNxtVector |
cOutVector * | sndAckVector |
cOutVector * | rcvSeqVector |
cOutVector * | rcvAckVector |
cOutVector * | unackedVector |
cOutVector * | dupAcksVector |
cOutVector * | pipeVector |
cOutVector * | sndSacksVector |
cOutVector * | rcvSacksVector |
cOutVector * | rcvOooSegVector |
cOutVector * | sackedBytesVector |
cOutVector * | tcpRcvQueueBytesVector |
cOutVector * | tcpRcvQueueDropsVector |
Manages a TCP connection. This class itself implements the TCP state machine as well as handling control PDUs (SYN, SYN+ACK, RST, FIN, etc.), timers (2MSL, CONN-ESTAB, FIN-WAIT-2) and events (OPEN, SEND, etc) associated with TCP state changes.
The implementation largely follows the functional specification at the end of RFC 793. Code comments extensively quote RFC 793 to make it easier to understand.
TCPConnection objects are not used alone -- they are instantiated and managed by a TCP module.
TCPConnection "outsources" several tasks to objects subclassed from TCPSendQueue, TCPReceiveQueue and TCPAlgorithm; see overview of this with TCP documentation.
Connection variables (TCB) are kept in TCPStateVariables. TCPAlgorithm implementations can extend TCPStateVariables to add their own stuff (see TCPAlgorithm::createStateVariables() factory method.)
The "entry points" of TCPConnnection from TCP are:
All three methods follow a common structure:
When the CLOSED state is reached, TCP will delete the TCPConnection object.
Definition at line 307 of file TCPConnection.h.
TCPConnection::TCPConnection | ( | TCP * | mod, | |
int | appGateIndex, | |||
int | connId | |||
) |
The "normal" constructor.
Definition at line 183 of file TCPConnectionBase.cc.
{ tcpMain = _mod; appGateIndex = _appGateIndex; connId = _connId; localPort = remotePort = -1; char fsmname[24]; sprintf(fsmname, "fsm-%d", connId); fsm.setName(fsmname); fsm.setState(TCP_S_INIT); // queues and algorithm will be created on active or passive open sendQueue = NULL; rexmitQueue = NULL; receiveQueue = NULL; tcpAlgorithm = NULL; state = NULL; the2MSLTimer = new cMessage("2MSL"); connEstabTimer = new cMessage("CONN-ESTAB"); finWait2Timer = new cMessage("FIN-WAIT-2"); synRexmitTimer = new cMessage("SYN-REXMIT"); the2MSLTimer->setContextPointer(this); connEstabTimer->setContextPointer(this); finWait2Timer->setContextPointer(this); synRexmitTimer->setContextPointer(this); // statistics sndWndVector = NULL; rcvWndVector = NULL; rcvAdvVector = NULL; sndNxtVector = NULL; sndAckVector = NULL; rcvSeqVector = NULL; rcvAckVector = NULL; unackedVector = NULL; dupAcksVector = NULL; sndSacksVector = NULL; rcvSacksVector = NULL; rcvOooSegVector = NULL; tcpRcvQueueBytesVector = NULL; tcpRcvQueueDropsVector = NULL; pipeVector = NULL; sackedBytesVector = NULL; if (getTcpMain()->recordStatistics) { sndWndVector = new cOutVector("send window"); rcvWndVector = new cOutVector("receive window"); rcvAdvVector = new cOutVector("advertised window"); sndNxtVector = new cOutVector("sent seq"); sndAckVector = new cOutVector("sent ack"); rcvSeqVector = new cOutVector("rcvd seq"); rcvAckVector = new cOutVector("rcvd ack"); unackedVector = new cOutVector("unacked bytes"); dupAcksVector = new cOutVector("rcvd dupAcks"); pipeVector = new cOutVector("pipe"); sndSacksVector = new cOutVector("sent sacks"); rcvSacksVector = new cOutVector("rcvd sacks"); rcvOooSegVector = new cOutVector("rcvd oooseg"); sackedBytesVector = new cOutVector("rcvd sackedBytes"); tcpRcvQueueBytesVector = new cOutVector("tcpRcvQueueBytes"); tcpRcvQueueDropsVector = new cOutVector("tcpRcvQueueDrops"); } }
TCPConnection::TCPConnection | ( | ) |
Note: this default ctor is NOT used to create live connections, only temporary ones so that TCPMain can invoke their segmentArrivalWhileClosed().
Definition at line 164 of file TCPConnectionBase.cc.
Referenced by cloneListeningConnection().
{ // Note: this ctor is NOT used to create live connections, only // temporary ones to invoke segmentArrivalWhileClosed() on sendQueue = NULL; rexmitQueue = NULL; receiveQueue = NULL; tcpAlgorithm = NULL; state = NULL; the2MSLTimer = connEstabTimer = finWait2Timer = synRexmitTimer = NULL; sndWndVector = rcvWndVector = rcvAdvVector = sndNxtVector = sndAckVector = rcvSeqVector = rcvAckVector = unackedVector = dupAcksVector = sndSacksVector = rcvSacksVector = rcvOooSegVector = tcpRcvQueueBytesVector = tcpRcvQueueDropsVector = pipeVector = sackedBytesVector = NULL; }
TCPConnection::~TCPConnection | ( | ) | [virtual] |
Destructor.
Definition at line 254 of file TCPConnectionBase.cc.
{ delete sendQueue; delete rexmitQueue; delete receiveQueue; delete tcpAlgorithm; delete state; if (the2MSLTimer) delete cancelEvent(the2MSLTimer); if (connEstabTimer) delete cancelEvent(connEstabTimer); if (finWait2Timer) delete cancelEvent(finWait2Timer); if (synRexmitTimer) delete cancelEvent(synRexmitTimer); // statistics delete sndWndVector; delete rcvWndVector; delete rcvAdvVector; delete sndNxtVector; delete sndAckVector; delete rcvSeqVector; delete rcvAckVector; delete unackedVector; delete dupAcksVector; delete sndSacksVector; delete rcvSacksVector; delete rcvOooSegVector; delete tcpRcvQueueBytesVector; delete tcpRcvQueueDropsVector; delete pipeVector; delete sackedBytesVector; }
TCPSegment TCPConnection::addSacks | ( | TCPSegment * | tcpseg | ) | [protected, virtual] |
Utility: adds SACKs to segments header options field
Definition at line 1309 of file TCPConnectionUtil.cc.
Referenced by writeHeaderOptions().
{ TCPOption option; uint options_len = 0; uint used_options_len = 0; uint m = 0; // number of sack blocks to be sent in current segment uint n = 0; // number of sack blocks in sacks_array before sending current segment bool skip_sacks_array = false; // set if dsack is subsets of a bigger sack block recently reported bool overlap = false; // set if recently reported sack blocks are subsets of "sacks_array[0]" uint32 start = state->start_seqno; uint32 end = state->end_seqno; ASSERT(start!=0 || end!=0); // delete old sacks (below rcv_nxt), delete duplicates and print previous status of sacks_array: tcpEV << "Previous status of sacks_array: \n"; for (uint a=0; a<MAX_SACK_BLOCKS; a++) // MAX_SACK_BLOCKS is set to 60 { if (state->sacks_array[a].getStart()!=0 && seqLE(state->sacks_array[a].getEnd(), state->rcv_nxt)) { state->sacks_array[a].setStart(0); state->sacks_array[a].setEnd(0); } if (state->sacks_array[a].getStart()!=0 && state->sacks_array[a].getEnd()!=0) // do not print empty entries tcpEV << "\t" << (a+1) << ". SACK in sacks_array:" << " [" << state->sacks_array[a].getStart() << ".." << state->sacks_array[a].getEnd() << ")\n"; else break; } for (uint a=0; a<MAX_SACK_BLOCKS-1; a++) { if (state->sacks_array[a].getStart() != 0) m++; else break; } n = m + 1; // +1 for new the new sack block // 2 padding bytes are prefixed or TS option is present if (tcpseg->getOptionsArraySize()>0) { for (uint i=0; i<tcpseg->getOptionsArraySize(); i++) used_options_len = used_options_len + tcpseg->getOptions(i).getLength(); if (used_options_len>30) { tcpEV << "ERROR: Failed to addSacks - at least 10 free bytes needed for SACK - used_options_len=" << used_options_len << "\n"; //reset flags: skip_sacks_array = false; state->snd_sack = false; state->snd_dsack = false; state->start_seqno = 0; state->end_seqno = 0; return *tcpseg; } else { n = std::min (n, (((40-used_options_len)-2)/8)); option.setValuesArraySize(n*2); } } else { n = std::min (n, MAX_SACK_ENTRIES); option.setValuesArraySize(n*2); } // before adding a new sack move old sacks by one to the right for (int a=(MAX_SACK_BLOCKS-1); a>=0; a--) // MAX_SACK_BLOCKS is set to 60 state->sacks_array[a+1] = state->sacks_array[a]; if (state->snd_dsack) // SequenceNo < rcv_nxt { // RFC 2883, page 3: // "(3) The left edge of the D-SACK block specifies the first sequence // number of the duplicate contiguous sequence, and the right edge of // the D-SACK block specifies the sequence number immediately following // the last sequence in the duplicate contiguous sequence." if (seqLess(start, state->rcv_nxt) && seqLess(state->rcv_nxt, end)) end = state->rcv_nxt; } else if (start==0 && end==0) // rcv_nxt_old != rcv_nxt { // RFC 2018, page 4: // "* The first SACK block (i.e., the one immediately following the // kind and length fields in the option) MUST specify the contiguous // block of data containing the segment which triggered this ACK, // unless that segment advanced the Acknowledgment Number field in // the header. This assures that the ACK with the SACK option // reflects the most recent change in the data receiver's buffer // queue." start = state->sacks_array[0].getStart(); end = state->sacks_array[0].getEnd(); } else // rcv_nxt_old == rcv_nxt or end <= rcv_nxt { // RFC 2018, page 4: // "* The first SACK block (i.e., the one immediately following the // kind and length fields in the option) MUST specify the contiguous // block of data containing the segment which triggered this ACK," start = receiveQueue->getLE(start); end = receiveQueue->getRE(end); } state->sacks_array[0].setStart(start); state->sacks_array[0].setEnd(end); // RFC 2883, page 3: // "(4) If the D-SACK block reports a duplicate contiguous sequence from // a (possibly larger) block of data in the receiver's data queue above // the cumulative acknowledgement, then the second SACK block in that // SACK option should specify that (possibly larger) block of data. // // (5) Following the SACK blocks described above for reporting duplicate // segments, additional SACK blocks can be used for reporting additional // blocks of data, as specified in RFC 2018." if (state->snd_dsack) { uint32 start_new = receiveQueue->getLE(start); uint32 end_new = receiveQueue->getRE(end); if (start_new != start || end_new != end) { skip_sacks_array = true; for (int a=(MAX_SACK_BLOCKS-1); a>=1; a--) // MAX_SACK_BLOCKS is set to 60 state->sacks_array[a+1] = state->sacks_array[a]; state->sacks_array[1].setStart(start_new); // specifies larger block of data state->sacks_array[1].setEnd(end_new); // specifies larger block of data } } // RFC 2018, page 4: // "* The SACK option SHOULD be filled out by repeating the most // recently reported SACK blocks (based on first SACK blocks in // previous SACK options) that are not subsets of a SACK block // already included in the SACK option being constructed." // check if recently reported SACK blocks are subsets of "sacks_array[0]" for (uint a=0; a<MAX_SACK_BLOCKS-1; a++) { uint i = 1; bool matched = false; if (a==0 && skip_sacks_array) a = 1; if (state->sacks_array[a+i].getStart() == 0) break; while ((state->sacks_array[a].getStart() == state->sacks_array[a+i].getStart() || state->sacks_array[a].getEnd() == state->sacks_array[a+i].getStart() || state->sacks_array[a].getEnd() == state->sacks_array[a+i].getEnd()) && a+i < MAX_SACK_BLOCKS && state->sacks_array[a].getStart()!=0) // MAX_SACK_BLOCKS is set to 60 { matched = true; i++; overlap = true; } if (matched) state->sacks_array[a+1] = state->sacks_array[a+i]; } if (!skip_sacks_array && overlap && m<4) n--; option.setKind(TCPOPTION_SACK); option.setLength(8*n+2); option.setValuesArraySize(2*n); // write sacks from sacks_array to options uint counter = 0; for (uint a=0; a<n; a++) { option.setValues(counter,state->sacks_array[a].getStart()); counter++; option.setValues(counter,state->sacks_array[a].getEnd()); counter++; } // independent of "n" we always need 2 padding bytes (NOP) to make: (used_options_len % 4 == 0) options_len = used_options_len + 8*n + 2; // 8 bytes for each SACK (n) + 2 bytes for kind&length if (options_len <= 40) // Options length allowed? - maximum: 40 Bytes { tcpseg->setOptionsArraySize(tcpseg->getOptionsArraySize()+1); tcpseg->setOptions((tcpseg->getOptionsArraySize()-1),option); // update number of sent sacks state->snd_sacks = state->snd_sacks+n; if (sndSacksVector) sndSacksVector->record(state->snd_sacks); uint counter = 0; tcpEV << n << " SACK(s) added to header:\n"; for (uint t=0; t<(n*2); t++) { counter++; tcpEV << counter << ". SACK:" << " [" << option.getValues(t); t++; tcpEV << ".." << option.getValues(t) << ")"; if (t==1) { if (state->snd_dsack) tcpEV << " (D-SACK)"; else if (seqLE(option.getValues(t),state->rcv_nxt)) { tcpEV << " (received segment filled out a gap)"; state->snd_dsack = true; // Note: Set snd_dsack to delete first sack from sacks_array } } tcpEV << "\n"; } } else tcpEV << "ERROR: Option length exceeded! Segment will be sent without SACK(s)" << "\n"; // RFC 2883, page 3: // "(1) A D-SACK block is only used to report a duplicate contiguous // sequence of data received by the receiver in the most recent packet. // // (2) Each duplicate contiguous sequence of data received is reported // in at most one D-SACK block. (I.e., the receiver sends two identical // D-SACK blocks in subsequent packets only if the receiver receives two // duplicate segments.)// // // In case of d-sack: delete first sack (d-sack) and move old sacks by one to the left if (state->snd_dsack) { for (int a=1; a<MAX_SACK_BLOCKS; a++) // MAX_SACK_BLOCKS is set to 60 state->sacks_array[a-1] = state->sacks_array[a]; // delete/reset last sack to avoid duplicates state->sacks_array[MAX_SACK_BLOCKS-1].setStart(0); state->sacks_array[MAX_SACK_BLOCKS-1].setEnd(0); } // reset flags: skip_sacks_array = false; state->snd_sack = false; state->snd_dsack = false; state->start_seqno = 0; state->end_seqno = 0; return *tcpseg; }
cMessage* TCPConnection::cancelEvent | ( | cMessage * | msg | ) | [inline, protected] |
Utility: cancel a timer
Definition at line 515 of file TCPConnection.h.
Referenced by processSegment1stThru8th(), startSynRexmitTimer(), stateEntered(), and ~TCPConnection().
{return tcpMain->cancelEvent(msg);}
TCPConnection * TCPConnection::cloneListeningConnection | ( | ) | [protected, virtual] |
Utility: clone a listening connection. Used for forking.
Definition at line 164 of file TCPConnectionUtil.cc.
Referenced by processSegmentInListen().
{ TCPConnection *conn = new TCPConnection(tcpMain,appGateIndex,connId); // following code to be kept consistent with initConnection() const char *sendQueueClass = sendQueue->getClassName(); conn->sendQueue = check_and_cast<TCPSendQueue *>(createOne(sendQueueClass)); conn->sendQueue->setConnection(conn); const char *receiveQueueClass = receiveQueue->getClassName(); conn->receiveQueue = check_and_cast<TCPReceiveQueue *>(createOne(receiveQueueClass)); conn->receiveQueue->setConnection(conn); // create SACK retransmit queue rexmitQueue = new TCPSACKRexmitQueue(); rexmitQueue->setConnection(this); const char *tcpAlgorithmClass = tcpAlgorithm->getClassName(); conn->tcpAlgorithm = check_and_cast<TCPAlgorithm *>(createOne(tcpAlgorithmClass)); conn->tcpAlgorithm->setConnection(conn); conn->state = conn->tcpAlgorithm->getStateVariables(); configureStateVariables(); conn->tcpAlgorithm->initialize(); // put it into LISTEN, with our localAddr/localPort conn->state->active = false; conn->state->fork = true; conn->localAddr = localAddr; conn->localPort = localPort; FSM_Goto(conn->fsm, TCP_S_LISTEN); return conn; }
void TCPConnection::configureStateVariables | ( | ) | [protected, virtual] |
Utility: set snd_mss, rcv_wnd and sack in newly created state variables block
Definition at line 349 of file TCPConnectionUtil.cc.
Referenced by cloneListeningConnection(), and initConnection().
{ long advertisedWindowPar = tcpMain->par("advertisedWindow").longValue(); state->ws_support = tcpMain->par("windowScalingSupport"); // if set, this means that current host supports WS (RFC 1323) if (!state->ws_support && (advertisedWindowPar > TCP_MAX_WIN || advertisedWindowPar <= 0)) throw cRuntimeError("Invalid advertisedWindow parameter: %ld", advertisedWindowPar); state->rcv_wnd = advertisedWindowPar; state->rcv_adv = advertisedWindowPar; if (state->ws_support && advertisedWindowPar > TCP_MAX_WIN) { state->rcv_wnd = TCP_MAX_WIN; // we cannot to guarantee that the other end is also supporting the Window Scale (header option) (RFC 1322) state->rcv_adv = TCP_MAX_WIN; // therefore TCP_MAX_WIN is used as initial value for rcv_wnd and rcv_adv } state->maxRcvBuffer = advertisedWindowPar; state->delayed_acks_enabled = tcpMain->par("delayedAcksEnabled"); // delayed ACK algorithm (RFC 1122) enabled/disabled state->nagle_enabled = tcpMain->par("nagleEnabled"); // Nagle's algorithm (RFC 896) enabled/disabled state->limited_transmit_enabled = tcpMain->par("limitedTransmitEnabled"); // Limited Transmit algorithm (RFC 3042) enabled/disabled state->increased_IW_enabled = tcpMain->par("increasedIWEnabled"); // Increased Initial Window (RFC 3390) enabled/disabled state->snd_mss = tcpMain->par("mss").longValue(); // Maximum Segment Size (RFC 793) state->ts_support = tcpMain->par("timestampSupport"); // if set, this means that current host supports TS (RFC 1323) state->sack_support = tcpMain->par("sackSupport"); // if set, this means that current host supports SACK (RFC 2018, 2883, 3517) if (state->sack_support) { std::string algorithmName1 = "TCPReno"; std::string algorithmName2 = tcpMain->par("tcpAlgorithmClass"); if (algorithmName1!=algorithmName2) // TODO add additional checks for new SACK supporting algorithms here once they are implemented { EV << "If you want to use TCP SACK please set tcpAlgorithmClass to TCPReno" << endl; ASSERT(false); } } }
uint32 TCPConnection::convertSimtimeToTS | ( | simtime_t | simtime | ) | [virtual] |
Utility: converts a given simtime to a timestamp (TS).
Definition at line 2045 of file TCPConnectionUtil.cc.
Referenced by TCPBaseAlg::rttMeasurementCompleteUsingTS(), and writeHeaderOptions().
{ ASSERT (SimTime::getScaleExp() <= -3); // FIXME TODO - If the scale factor is different, we need to adjust our simTime to uint32 casts - we are currently using ms precision uint32 timestamp = (uint32) (simtime.dbl() * 1000); return timestamp; }
simtime_t TCPConnection::convertTSToSimtime | ( | uint32 | timestamp | ) | [virtual] |
Utility: converts a given timestamp (TS) to a simtime.
Definition at line 2052 of file TCPConnectionUtil.cc.
Referenced by TCPBaseAlg::rttMeasurementCompleteUsingTS().
{ ASSERT (SimTime::getScaleExp() <= -3); // FIXME TODO - If the scale factor is different, we need to adjust our simTime to uint32 casts - we are currently using ms precision simtime_t simtime = (simtime_t) ((double) timestamp * 0.001); return simtime; }
TCPSegment * TCPConnection::createTCPSegment | ( | const char * | name | ) | [virtual] |
Utility: This factory method gets invoked throughout the TCP model to create a TCPSegment. Override it if you need to subclass TCPSegment.
Definition at line 273 of file TCPConnectionUtil.cc.
Referenced by TCPVirtualDataSendQueue::createSegmentWithBytes(), TCPMsgBasedSendQueue::createSegmentWithBytes(), sendAck(), sendFin(), sendRst(), sendRstAck(), sendSegment(), sendSyn(), and sendSynAck().
{ return new TCPSegment(name); }
const char * TCPConnection::eventName | ( | int | event | ) | [static] |
Utility: returns name of TCP_E_xxx constants
Definition at line 60 of file TCPConnectionUtil.cc.
Referenced by performStateTransition(), and processAppCommand().
{ #define CASE(x) case x: s=#x+6; break const char *s = "unknown"; switch (event) { CASE(TCP_E_IGNORE); CASE(TCP_E_OPEN_ACTIVE); CASE(TCP_E_OPEN_PASSIVE); CASE(TCP_E_SEND); CASE(TCP_E_CLOSE); CASE(TCP_E_ABORT); CASE(TCP_E_STATUS); CASE(TCP_E_RCV_DATA); CASE(TCP_E_RCV_ACK); CASE(TCP_E_RCV_SYN); CASE(TCP_E_RCV_SYN_ACK); CASE(TCP_E_RCV_FIN); CASE(TCP_E_RCV_FIN_ACK); CASE(TCP_E_RCV_RST); CASE(TCP_E_RCV_UNEXP_SYN); CASE(TCP_E_TIMEOUT_2MSL); CASE(TCP_E_TIMEOUT_CONN_ESTAB); CASE(TCP_E_TIMEOUT_FIN_WAIT_2); } return s; #undef CASE }
int TCPConnection::getFsmState | ( | ) | const [inline] |
Definition at line 580 of file TCPConnection.h.
{return fsm.getState();}
TCPReceiveQueue* TCPConnection::getReceiveQueue | ( | ) | [inline] |
Definition at line 584 of file TCPConnection.h.
{return receiveQueue;}
TCPSACKRexmitQueue* TCPConnection::getRexmitQueue | ( | ) | [inline] |
Definition at line 583 of file TCPConnection.h.
{return rexmitQueue;}
TCPSendQueue* TCPConnection::getSendQueue | ( | ) | [inline] |
Definition at line 582 of file TCPConnection.h.
{return sendQueue;}
TCPStateVariables* TCPConnection::getState | ( | ) | [inline] |
Definition at line 581 of file TCPConnection.h.
{return state;}
TCPAlgorithm* TCPConnection::getTcpAlgorithm | ( | ) | [inline] |
Definition at line 585 of file TCPConnection.h.
{return tcpAlgorithm;}
TCP* TCPConnection::getTcpMain | ( | ) | [inline] |
Definition at line 586 of file TCPConnection.h.
Referenced by DumbTCP::connectionClosed(), DumbTCP::dataSent(), TCPBaseAlg::initialize(), TCPConnection(), and DumbTCP::~DumbTCP().
{return tcpMain;}
uint32 TCPConnection::getTSecr | ( | TCPSegment * | tcpseg | ) | [protected, virtual] |
Utility: get TSecr from segments TS header option
Definition at line 1566 of file TCPConnectionUtil.cc.
Referenced by processAckInEstabEtc().
{ for (uint i=0; i<tcpseg->getOptionsArraySize(); i++) { const TCPOption& option = tcpseg->getOptions(i); short kind = option.getKind(); if (kind == TCPOPTION_TIMESTAMP) return option.getValues(1); } return 0; }
uint32 TCPConnection::getTSval | ( | TCPSegment * | tcpseg | ) | [protected, virtual] |
Utility: get TSval from segments TS header option
Definition at line 1554 of file TCPConnectionUtil.cc.
Referenced by processSegment1stThru8th().
{ for (uint i=0; i<tcpseg->getOptionsArraySize(); i++) { const TCPOption& option = tcpseg->getOptions(i); short kind = option.getKind(); if (kind == TCPOPTION_TIMESTAMP) return option.getValues(0); } return 0; }
const char * TCPConnection::indicationName | ( | int | code | ) | [static] |
Utility: returns name of TCP_I_xxx constants
Definition at line 89 of file TCPConnectionUtil.cc.
Referenced by sendEstabIndicationToApp(), and sendIndicationToApp().
{ #define CASE(x) case x: s=#x+6; break const char *s = "unknown"; switch (code) { CASE(TCP_I_DATA); CASE(TCP_I_URGENT_DATA); CASE(TCP_I_ESTABLISHED); CASE(TCP_I_PEER_CLOSED); CASE(TCP_I_CLOSED); CASE(TCP_I_CONNECTION_REFUSED); CASE(TCP_I_CONNECTION_RESET); CASE(TCP_I_TIMED_OUT); CASE(TCP_I_STATUS); } return s; #undef CASE }
void TCPConnection::initConnection | ( | TCPOpenCommand * | openCmd | ) | [protected, virtual] |
Utility: creates send/receive queues and tcpAlgorithm
Definition at line 316 of file TCPConnectionUtil.cc.
Referenced by process_OPEN_ACTIVE(), and process_OPEN_PASSIVE().
{ // create send queue const char *sendQueueClass = openCmd->getSendQueueClass(); if (!sendQueueClass || !sendQueueClass[0]) sendQueueClass = tcpMain->par("sendQueueClass"); sendQueue = check_and_cast<TCPSendQueue *>(createOne(sendQueueClass)); sendQueue->setConnection(this); // create receive queue const char *receiveQueueClass = openCmd->getReceiveQueueClass(); if (!receiveQueueClass || !receiveQueueClass[0]) receiveQueueClass = tcpMain->par("receiveQueueClass"); receiveQueue = check_and_cast<TCPReceiveQueue *>(createOne(receiveQueueClass)); receiveQueue->setConnection(this); // create SACK retransmit queue rexmitQueue = new TCPSACKRexmitQueue(); rexmitQueue->setConnection(this); // create algorithm const char *tcpAlgorithmClass = openCmd->getTcpAlgorithmClass(); if (!tcpAlgorithmClass || !tcpAlgorithmClass[0]) tcpAlgorithmClass = tcpMain->par("tcpAlgorithmClass"); tcpAlgorithm = check_and_cast<TCPAlgorithm *>(createOne(tcpAlgorithmClass)); tcpAlgorithm->setConnection(this); // create state block state = tcpAlgorithm->getStateVariables(); configureStateVariables(); tcpAlgorithm->initialize(); }
bool TCPConnection::isLost | ( | uint32 | seqNum | ) | [virtual] |
For SACK TCP. RFC 3517, page 3: "This routine returns whether the given sequence number is considered to be lost. The routine returns true when either DupThresh discontiguous SACKed sequences have arrived above 'SeqNum' or (DupThresh * SMSS) bytes with sequence numbers greater than 'SeqNum' have been SACKed. Otherwise, the routine returns false."
Definition at line 1669 of file TCPConnectionUtil.cc.
Referenced by nextSeg(), and setPipe().
{ ASSERT (state->sack_enabled); // RFC 3517, page 3: "This routine returns whether the given sequence number is // considered to be lost. The routine returns true when either // DupThresh discontiguous SACKed sequences have arrived above // 'SeqNum' or (DupThresh * SMSS) bytes with sequence numbers greater // than 'SeqNum' have been SACKed. Otherwise, the routine returns // false." bool isLost = false; ASSERT(seqGE(seqNum,state->snd_una)); // HighAck = snd_una if (rexmitQueue->getNumOfDiscontiguousSacks(seqNum) >= DUPTHRESH || // DUPTHRESH = 3 rexmitQueue->getAmountOfSackedBytes(seqNum) >= (DUPTHRESH * state->snd_mss)) isLost = true; else isLost = false; return isLost; }
bool TCPConnection::isSegmentAcceptable | ( | TCPSegment * | tcpseg | ) | [protected, virtual] |
Utility: check if segment is acceptable (all bytes are in receive window)
Definition at line 393 of file TCPConnectionUtil.cc.
Referenced by processSegment1stThru8th().
{ // check that segment entirely falls in receive window // RFC 793, page 69: // "There are four cases for the acceptability test for an incoming segment:" uint32 len = tcpseg->getPayloadLength(); uint32 seqNo = tcpseg->getSequenceNo(); bool ret; if (len == 0) { if (state->rcv_wnd == 0) ret = (seqNo == state->rcv_nxt); else // rcv_wnd > 0 ret = seqLE(state->rcv_nxt, seqNo) && seqLess(seqNo, state->rcv_nxt + state->rcv_wnd); } else // len > 0 { if (state->rcv_wnd == 0) ret = false; else // rcv_wnd > 0 ret = (seqLE(state->rcv_nxt, seqNo) && seqLess(seqNo, state->rcv_nxt + state->rcv_wnd)) || (seqLE(state->rcv_nxt, seqNo + len - 1) && seqLess(seqNo + len - 1, state->rcv_nxt + state->rcv_wnd)); } if (!ret) { tcpEV << "Not Acceptable segment. seqNo:" << seqNo << ", len:" << len << ", rcv_nxt:" << state->rcv_nxt << ", rcv_wnd:" << state->rcv_wnd << endl; } return ret; }
bool TCPConnection::isSendQueueEmpty | ( | ) | [virtual] |
Utility: checks if send queue is empty (no data to send).
Definition at line 2059 of file TCPConnectionUtil.cc.
Referenced by TCPBaseAlg::sendData().
{ return (sendQueue->getBytesAvailable(state->snd_nxt) == 0); }
uint32 TCPConnection::nextSeg | ( | ) | [virtual] |
For SACK TCP. RFC 3517, page 3: "This routine uses the scoreboard data structure maintained by the Update() function to determine what to transmit based on the SACK information that has arrived from the data receiver (and hence been marked in the scoreboard). NextSeg () MUST return the sequence number range of the next segment that is to be transmitted..."
Definition at line 1760 of file TCPConnectionUtil.cc.
Referenced by sendDataDuringLossRecoveryPhase().
{ ASSERT (state->sack_enabled); // RFC 3517, page 5: "This routine uses the scoreboard data structure maintained by the // Update() function to determine what to transmit based on the SACK // information that has arrived from the data receiver (and hence // been marked in the scoreboard). NextSeg () MUST return the // sequence number range of the next segment that is to be // transmitted, per the following rules:" state->highRxt = rexmitQueue->getHighestRexmittedSeqNum(); uint32 seqNum = 0; bool found = false; uint32 shift = state->snd_mss; if (state->ts_enabled) shift -= TCP_OPTION_TS_SIZE; // RFC 3517, page 5: "(1) If there exists a smallest unSACKed sequence number 'S2' that // meets the following three criteria for determining loss, the // sequence range of one segment of up to SMSS octets starting // with S2 MUST be returned. // // (1.a) S2 is greater than HighRxt. // // (1.b) S2 is less than the highest octet covered by any // received SACK. // // (1.c) IsLost (S2) returns true." for (uint32 s2=state->snd_una; s2<state->snd_max; s2=s2+shift) { if (rexmitQueue->getSackedBit(s2)==false) { if (seqGE(s2,state->highRxt) && seqLE(s2,(rexmitQueue->getHighestSackedSeqNum())) && isLost(s2)) { seqNum = s2; found = true; return seqNum; } } } // RFC 3517, page 5: "(2) If no sequence number 'S2' per rule (1) exists but there // exists available unsent data and the receiver's advertised // window allows, the sequence range of one segment of up to SMSS // octets of previously unsent data starting with sequence number // HighData+1 MUST be returned." if (!found) { // check how many unsent bytes we have ulong buffered = sendQueue->getBytesAvailable(state->snd_max); ulong maxWindow = state->snd_wnd; // effectiveWindow: number of bytes we're allowed to send now ulong effectiveWin = maxWindow - state->pipe; if (buffered > 0 && effectiveWin >= state->snd_mss) { seqNum = state->snd_max; // HighData = snd_max found = true; return seqNum; } } // RFC 3517, pages 5 and 6: "(3) If the conditions for rules (1) and (2) fail, but there exists // an unSACKed sequence number 'S3' that meets the criteria for // detecting loss given in steps (1.a) and (1.b) above // (specifically excluding step (1.c)) then one segment of up to // SMSS octets starting with S3 MAY be returned. // // Note that rule (3) is a sort of retransmission "last resort". // It allows for retransmission of sequence numbers even when the // sender has less certainty a segment has been lost than as with // rule (1). Retransmitting segments via rule (3) will help // sustain TCP's ACK clock and therefore can potentially help // avoid retransmission timeouts. However, in sending these // segments the sender has two copies of the same data considered // to be in the network (and also in the Pipe estimate). When an // ACK or SACK arrives covering this retransmitted segment, the // sender cannot be sure exactly how much data left the network // (one of the two transmissions of the packet or both // transmissions of the packet). Therefore the sender may // underestimate Pipe by considering both segments to have left // the network when it is possible that only one of the two has. // // We believe that the triggering of rule (3) will be rare and // that the implications are likely limited to corner cases // relative to the entire recovery algorithm. Therefore we leave // the decision of whether or not to use rule (3) to // implementors." if (!found) { for (uint32 s3=state->snd_una; s3<state->snd_max; s3=s3+shift) { if (rexmitQueue->getSackedBit(s3)==false) { if (seqGE(s3,state->highRxt) && seqLE(s3,(rexmitQueue->getHighestSackedSeqNum()))) { seqNum = s3; found = true; return seqNum; } } } } // RFC 3517, page 6: "(4) If the conditions for each of (1), (2), and (3) are not met, // then NextSeg () MUST indicate failure, and no segment is // returned." if (!found) seqNum = 0; return seqNum; }
const char * TCPConnection::optionName | ( | int | option | ) | [static] |
Utility: returns name of TCPOPTION_xxx constants
Definition at line 109 of file TCPConnectionUtil.cc.
Referenced by printSegmentBrief(), and readHeaderOptions().
{ switch (option) { case TCPOPTION_END_OF_OPTION_LIST: return "EOL"; case TCPOPTION_NO_OPERATION: return "NOP"; case TCPOPTION_MAXIMUM_SEGMENT_SIZE: return "MSS"; case TCPOPTION_WINDOW_SCALE: return "WS"; case TCPOPTION_SACK_PERMITTED: return "SACK_PERMITTED"; case TCPOPTION_SACK: return "SACK"; case TCPOPTION_TIMESTAMP: return "TS"; default: return "unknown"; } }
bool TCPConnection::performStateTransition | ( | const TCPEventCode & | event | ) | [protected, virtual] |
Implemements the pure TCP state machine
Definition at line 386 of file TCPConnectionBase.cc.
Referenced by processAppCommand(), processTCPSegment(), and processTimer().
{ ASSERT(fsm.getState()!=TCP_S_CLOSED); // closed connections should be deleted immediately if (event==TCP_E_IGNORE) // e.g. discarded segment { tcpEV << "Staying in state: " << stateName(fsm.getState()) << " (no FSM event)\n"; return true; } // state machine // TBD add handling of connection timeout event (KEEP-ALIVE), with transition to CLOSED // Note: empty "default:" lines are for gcc's benefit which would otherwise spit warnings int oldState = fsm.getState(); switch (fsm.getState()) { case TCP_S_INIT: switch (event) { case TCP_E_OPEN_PASSIVE:FSM_Goto(fsm, TCP_S_LISTEN); break; case TCP_E_OPEN_ACTIVE: FSM_Goto(fsm, TCP_S_SYN_SENT); break; default:; } break; case TCP_S_LISTEN: switch (event) { case TCP_E_OPEN_ACTIVE: FSM_Goto(fsm, TCP_S_SYN_SENT); break; case TCP_E_SEND: FSM_Goto(fsm, TCP_S_SYN_SENT); break; case TCP_E_CLOSE: FSM_Goto(fsm, TCP_S_CLOSED); break; case TCP_E_ABORT: FSM_Goto(fsm, TCP_S_CLOSED); break; case TCP_E_RCV_SYN: FSM_Goto(fsm, TCP_S_SYN_RCVD);break; default:; } break; case TCP_S_SYN_RCVD: switch (event) { case TCP_E_CLOSE: FSM_Goto(fsm, TCP_S_FIN_WAIT_1); break; case TCP_E_ABORT: FSM_Goto(fsm, TCP_S_CLOSED); break; case TCP_E_TIMEOUT_CONN_ESTAB: FSM_Goto(fsm, state->active ? TCP_S_CLOSED : TCP_S_LISTEN); break; case TCP_E_RCV_RST: FSM_Goto(fsm, state->active ? TCP_S_CLOSED : TCP_S_LISTEN); break; case TCP_E_RCV_ACK: FSM_Goto(fsm, TCP_S_ESTABLISHED); break; case TCP_E_RCV_FIN: FSM_Goto(fsm, TCP_S_CLOSE_WAIT); break; case TCP_E_RCV_UNEXP_SYN: FSM_Goto(fsm, TCP_S_CLOSED); break; default:; } break; case TCP_S_SYN_SENT: switch (event) { case TCP_E_CLOSE: FSM_Goto(fsm, TCP_S_CLOSED); break; case TCP_E_ABORT: FSM_Goto(fsm, TCP_S_CLOSED); break; case TCP_E_TIMEOUT_CONN_ESTAB: FSM_Goto(fsm, TCP_S_CLOSED); break; case TCP_E_RCV_RST: FSM_Goto(fsm, TCP_S_CLOSED); break; case TCP_E_RCV_SYN_ACK: FSM_Goto(fsm, TCP_S_ESTABLISHED); break; case TCP_E_RCV_SYN: FSM_Goto(fsm, TCP_S_SYN_RCVD); break; default:; } break; case TCP_S_ESTABLISHED: switch (event) { case TCP_E_CLOSE: FSM_Goto(fsm, TCP_S_FIN_WAIT_1); break; case TCP_E_ABORT: FSM_Goto(fsm, TCP_S_CLOSED); break; case TCP_E_RCV_FIN: FSM_Goto(fsm, TCP_S_CLOSE_WAIT); break; case TCP_E_RCV_RST: FSM_Goto(fsm, TCP_S_CLOSED); break; case TCP_E_RCV_UNEXP_SYN: FSM_Goto(fsm, TCP_S_CLOSED); break; default:; } break; case TCP_S_CLOSE_WAIT: switch (event) { case TCP_E_CLOSE: FSM_Goto(fsm, TCP_S_LAST_ACK); break; case TCP_E_ABORT: FSM_Goto(fsm, TCP_S_CLOSED); break; case TCP_E_RCV_RST: FSM_Goto(fsm, TCP_S_CLOSED); break; case TCP_E_RCV_UNEXP_SYN: FSM_Goto(fsm, TCP_S_CLOSED); break; default:; } break; case TCP_S_LAST_ACK: switch (event) { case TCP_E_ABORT: FSM_Goto(fsm, TCP_S_CLOSED); break; case TCP_E_RCV_ACK: FSM_Goto(fsm, TCP_S_CLOSED); break; case TCP_E_RCV_RST: FSM_Goto(fsm, TCP_S_CLOSED); break; case TCP_E_RCV_UNEXP_SYN: FSM_Goto(fsm, TCP_S_CLOSED); break; default:; } break; case TCP_S_FIN_WAIT_1: switch (event) { case TCP_E_ABORT: FSM_Goto(fsm, TCP_S_CLOSED); break; case TCP_E_RCV_FIN: FSM_Goto(fsm, TCP_S_CLOSING); break; case TCP_E_RCV_ACK: FSM_Goto(fsm, TCP_S_FIN_WAIT_2); break; case TCP_E_RCV_FIN_ACK: FSM_Goto(fsm, TCP_S_TIME_WAIT); break; case TCP_E_RCV_RST: FSM_Goto(fsm, TCP_S_CLOSED); break; case TCP_E_RCV_UNEXP_SYN: FSM_Goto(fsm, TCP_S_CLOSED); break; default:; } break; case TCP_S_FIN_WAIT_2: switch (event) { case TCP_E_ABORT: FSM_Goto(fsm, TCP_S_CLOSED); break; case TCP_E_RCV_FIN: FSM_Goto(fsm, TCP_S_TIME_WAIT); break; case TCP_E_TIMEOUT_FIN_WAIT_2: FSM_Goto(fsm, TCP_S_CLOSED); break; case TCP_E_RCV_RST: FSM_Goto(fsm, TCP_S_CLOSED); break; case TCP_E_RCV_UNEXP_SYN: FSM_Goto(fsm, TCP_S_CLOSED); break; default:; } break; case TCP_S_CLOSING: switch (event) { case TCP_E_ABORT: FSM_Goto(fsm, TCP_S_CLOSED); break; case TCP_E_RCV_ACK: FSM_Goto(fsm, TCP_S_TIME_WAIT); break; case TCP_E_RCV_RST: FSM_Goto(fsm, TCP_S_CLOSED); break; case TCP_E_RCV_UNEXP_SYN: FSM_Goto(fsm, TCP_S_CLOSED); break; default:; } break; case TCP_S_TIME_WAIT: switch (event) { case TCP_E_ABORT: FSM_Goto(fsm, TCP_S_CLOSED); break; case TCP_E_TIMEOUT_2MSL: FSM_Goto(fsm, TCP_S_CLOSED); break; case TCP_E_RCV_RST: FSM_Goto(fsm, TCP_S_CLOSED); break; case TCP_E_RCV_UNEXP_SYN: FSM_Goto(fsm, TCP_S_CLOSED); break; default:; } break; case TCP_S_CLOSED: break; } if (oldState!=fsm.getState()) { tcpEV << "Transition: " << stateName(oldState) << " --> " << stateName(fsm.getState()) << " (event was: " << eventName(event) << ")\n"; testingEV << tcpMain->getName() << ": " << stateName(oldState) << " --> " << stateName(fsm.getState()) << " (on " << eventName(event) << ")\n"; // cancel timers, etc. stateEntered(fsm.getState()); } else { tcpEV << "Staying in state: " << stateName(fsm.getState()) << " (event was: " << eventName(event) << ")\n"; } return fsm.getState()!=TCP_S_CLOSED; }
TCPEventCode TCPConnection::preanalyseAppCommandEvent | ( | int | commandCode | ) | [protected, virtual] |
Maps app command codes (msg kind of app command msgs) to TCP_E_xxx event codes
Definition at line 371 of file TCPConnectionBase.cc.
Referenced by processAppCommand().
{ switch (commandCode) { case TCP_C_OPEN_ACTIVE: return TCP_E_OPEN_ACTIVE; case TCP_C_OPEN_PASSIVE: return TCP_E_OPEN_PASSIVE; case TCP_C_SEND: return TCP_E_SEND; case TCP_C_CLOSE: return TCP_E_CLOSE; case TCP_C_ABORT: return TCP_E_ABORT; case TCP_C_STATUS: return TCP_E_STATUS; default: opp_error("Unknown message kind in app command"); return (TCPEventCode)0; // to satisfy compiler } }
void TCPConnection::printConnBrief | ( | ) | [virtual] |
Utility: prints local/remote addr/port and app gate index/connId
Definition at line 124 of file TCPConnectionUtil.cc.
Referenced by processAppCommand(), processTCPSegment(), and processTimer().
{ tcpEV << "Connection "; tcpEV << localAddr << ":" << localPort << " to " << remoteAddr << ":" << remotePort; tcpEV << " on app[" << appGateIndex << "],connId=" << connId; tcpEV << " in " << stateName(fsm.getState()); tcpEV << " (ptr=0x" << this << ")\n"; }
void TCPConnection::printSegmentBrief | ( | TCPSegment * | tcpseg | ) | [static] |
Utility: prints important header fields
Definition at line 133 of file TCPConnectionUtil.cc.
Referenced by process_RCV_SEGMENT(), segmentArrivalWhileClosed(), and sendToIP().
{ tcpEV << "." << tcpseg->getSrcPort() << " > "; tcpEV << "." << tcpseg->getDestPort() << ": "; if (tcpseg->getSynBit()) tcpEV << (tcpseg->getAckBit() ? "SYN+ACK " : "SYN "); if (tcpseg->getFinBit()) tcpEV << "FIN(+ACK) "; if (tcpseg->getRstBit()) tcpEV << (tcpseg->getAckBit() ? "RST+ACK " : "RST "); if (tcpseg->getPshBit()) tcpEV << "PSH "; if (tcpseg->getPayloadLength()>0 || tcpseg->getSynBit()) { tcpEV << "[" << tcpseg->getSequenceNo() << ".." << (tcpseg->getSequenceNo()+tcpseg->getPayloadLength()) << ") "; tcpEV << "(l=" << tcpseg->getPayloadLength() << ") "; } if (tcpseg->getAckBit()) tcpEV << "ack " << tcpseg->getAckNo() << " "; tcpEV << "win " << tcpseg->getWindow() << " "; if (tcpseg->getUrgBit()) tcpEV << "urg " << tcpseg->getUrgentPointer() << " "; if (tcpseg->getHeaderLength() > TCP_HEADER_OCTETS) // Header options present? TCP_HEADER_OCTETS = 20 { tcpEV << "options "; for (uint i=0; i<tcpseg->getOptionsArraySize(); i++) { const TCPOption& option = tcpseg->getOptions(i); short kind = option.getKind(); tcpEV << optionName(kind) << " "; } } tcpEV << "\n"; }
void TCPConnection::process_ABORT | ( | TCPEventCode & | event, | |
TCPCommand * | tcpCommand, | |||
cMessage * | msg | |||
) | [protected, virtual] |
Definition at line 221 of file TCPConnectionEventProc.cc.
Referenced by processAppCommand().
{ delete tcpCommand; delete msg; // // The ABORT event will automatically take the connection to the CLOSED // state, flush queues etc -- no need to do it here. Also, we don't need to // send notification to the user, they know what's going on. // switch(fsm.getState()) { case TCP_S_INIT: opp_error("Error processing command ABORT: connection not open"); case TCP_S_SYN_RCVD: case TCP_S_ESTABLISHED: case TCP_S_FIN_WAIT_1: case TCP_S_FIN_WAIT_2: case TCP_S_CLOSE_WAIT: //" // Send a reset segment: // // <SEQ=SND.NXT><CTL=RST> //" sendRst(state->snd_nxt); break; } }
void TCPConnection::process_CLOSE | ( | TCPEventCode & | event, | |
TCPCommand * | tcpCommand, | |||
cMessage * | msg | |||
) | [protected, virtual] |
Definition at line 160 of file TCPConnectionEventProc.cc.
Referenced by processAppCommand().
{ delete tcpCommand; delete msg; switch(fsm.getState()) { case TCP_S_INIT: opp_error("Error processing command CLOSE: connection not open"); case TCP_S_LISTEN: // Nothing to do here break; case TCP_S_SYN_SENT: // Delete the TCB and return "error: closing" responses to any // queued SENDs, or RECEIVEs. break; case TCP_S_SYN_RCVD: case TCP_S_ESTABLISHED: case TCP_S_CLOSE_WAIT: // // SYN_RCVD processing (ESTABLISHED and CLOSE_WAIT are similar): //" // If no SENDs have been issued and there is no pending data to send, // then form a FIN segment and send it, and enter FIN-WAIT-1 state; // otherwise queue for processing after entering ESTABLISHED state. //" if (state->snd_max==sendQueue->getBufferEndSeq()) { tcpEV << "No outstanding SENDs, sending FIN right away, advancing snd_nxt over the FIN\n"; state->snd_nxt = state->snd_max; sendFin(); tcpAlgorithm->restartRexmitTimer(); state->snd_max = ++state->snd_nxt; if (unackedVector) unackedVector->record(state->snd_max - state->snd_una); // state transition will automatically take us to FIN_WAIT_1 (or LAST_ACK) } else { tcpEV << "SEND of " << (sendQueue->getBufferEndSeq()-state->snd_max) << " bytes pending, deferring sending of FIN\n"; event = TCP_E_IGNORE; } state->send_fin = true; state->snd_fin_seq = sendQueue->getBufferEndSeq(); break; case TCP_S_FIN_WAIT_1: case TCP_S_FIN_WAIT_2: case TCP_S_CLOSING: case TCP_S_LAST_ACK: case TCP_S_TIME_WAIT: // RFC 793 is not entirely clear on how to handle a duplicate close request. // Here we treat it as an error. opp_error("Duplicate CLOSE command: connection already closing"); } }
void TCPConnection::process_OPEN_ACTIVE | ( | TCPEventCode & | event, | |
TCPCommand * | tcpCommand, | |||
cMessage * | msg | |||
) | [protected, virtual] |
Definition at line 33 of file TCPConnectionEventProc.cc.
Referenced by processAppCommand().
{ TCPOpenCommand *openCmd = check_and_cast<TCPOpenCommand *>(tcpCommand); IPvXAddress localAddr, remoteAddr; int localPort, remotePort; switch(fsm.getState()) { case TCP_S_INIT: initConnection(openCmd); // store local/remote socket state->active = true; localAddr = openCmd->getLocalAddr(); remoteAddr = openCmd->getRemoteAddr(); localPort = openCmd->getLocalPort(); remotePort = openCmd->getRemotePort(); if (remoteAddr.isUnspecified() || remotePort==-1) opp_error("Error processing command OPEN_ACTIVE: remote address and port must be specified"); if (localPort==-1) { localPort = tcpMain->getEphemeralPort(); tcpEV << "Assigned ephemeral port " << localPort << "\n"; } tcpEV << "OPEN: " << localAddr << ":" << localPort << " --> " << remoteAddr << ":" << remotePort << "\n"; tcpMain->addSockPair(this, localAddr, remoteAddr, localPort, remotePort); // send initial SYN selectInitialSeqNum(); sendSyn(); startSynRexmitTimer(); scheduleTimeout(connEstabTimer, TCP_TIMEOUT_CONN_ESTAB); break; default: opp_error("Error processing command OPEN_ACTIVE: connection already exists"); } delete openCmd; delete msg; }
void TCPConnection::process_OPEN_PASSIVE | ( | TCPEventCode & | event, | |
TCPCommand * | tcpCommand, | |||
cMessage * | msg | |||
) | [protected, virtual] |
Definition at line 79 of file TCPConnectionEventProc.cc.
Referenced by processAppCommand().
{ TCPOpenCommand *openCmd = check_and_cast<TCPOpenCommand *>(tcpCommand); IPvXAddress localAddr; int localPort; switch(fsm.getState()) { case TCP_S_INIT: initConnection(openCmd); // store local/remote socket state->active = false; state->fork = openCmd->getFork(); localAddr = openCmd->getLocalAddr(); localPort = openCmd->getLocalPort(); if (localPort==-1) opp_error("Error processing command OPEN_PASSIVE: local port must be specified"); tcpEV << "Starting to listen on: " << localAddr << ":" << localPort << "\n"; tcpMain->addSockPair(this, localAddr, IPvXAddress(), localPort, -1); break; default: opp_error("Error processing command OPEN_PASSIVE: connection already exists"); } delete openCmd; delete msg; }
TCPEventCode TCPConnection::process_RCV_SEGMENT | ( | TCPSegment * | tcpseg, | |
IPvXAddress | src, | |||
IPvXAddress | dest | |||
) | [protected, virtual] |
Process incoming TCP segment. Returns a specific event code (e.g. TCP_E_RCV_SYN) which will drive the state machine.
Definition at line 83 of file TCPConnectionRcvSegment.cc.
Referenced by processTCPSegment().
{ tcpEV << "Seg arrived: "; printSegmentBrief(tcpseg); tcpEV << "TCB: " << state->info() << "\n"; if (rcvSeqVector) rcvSeqVector->record(tcpseg->getSequenceNo()); if (rcvAckVector) rcvAckVector->record(tcpseg->getAckNo()); // // Note: this code is organized exactly as RFC 793, section "3.9 Event // Processing", subsection "SEGMENT ARRIVES". // TCPEventCode event; if (fsm.getState()==TCP_S_LISTEN) { event = processSegmentInListen(tcpseg, src, dest); } else if (fsm.getState()==TCP_S_SYN_SENT) { event = processSegmentInSynSent(tcpseg, src, dest); } else { // RFC 793 steps "first check sequence number", "second check the RST bit", etc event = processSegment1stThru8th(tcpseg); } delete tcpseg; return event; }
void TCPConnection::process_SEND | ( | TCPEventCode & | event, | |
TCPCommand * | tcpCommand, | |||
cMessage * | msg | |||
) | [protected, virtual] |
Definition at line 112 of file TCPConnectionEventProc.cc.
Referenced by processAppCommand().
{ TCPSendCommand *sendCommand = check_and_cast<TCPSendCommand *>(tcpCommand); // FIXME how to support PUSH? One option is to treat each SEND as a unit of data, // and set PSH at SEND boundaries switch(fsm.getState()) { case TCP_S_INIT: opp_error("Error processing command SEND: connection not open"); case TCP_S_LISTEN: tcpEV << "SEND command turns passive open into active open, sending initial SYN\n"; state->active = true; selectInitialSeqNum(); sendSyn(); startSynRexmitTimer(); scheduleTimeout(connEstabTimer, TCP_TIMEOUT_CONN_ESTAB); sendQueue->enqueueAppData(PK(msg)); // queue up for later tcpEV << sendQueue->getBytesAvailable(state->snd_una) << " bytes in queue\n"; break; case TCP_S_SYN_RCVD: case TCP_S_SYN_SENT: tcpEV << "Queueing up data for sending later.\n"; sendQueue->enqueueAppData(PK(msg)); // queue up for later tcpEV << sendQueue->getBytesAvailable(state->snd_una) << " bytes in queue\n"; break; case TCP_S_ESTABLISHED: case TCP_S_CLOSE_WAIT: sendQueue->enqueueAppData(PK(msg)); tcpEV << sendQueue->getBytesAvailable(state->snd_una) << " bytes in queue, plus " << (state->snd_max-state->snd_una) << " bytes unacknowledged\n"; tcpAlgorithm->sendCommandInvoked(); break; case TCP_S_LAST_ACK: case TCP_S_FIN_WAIT_1: case TCP_S_FIN_WAIT_2: case TCP_S_CLOSING: case TCP_S_TIME_WAIT: opp_error("Error processing command SEND: connection closing"); } delete sendCommand; // msg itself has been taken by the sendQueue }
void TCPConnection::process_STATUS | ( | TCPEventCode & | event, | |
TCPCommand * | tcpCommand, | |||
cMessage * | msg | |||
) | [protected, virtual] |
Definition at line 252 of file TCPConnectionEventProc.cc.
Referenced by processAppCommand().
{ delete tcpCommand; // but reuse msg for reply if (fsm.getState()==TCP_S_INIT) opp_error("Error processing command STATUS: connection not open"); TCPStatusInfo *statusInfo = new TCPStatusInfo(); statusInfo->setState(fsm.getState()); statusInfo->setStateName(stateName(fsm.getState())); statusInfo->setLocalAddr(localAddr); statusInfo->setRemoteAddr(remoteAddr); statusInfo->setLocalPort(localPort); statusInfo->setRemotePort(remotePort); statusInfo->setSnd_mss(state->snd_mss); statusInfo->setSnd_una(state->snd_una); statusInfo->setSnd_nxt(state->snd_nxt); statusInfo->setSnd_max(state->snd_max); statusInfo->setSnd_wnd(state->snd_wnd); statusInfo->setSnd_up(state->snd_up); statusInfo->setSnd_wl1(state->snd_wl1); statusInfo->setSnd_wl2(state->snd_wl2); statusInfo->setIss(state->iss); statusInfo->setRcv_nxt(state->rcv_nxt); statusInfo->setRcv_wnd(state->rcv_wnd); statusInfo->setRcv_up(state->rcv_up); statusInfo->setIrs(state->irs); statusInfo->setFin_ack_rcvd(state->fin_ack_rcvd); msg->setControlInfo(statusInfo); sendToApp(msg); }
void TCPConnection::process_TIMEOUT_2MSL | ( | ) | [protected, virtual] |
Definition at line 1266 of file TCPConnectionRcvSegment.cc.
Referenced by processTimer().
{ //" // If the time-wait timeout expires on a connection delete the TCB, // enter the CLOSED state and return. //" switch(fsm.getState()) { case TCP_S_TIME_WAIT: // Nothing to do here. The TIMEOUT_2MSL event will automatically take // the connection to CLOSED. We already notified the user // (TCP_I_CLOSED) when we entered the TIME_WAIT state from CLOSING, // FIN_WAIT_1 or FIN_WAIT_2. break; default: // We should not receive this timeout in this state. opp_error("Internal error: received time-wait (2MSL) timeout in state %s", stateName(fsm.getState())); } }
void TCPConnection::process_TIMEOUT_CONN_ESTAB | ( | ) | [protected, virtual] |
Definition at line 1246 of file TCPConnectionRcvSegment.cc.
Referenced by processTimer().
{ switch(fsm.getState()) { case TCP_S_SYN_RCVD: case TCP_S_SYN_SENT: // Nothing to do here. TIMEOUT_CONN_ESTAB event will automatically // take the connection to LISTEN or CLOSED, and cancel SYN-REXMIT timer. if (state->active) { // notify user if we're on the active side sendIndicationToApp(TCP_I_TIMED_OUT); } break; default: // We should not receive this timeout in this state. opp_error("Internal error: received CONN_ESTAB timeout in state %s", stateName(fsm.getState())); } }
void TCPConnection::process_TIMEOUT_FIN_WAIT_2 | ( | ) | [protected, virtual] |
Definition at line 1286 of file TCPConnectionRcvSegment.cc.
Referenced by processTimer().
{ switch(fsm.getState()) { case TCP_S_FIN_WAIT_2: // Nothing to do here. The TIMEOUT_FIN_WAIT_2 event will automatically take // the connection to CLOSED. sendIndicationToApp(TCP_I_CLOSED); break; default: // We should not receive this timeout in this state. opp_error("Internal error: received FIN_WAIT_2 timeout in state %s", stateName(fsm.getState())); } }
void TCPConnection::process_TIMEOUT_SYN_REXMIT | ( | TCPEventCode & | event | ) | [protected, virtual] |
Definition at line 1311 of file TCPConnectionRcvSegment.cc.
Referenced by processTimer().
{ if (++state->syn_rexmit_count>MAX_SYN_REXMIT_COUNT) { tcpEV << "Retransmission count during connection setup exceeds " << MAX_SYN_REXMIT_COUNT << ", giving up\n"; // Note ABORT will take the connection to closed, and cancel CONN-ESTAB timer as well event = TCP_E_ABORT; return; } tcpEV << "Performing retransmission #" << state->syn_rexmit_count << "\n"; // resend what's needed switch(fsm.getState()) { case TCP_S_SYN_SENT: sendSyn(); break; case TCP_S_SYN_RCVD: sendSynAck(); break; default: opp_error("Internal error: SYN-REXMIT timer expired while in state %s", stateName(fsm.getState())); } // reschedule timer state->syn_rexmit_timeout *= 2; if (state->syn_rexmit_timeout > TCP_TIMEOUT_SYN_REXMIT_MAX) state->syn_rexmit_timeout = TCP_TIMEOUT_SYN_REXMIT_MAX; scheduleTimeout(synRexmitTimer, state->syn_rexmit_timeout); }
bool TCPConnection::processAckInEstabEtc | ( | TCPSegment * | tcpseg | ) | [protected, virtual] |
Definition at line 1104 of file TCPConnectionRcvSegment.cc.
Referenced by processSegment1stThru8th().
{ tcpEV2 << "Processing ACK in a data transfer state\n"; // //" // If SND.UNA < SEG.ACK =< SND.NXT then, set SND.UNA <- SEG.ACK. // Any segments on the retransmission queue which are thereby // entirely acknowledged are removed. Users should receive // positive acknowledgments for buffers which have been SENT and // fully acknowledged (i.e., SEND buffer should be returned with // "ok" response). If the ACK is a duplicate // (SEG.ACK < SND.UNA), it can be ignored. If the ACK acks // something not yet sent (SEG.ACK > SND.NXT) then send an ACK, // drop the segment, and return. // // If SND.UNA < SEG.ACK =< SND.NXT, the send window should be // updated. If (SND.WL1 < SEG.SEQ or (SND.WL1 = SEG.SEQ and // SND.WL2 =< SEG.ACK)), set SND.WND <- SEG.WND, set // SND.WL1 <- SEG.SEQ, and set SND.WL2 <- SEG.ACK. // // Note that SND.WND is an offset from SND.UNA, that SND.WL1 // records the sequence number of the last segment used to update // SND.WND, and that SND.WL2 records the acknowledgment number of // the last segment used to update SND.WND. The check here // prevents using old segments to update the window. //" // Note: should use SND.MAX instead of SND.NXT in above checks // if (seqGE(state->snd_una, tcpseg->getAckNo())) { // // duplicate ACK? A received TCP segment is a duplicate ACK if all of // the following apply: // (1) snd_una==ackNo // (2) segment contains no data // (3) there's unacked data (snd_una!=snd_max) // // Note: ssfnet uses additional constraint "window is the same as last // received (not an update)" -- we don't do that because window updates // are ignored anyway if neither seqNo nor ackNo has changed. // if (state->snd_una==tcpseg->getAckNo() && tcpseg->getPayloadLength()==0 && state->snd_una!=state->snd_max) { state->dupacks++; if (dupAcksVector) dupAcksVector->record(state->dupacks); // we need to update send window even if the ACK is a dupACK, because rcv win // could have been changed if faulty data receiver is not respecting the "do not shrink window" rule updateWndInfo(tcpseg); tcpAlgorithm->receivedDuplicateAck(); } else { // if doesn't qualify as duplicate ACK, just ignore it. if (tcpseg->getPayloadLength()==0) { if (state->snd_una!=tcpseg->getAckNo()) tcpEV << "Old ACK: ackNo<snd_una\n"; else if (state->snd_una==state->snd_max) tcpEV << "ACK looks duplicate but we have currently no unacked data (snd_una==snd_max)\n"; } // reset counter state->dupacks = 0; if (dupAcksVector) dupAcksVector->record(state->dupacks); } } else if (seqLE(tcpseg->getAckNo(), state->snd_max)) { // ack in window. uint32 old_snd_una = state->snd_una; state->snd_una = tcpseg->getAckNo(); if (unackedVector) unackedVector->record(state->snd_max - state->snd_una); // after retransmitting a lost segment, we may get an ack well ahead of snd_nxt if (seqLess(state->snd_nxt, state->snd_una)) state->snd_nxt = state->snd_una; // RFC 1323, page 36: // "If SND.UNA < SEG.ACK =< SND.NXT then, set SND.UNA <- SEG.ACK. // Also compute a new estimate of round-trip time. If Snd.TS.OK // bit is on, use my.TSclock - SEG.TSecr; otherwise use the // elapsed time since the first segment in the retransmission // queue was sent. Any segments on the retransmission queue // which are thereby entirely acknowledged." if (state->ts_enabled) tcpAlgorithm->rttMeasurementCompleteUsingTS(getTSecr(tcpseg)); // Note: If TS is disabled the RTT measurement is complete in TCPBaseAlg::receivedDataAck() uint32 discardUpToSeq = state->snd_una; // our FIN acked? if (state->send_fin && tcpseg->getAckNo()==state->snd_fin_seq+1) { // set flag that our FIN has been acked tcpEV << "ACK acks our FIN\n"; state->fin_ack_rcvd = true; discardUpToSeq--; // the FIN sequence number is not real data } // acked data no longer needed in send queue sendQueue->discardUpTo(discardUpToSeq); // acked data no longer needed in rexmit queue if (state->sack_enabled) rexmitQueue->discardUpTo(discardUpToSeq); updateWndInfo(tcpseg); // if segment contains data, wait until data has been forwarded to app before sending ACK, // otherwise we would use an old ACKNo if (tcpseg->getPayloadLength() == 0 && fsm.getState()!=TCP_S_SYN_RCVD) { // notify tcpAlgorithm->receivedDataAck(old_snd_una); // in the receivedDataAck we need the old value state->dupacks = 0; if (dupAcksVector) dupAcksVector->record(state->dupacks); } } else { ASSERT(seqGreater(tcpseg->getAckNo(), state->snd_max)); // from if-ladder // send an ACK, drop the segment, and return. tcpAlgorithm->receivedAckForDataNotYetSent(tcpseg->getAckNo()); state->dupacks = 0; if (dupAcksVector) dupAcksVector->record(state->dupacks); return false; // means "drop" } return true; }
bool TCPConnection::processAppCommand | ( | cMessage * | msg | ) | [virtual] |
Process commands from the application. Normally returns true. A return value of false means that the connection structure must be deleted by the caller (TCP).
Definition at line 347 of file TCPConnectionBase.cc.
Referenced by TCP::handleMessage().
{ printConnBrief(); // first do actions TCPCommand *tcpCommand = (TCPCommand *)(msg->removeControlInfo()); TCPEventCode event = preanalyseAppCommandEvent(msg->getKind()); tcpEV << "App command: " << eventName(event) << "\n"; switch (event) { case TCP_E_OPEN_ACTIVE: process_OPEN_ACTIVE(event, tcpCommand, msg); break; case TCP_E_OPEN_PASSIVE: process_OPEN_PASSIVE(event, tcpCommand, msg); break; case TCP_E_SEND: process_SEND(event, tcpCommand, msg); break; case TCP_E_CLOSE: process_CLOSE(event, tcpCommand, msg); break; case TCP_E_ABORT: process_ABORT(event, tcpCommand, msg); break; case TCP_E_STATUS: process_STATUS(event, tcpCommand, msg); break; default: opp_error("wrong event code"); } // then state transitions return performStateTransition(event); }
bool TCPConnection::processMSSOption | ( | TCPSegment * | tcpseg, | |
const TCPOption & | option | |||
) | [protected, virtual] |
Definition at line 848 of file TCPConnectionUtil.cc.
Referenced by readHeaderOptions().
{ if (option.getLength() != 4) { tcpEV << "ERROR: option length incorrect\n"; return false; } if (fsm.getState() != TCP_S_LISTEN && fsm.getState() != TCP_S_SYN_SENT) { tcpEV << "ERROR: TCP Header Option MSS received, but in unexpected state\n"; return false; } if (option.getValuesArraySize() == 0) { // since option.getLength() was already checked, this is a programming error not a TCP error throw cRuntimeError("TCPOption for MSS does not contain the data its getLength() promises"); } // RFC 2581, page 1: // "The SMSS is the size of the largest segment that the sender can transmit. // This value can be based on the maximum transmission unit of the network, // the path MTU discovery [MD90] algorithm, RMSS (see next item), or other // factors. The size does not include the TCP/IP headers and options." // // "The RMSS is the size of the largest segment the receiver is willing to accept. // This is the value specified in the MSS option sent by the receiver during // connection startup. Or, if the MSS option is not used, 536 bytes [Bra89]. // The size does not include the TCP/IP headers and options." // // // The value of snd_mss (SMSS) is set to the minimum of snd_mss (local parameter) and // the value specified in the MSS option received during connection startup. state->snd_mss = std::min(state->snd_mss, (uint32) option.getValues(0)); if (state->snd_mss==0) state->snd_mss = 536; tcpEV << "TCP Header Option MSS(=" << option.getValues(0) << ") received, SMSS is set to: " << state->snd_mss << "\n"; return true; }
TCPEventCode TCPConnection::processRstInSynReceived | ( | TCPSegment * | tcpseg | ) | [protected, virtual] |
Definition at line 1073 of file TCPConnectionRcvSegment.cc.
Referenced by processSegment1stThru8th().
{ tcpEV2 << "Processing RST in SYN_RCVD\n"; //" // If this connection was initiated with a passive OPEN (i.e., // came from the LISTEN state), then return this connection to // LISTEN state and return. The user need not be informed. If // this connection was initiated with an active OPEN (i.e., came // from SYN-SENT state) then the connection was refused, signal // the user "connection refused". In either case, all segments // on the retransmission queue should be removed. And in the // active OPEN case, enter the CLOSED state and delete the TCB, // and return. //" sendQueue->discardUpTo(sendQueue->getBufferEndSeq()); // flush send queue if (state->sack_enabled) rexmitQueue->discardUpTo(rexmitQueue->getBufferEndSeq()); // flush rexmit queue if (state->active) { // signal "connection refused" sendIndicationToApp(TCP_I_CONNECTION_REFUSED); } // on RCV_RST, FSM will go either to LISTEN or to CLOSED, depending on state->active // FIXME if this was a forked connection, it should rather close than go back to listening (otherwise we'd now have two listening connections with the original one!) return TCP_E_RCV_RST; }
bool TCPConnection::processSACKOption | ( | TCPSegment * | tcpseg, | |
const TCPOption & | option | |||
) | [protected, virtual] |
Definition at line 1001 of file TCPConnectionUtil.cc.
Referenced by readHeaderOptions().
{ if (option.getLength() % 8 != 2) { tcpEV << "ERROR: option length incorrect\n"; return false; } if (state->sack_enabled && fsm.getState() != TCP_S_SYN_RCVD && fsm.getState() != TCP_S_ESTABLISHED && fsm.getState() != TCP_S_FIN_WAIT_1 && fsm.getState() != TCP_S_FIN_WAIT_2) { tcpEV << "ERROR: TCP Header Option SACK received, but in unexpected state\n"; return false; } if (!state->sack_enabled) { tcpEV << "ERROR: " << (option.getLength()/2) << ". SACK(s) received, but sack_enabled is set to " << state->sack_enabled << "\n"; return false; } uint n = option.getValuesArraySize()/2; if (n > 0) // sacks present? { tcpEV << n << " SACK(s) received:\n"; uint count=0; for (uint i=0; i<n; i++) { Sack tmp; tmp.setStart(option.getValues(count)); count++; tmp.setEnd(option.getValues(count)); count++; tcpEV << (i+1) << ". SACK:" << " [" << tmp.getStart() << ".." << tmp.getEnd() << ")\n"; // check for D-SACK if (i==0 && seqLess(tmp.getEnd(), tcpseg->getAckNo())) { // RFC 2883, page 8: // "In order for the sender to check that the first (D)SACK block of an // acknowledgement in fact acknowledges duplicate data, the sender // should compare the sequence space in the first SACK block to the // cumulative ACK which is carried IN THE SAME PACKET. If the SACK // sequence space is less than this cumulative ACK, it is an indication // that the segment identified by the SACK block has been received more // than once by the receiver. An implementation MUST NOT compare the // sequence space in the SACK block to the TCP state variable snd.una // (which carries the total cumulative ACK), as this may result in the // wrong conclusion if ACK packets are reordered." tcpEV << "Received D-SACK below cumulative ACK=" << tcpseg->getAckNo() << " D-SACK:" << " [" << tmp.getStart() << ".." << tmp.getEnd() << ")\n"; } else if (i==0 && seqGE(tmp.getEnd(), tcpseg->getAckNo()) && n>1) { // RFC 2883, page 8: // "If the sequence space in the first SACK block is greater than the // cumulative ACK, then the sender next compares the sequence space in // the first SACK block with the sequence space in the second SACK // block, if there is one. This comparison can determine if the first // SACK block is reporting duplicate data that lies above the cumulative // ACK." Sack tmp2; tmp2.setStart(option.getValues(2)); tmp2.setEnd(option.getValues(3)); if (seqGE(tmp.getStart(), tmp2.getStart()) && seqLE(tmp.getEnd(), tmp2.getEnd())) {tcpEV << "Received D-SACK above cumulative ACK=" << tcpseg->getAckNo() << " D-SACK:" << " [" << tmp.getStart() << ".." << tmp.getEnd() << ") SACK:" << " [" << tmp2.getStart() << ".." << tmp2.getEnd() << ")\n";} } if (seqGreater(tmp.getEnd(), tcpseg->getAckNo())) rexmitQueue->setSackedBit(tmp.getStart(), tmp.getEnd()); } state->rcv_sacks = state->rcv_sacks + n; // total counter, no current number if (rcvSacksVector) rcvSacksVector->record(state->rcv_sacks); // update scoreboard state->sackedBytes_old = state->sackedBytes; // needed for RFC 3042 to check if last dupAck contained new sack information state->sackedBytes = rexmitQueue->getTotalAmountOfSackedBytes(); if (sackedBytesVector) sackedBytesVector->record(state->sackedBytes); } return true; }
bool TCPConnection::processSACKPermittedOption | ( | TCPSegment * | tcpseg, | |
const TCPOption & | option | |||
) | [protected, virtual] |
Definition at line 981 of file TCPConnectionUtil.cc.
Referenced by readHeaderOptions().
{ if (option.getLength() != 2) { tcpEV << "ERROR: length incorrect\n"; return false; } if (fsm.getState() != TCP_S_LISTEN && fsm.getState() != TCP_S_SYN_SENT) { tcpEV << "ERROR: TCP Header Option SACK_PERMITTED received, but in unexpected state\n"; return false; } state->rcv_sack_perm = true; state->sack_enabled = state->sack_support && state->snd_sack_perm && state->rcv_sack_perm; tcpEV << "TCP Header Option SACK_PERMITTED received, SACK (sack_enabled) is set to: " << state->sack_enabled << "\n"; return true; }
TCPEventCode TCPConnection::processSegment1stThru8th | ( | TCPSegment * | tcpseg | ) | [protected, virtual] |
Definition at line 114 of file TCPConnectionRcvSegment.cc.
Referenced by process_RCV_SEGMENT().
{ // // RFC 793: first check sequence number // bool acceptable = true; if (tcpseg->getHeaderLength() > TCP_HEADER_OCTETS) // Header options present? TCP_HEADER_OCTETS = 20 { // PAWS if (state->ts_enabled) { uint32 tsval = getTSval(tcpseg); // FIXME check TS rounding on all other code if (tsval != 0 && seqLess(tsval, state->ts_recent) && (simTime() - state->time_last_data_sent) > PAWS_IDLE_TIME_THRESH) // PAWS_IDLE_TIME_THRESH = 24 days { tcpEV << "PAWS: Segment is not acceptable, TSval=" << tsval << " in " << stateName(fsm.getState()) << " state received: dropping segment\n"; acceptable = false; } } readHeaderOptions(tcpseg); } if (acceptable) acceptable = isSegmentAcceptable(tcpseg); if (!acceptable) { //" // If an incoming segment is not acceptable, an acknowledgment // should be sent in reply (unless the RST bit is set, if so drop // the segment and return): // // <SEQ=SND.NXT><ACK=RCV.NXT><CTL=ACK> //" if (tcpseg->getRstBit()) { tcpEV << "RST with unacceptable seqNum: dropping\n"; } else { if (tcpseg->getSynBit()) { tcpEV << "SYN with unacceptable seqNum in " << stateName(fsm.getState()) << " state received (SYN duplicat?)\n"; } else if (state->sack_enabled && seqLess((tcpseg->getSequenceNo()+tcpseg->getPayloadLength()), state->rcv_nxt)) { state->start_seqno = tcpseg->getSequenceNo(); state->end_seqno = tcpseg->getSequenceNo() + tcpseg->getPayloadLength(); state->snd_dsack = true; tcpEV << "SND_D-SACK SET (dupseg rcvd)\n"; } tcpEV << "Segment seqNum not acceptable, sending ACK with current receive seq\n"; // RFC 2018, page 4: // "The receiver SHOULD send an ACK for every valid segment that arrives // containing new data, and each of these "duplicate" ACKs SHOULD bear a // SACK option." // // The received segment is not "valid" therefore the ACK will not bear a SACK option, if snd_dsack (D-SACK) is not set. sendAck(); } return TCP_E_IGNORE; } // // RFC 793: second check the RST bit, // if (tcpseg->getRstBit()) { // Note: if we come from LISTEN, processSegmentInListen() has already handled RST. switch (fsm.getState()) { case TCP_S_SYN_RCVD: //" // If this connection was initiated with a passive OPEN (i.e., // came from the LISTEN state), then return this connection to // LISTEN state and return. The user need not be informed. If // this connection was initiated with an active OPEN (i.e., came // from SYN-SENT state) then the connection was refused, signal // the user "connection refused". In either case, all segments // on the retransmission queue should be removed. And in the // active OPEN case, enter the CLOSED state and delete the TCB, // and return. //" return processRstInSynReceived(tcpseg); case TCP_S_ESTABLISHED: case TCP_S_FIN_WAIT_1: case TCP_S_FIN_WAIT_2: case TCP_S_CLOSE_WAIT: //" // If the RST bit is set then, any outstanding RECEIVEs and SEND // should receive "reset" responses. All segment queues should be // flushed. Users should also receive an unsolicited general // "connection reset" signal. // // Enter the CLOSED state, delete the TCB, and return. //" tcpEV << "RST: performing connection reset, closing connection\n"; sendIndicationToApp(TCP_I_CONNECTION_RESET); return TCP_E_RCV_RST; // this will trigger state transition case TCP_S_CLOSING: case TCP_S_LAST_ACK: case TCP_S_TIME_WAIT: //" // enter the CLOSED state, delete the TCB, and return. //" tcpEV << "RST: closing connection\n"; if (fsm.getState()!=TCP_S_TIME_WAIT) sendIndicationToApp(TCP_I_CLOSED); // in TIME_WAIT, we've already sent it return TCP_E_RCV_RST; // this will trigger state transition default: ASSERT(0); } } // RFC 793: third check security and precedence // This step is ignored. // // RFC 793: fourth, check the SYN bit, // if (tcpseg->getSynBit()) { //" // If the SYN is in the window it is an error, send a reset, any // outstanding RECEIVEs and SEND should receive "reset" responses, // all segment queues should be flushed, the user should also // receive an unsolicited general "connection reset" signal, enter // the CLOSED state, delete the TCB, and return. // // If the SYN is not in the window this step would not be reached // and an ack would have been sent in the first step (sequence // number check). //" ASSERT(isSegmentAcceptable(tcpseg)); // assert SYN is in the window tcpEV << "SYN is in the window: performing connection reset, closing connection\n"; sendIndicationToApp(TCP_I_CONNECTION_RESET); return TCP_E_RCV_UNEXP_SYN; } // // RFC 793: fifth check the ACK field, // if (!tcpseg->getAckBit()) { // if the ACK bit is off drop the segment and return tcpEV << "ACK not set, dropping segment\n"; return TCP_E_IGNORE; } uint32 old_snd_una = state->snd_una; TCPEventCode event = TCP_E_IGNORE; if (fsm.getState()==TCP_S_SYN_RCVD) { //" // If SND.UNA =< SEG.ACK =< SND.NXT then enter ESTABLISHED state // and continue processing. // // If the segment acknowledgment is not acceptable, form a // reset segment, // // <SEQ=SEG.ACK><CTL=RST> // // and send it. //" if (!seqLE(state->snd_una,tcpseg->getAckNo()) || !seqLE(tcpseg->getAckNo(),state->snd_nxt)) { sendRst(tcpseg->getAckNo()); return TCP_E_IGNORE; } // notify tcpAlgorithm and app layer tcpAlgorithm->established(false); sendEstabIndicationToApp(); // This will trigger transition to ESTABLISHED. Timers and notifying // app will be taken care of in stateEntered(). event = TCP_E_RCV_ACK; } uint32 old_snd_nxt = state->snd_nxt; // later we'll need to see if snd_nxt changed // Note: If one of the last data segments is lost while already in LAST-ACK state (e.g. if using TCPEchoApps) // TCP must be able to process acceptable acknowledgments, however please note RFC 793, page 73: // "LAST-ACK STATE // The only thing that can arrive in this state is an // acknowledgment of our FIN. If our FIN is now acknowledged, // delete the TCB, enter the CLOSED state, and return." if (fsm.getState()==TCP_S_SYN_RCVD || fsm.getState()==TCP_S_ESTABLISHED || fsm.getState()==TCP_S_FIN_WAIT_1 || fsm.getState()==TCP_S_FIN_WAIT_2 || fsm.getState()==TCP_S_CLOSE_WAIT || fsm.getState()==TCP_S_CLOSING || fsm.getState()==TCP_S_LAST_ACK) { // // ESTABLISHED processing: //" // If SND.UNA < SEG.ACK =< SND.NXT then, set SND.UNA <- SEG.ACK. // Any segments on the retransmission queue which are thereby // entirely acknowledged are removed. Users should receive // positive acknowledgments for buffers which have been SENT and // fully acknowledged (i.e., SEND buffer should be returned with // "ok" response). If the ACK is a duplicate // (SEG.ACK < SND.UNA), it can be ignored. If the ACK acks // something not yet sent (SEG.ACK > SND.NXT) then send an ACK, // drop the segment, and return. // // If SND.UNA < SEG.ACK =< SND.NXT, the send window should be // updated. If (SND.WL1 < SEG.SEQ or (SND.WL1 = SEG.SEQ and // SND.WL2 =< SEG.ACK)), set SND.WND <- SEG.WND, set // SND.WL1 <- SEG.SEQ, and set SND.WL2 <- SEG.ACK. // // Note that SND.WND is an offset from SND.UNA, that SND.WL1 // records the sequence number of the last segment used to update // SND.WND, and that SND.WL2 records the acknowledgment number of // the last segment used to update SND.WND. The check here // prevents using old segments to update the window. //" bool ok = processAckInEstabEtc(tcpseg); if (!ok) return TCP_E_IGNORE; // if acks something not yet sent, drop it } if ((fsm.getState()==TCP_S_FIN_WAIT_1 && state->fin_ack_rcvd)) { //" // FIN-WAIT-1 STATE // In addition to the processing for the ESTABLISHED state, if // our FIN is now acknowledged then enter FIN-WAIT-2 and continue // processing in that state. //" event = TCP_E_RCV_ACK; // will trigger transition to FIN-WAIT-2 } if (fsm.getState()==TCP_S_FIN_WAIT_2) { //" // FIN-WAIT-2 STATE // In addition to the processing for the ESTABLISHED state, if // the retransmission queue is empty, the user's CLOSE can be // acknowledged ("ok") but do not delete the TCB. //" // nothing to do here (in our model, used commands don't need to be // acknowledged) } if (fsm.getState()==TCP_S_CLOSING) { //" // In addition to the processing for the ESTABLISHED state, if // the ACK acknowledges our FIN then enter the TIME-WAIT state, // otherwise ignore the segment. //" if (state->fin_ack_rcvd) { tcpEV << "Our FIN acked -- can go to TIME_WAIT now\n"; event = TCP_E_RCV_ACK; // will trigger transition to TIME-WAIT scheduleTimeout(the2MSLTimer, TCP_TIMEOUT_2MSL); // start timer // we're entering TIME_WAIT, so we can signal CLOSED the user // (the only thing left to do is wait until the 2MSL timer expires) sendIndicationToApp(TCP_I_CLOSED); } } if (fsm.getState()==TCP_S_LAST_ACK) { //" // The only thing that can arrive in this state is an // acknowledgment of our FIN. If our FIN is now acknowledged, // delete the TCB, enter the CLOSED state, and return. //" if (state->send_fin && tcpseg->getAckNo()==state->snd_fin_seq+1) { tcpEV << "Last ACK arrived\n"; sendIndicationToApp(TCP_I_CLOSED); return TCP_E_RCV_ACK; // will trigger transition to CLOSED } } if (fsm.getState()==TCP_S_TIME_WAIT) { //" // The only thing that can arrive in this state is a // retransmission of the remote FIN. Acknowledge it, and restart // the 2 MSL timeout. //" // And we are staying in the TIME_WAIT state. // sendAck(); cancelEvent(the2MSLTimer); scheduleTimeout(the2MSLTimer, TCP_TIMEOUT_2MSL); } // // RFC 793: sixth, check the URG bit, // if (tcpseg->getUrgBit() && (fsm.getState()==TCP_S_ESTABLISHED || fsm.getState()==TCP_S_FIN_WAIT_1 || fsm.getState()==TCP_S_FIN_WAIT_2)) { //" // If the URG bit is set, RCV.UP <- max(RCV.UP,SEG.UP), and signal // the user that the remote side has urgent data if the urgent // pointer (RCV.UP) is in advance of the data consumed. If the // user has already been signaled (or is still in the "urgent // mode") for this continuous sequence of urgent data, do not // signal the user again. //" // TBD: URG currently not supported } // // RFC 793: seventh, process the segment text, // uint32 old_rcv_nxt = state->rcv_nxt; // if rcv_nxt changes, we need to send/schedule an ACK if (fsm.getState()==TCP_S_SYN_RCVD || fsm.getState()==TCP_S_ESTABLISHED || fsm.getState()==TCP_S_FIN_WAIT_1 || fsm.getState()==TCP_S_FIN_WAIT_2) { //" // Once in the ESTABLISHED state, it is possible to deliver segment // text to user RECEIVE buffers. Text from segments can be moved // into buffers until either the buffer is full or the segment is // empty. If the segment empties and carries an PUSH flag, then // the user is informed, when the buffer is returned, that a PUSH // has been received. // // When the TCP takes responsibility for delivering the data to the // user it must also acknowledge the receipt of the data. // // Once the TCP takes responsibility for the data it advances // RCV.NXT over the data accepted, and adjusts RCV.WND as // apporopriate to the current buffer availability. The total of // RCV.NXT and RCV.WND should not be reduced. // // Please note the window management suggestions in section 3.7. // // Send an acknowledgment of the form: // // <SEQ=SND.NXT><ACK=RCV.NXT><CTL=ACK> // // This acknowledgment should be piggybacked on a segment being // transmitted if possible without incurring undue delay. //" tcpseg->truncateSegment(state->rcv_nxt, state->rcv_nxt + state->rcv_wnd); if (tcpseg->getPayloadLength()>0) { // check for full sized segment if (tcpseg->getPayloadLength() == state->snd_mss || tcpseg->getPayloadLength() + tcpseg->getHeaderLength() - TCP_HEADER_OCTETS == state->snd_mss) state->full_sized_segment_counter++; // check for persist probe if (tcpseg->getPayloadLength() == 1) state->ack_now = true; // TODO how to check if it is really a persist probe? updateRcvQueueVars(); if (state->freeRcvBuffer >= tcpseg->getPayloadLength()) // enough freeRcvBuffer in rcvQueue for new segment? { tcpEV2 << "Processing segment text in a data transfer state\n"; // insert into receive buffers. If this segment is contiguous with // previously received ones (seqNo==rcv_nxt), rcv_nxt can be increased; // otherwise it stays the same but the data must be cached nevertheless // (to avoid "Failure to retain above-sequence data" problem, RFC 2525 // section 2.5). uint32 old_usedRcvBuffer = state->usedRcvBuffer; state->rcv_nxt = receiveQueue->insertBytesFromSegment(tcpseg); if (seqGreater(state->snd_una, old_snd_una)) { // notify tcpAlgorithm->receivedDataAck(old_snd_una); // in the receivedDataAck we need the old value state->dupacks = 0; if (dupAcksVector) dupAcksVector->record(state->dupacks); } // out-of-order segment? if (old_rcv_nxt==state->rcv_nxt) { state->rcv_oooseg++; if (rcvOooSegVector) rcvOooSegVector->record(state->rcv_oooseg); // RFC 2018, page 4: // "The receiver SHOULD send an ACK for every valid segment that arrives // containing new data, and each of these "duplicate" ACKs SHOULD bear a // SACK option." if (state->sack_enabled) { // store start and end sequence numbers of current oooseg in state variables state->start_seqno = tcpseg->getSequenceNo(); state->end_seqno = tcpseg->getSequenceNo() + tcpseg->getPayloadLength(); if (old_usedRcvBuffer == receiveQueue->getAmountOfBufferedBytes()) // D-SACK { state->snd_dsack = true; tcpEV << "SND_D-SACK SET (old_rcv_nxt==rcv_nxt duplicated oooseg rcvd)\n"; } else // SACK { state->snd_sack = true; tcpEV << "SND_SACK SET (old_rcv_nxt==rcv_nxt oooseg rcvd)\n"; } } tcpAlgorithm->receivedOutOfOrderSegment(); } else { // forward data to app // // FIXME observe PSH bit // // FIXME we should implement socket READ command, and pass up only // as many bytes as requested. rcv_wnd should be decreased // accordingly! // cPacket *msg; while ((msg=receiveQueue->extractBytesUpTo(state->rcv_nxt))!=NULL) { msg->setKind(TCP_I_DATA); // TBD currently we never send TCP_I_URGENT_DATA TCPCommand *cmd = new TCPCommand(); cmd->setConnId(connId); msg->setControlInfo(cmd); sendToApp(msg); } // if this segment "filled the gap" until the previously arrived segment // that carried a FIN (i.e.rcv_nxt==rcv_fin_seq), we have to advance // rcv_nxt over the FIN. if (state->fin_rcvd && state->rcv_nxt==state->rcv_fin_seq) { state->ack_now = true; // although not mentioned in [Stevens, W.R.: TCP/IP Illustrated, Volume 2, page 861] seems like we have to set ack_now tcpEV << "All segments arrived up to the FIN segment, advancing rcv_nxt over the FIN\n"; state->rcv_nxt = state->rcv_fin_seq+1; // state transitions will be done in the state machine, here we just set // the proper event code (TCP_E_RCV_FIN or TCP_E_RCV_FIN_ACK) event = TCP_E_RCV_FIN; switch (fsm.getState()) { case TCP_S_FIN_WAIT_1: if (state->fin_ack_rcvd) { event = TCP_E_RCV_FIN_ACK; // start the time-wait timer, turn off the other timers cancelEvent(finWait2Timer); scheduleTimeout(the2MSLTimer, TCP_TIMEOUT_2MSL); // we're entering TIME_WAIT, so we can signal CLOSED the user // (the only thing left to do is wait until the 2MSL timer expires) sendIndicationToApp(TCP_I_CLOSED); } break; case TCP_S_FIN_WAIT_2: // Start the time-wait timer, turn off the other timers. cancelEvent(finWait2Timer); scheduleTimeout(the2MSLTimer, TCP_TIMEOUT_2MSL); // we're entering TIME_WAIT, so we can signal CLOSED the user // (the only thing left to do is wait until the 2MSL timer expires) sendIndicationToApp(TCP_I_CLOSED); break; case TCP_S_TIME_WAIT: // Restart the 2 MSL time-wait timeout. cancelEvent(the2MSLTimer); scheduleTimeout(the2MSLTimer, TCP_TIMEOUT_2MSL); break; } sendIndicationToApp(TCP_I_PEER_CLOSED); } } } else // not enough freeRcvBuffer in rcvQueue for new segment { state->tcpRcvQueueDrops++; // update current number of tcp receive queue drops if (tcpRcvQueueDropsVector) tcpRcvQueueDropsVector->record(state->tcpRcvQueueDrops); // if the ACK bit is off drop the segment and return tcpEV << "RcvQueueBuffer has run out, dropping segment\n"; return TCP_E_IGNORE; } } } // // RFC 793: eighth, check the FIN bit, // if (tcpseg->getFinBit()) { state->ack_now = true; //" // If the FIN bit is set, signal the user "connection closing" and // return any pending RECEIVEs with same message, advance RCV.NXT // over the FIN, and send an acknowledgment for the FIN. Note that // FIN implies PUSH for any segment text not yet delivered to the // user. //" // Note: seems like RFC 793 is not entirely correct here: if the // segment is "above sequence" (ie. RCV.NXT < SEG.SEQ), we cannot // advance RCV.NXT over the FIN. Instead we remember this sequence // number and do it later. uint32 fin_seq = (uint32)tcpseg->getSequenceNo() + (uint32)tcpseg->getPayloadLength(); if (state->rcv_nxt==fin_seq) { // advance rcv_nxt over FIN now tcpEV << "FIN arrived, advancing rcv_nxt over the FIN\n"; state->rcv_nxt++; // state transitions will be done in the state machine, here we just set // the proper event code (TCP_E_RCV_FIN or TCP_E_RCV_FIN_ACK) event = TCP_E_RCV_FIN; switch (fsm.getState()) { case TCP_S_FIN_WAIT_1: if (state->fin_ack_rcvd) { event = TCP_E_RCV_FIN_ACK; // start the time-wait timer, turn off the other timers cancelEvent(finWait2Timer); scheduleTimeout(the2MSLTimer, TCP_TIMEOUT_2MSL); // we're entering TIME_WAIT, so we can signal CLOSED the user // (the only thing left to do is wait until the 2MSL timer expires) sendIndicationToApp(TCP_I_CLOSED); } break; case TCP_S_FIN_WAIT_2: // Start the time-wait timer, turn off the other timers. cancelEvent(finWait2Timer); scheduleTimeout(the2MSLTimer, TCP_TIMEOUT_2MSL); // we're entering TIME_WAIT, so we can signal CLOSED the user // (the only thing left to do is wait until the 2MSL timer expires) sendIndicationToApp(TCP_I_CLOSED); break; case TCP_S_TIME_WAIT: // Restart the 2 MSL time-wait timeout. cancelEvent(the2MSLTimer); scheduleTimeout(the2MSLTimer, TCP_TIMEOUT_2MSL); break; } sendIndicationToApp(TCP_I_PEER_CLOSED); } else { // we'll have to do it later (when an arriving segment "fills the gap") tcpEV << "FIN segment above sequence, storing sequence number of FIN\n"; state->fin_rcvd = true; state->rcv_fin_seq = fin_seq; } // TBD do PUSH stuff } if (old_rcv_nxt!=state->rcv_nxt) { // if rcv_nxt changed, either because we received segment text or we // received a FIN that needs to be acked (or both), we need to send or // schedule an ACK. if (state->sack_enabled) { if (receiveQueue->getQueueLength()!=0) { // RFC 2018, page 4: // "If sent at all, SACK options SHOULD be included in all ACKs which do // not ACK the highest sequence number in the data receiver's queue." state->start_seqno = tcpseg->getSequenceNo(); state->end_seqno = tcpseg->getSequenceNo() + tcpseg->getPayloadLength(); state->snd_sack = true; tcpEV << "SND_SACK SET (rcv_nxt changed, but rexmitQ is not empty)\n"; state->ack_now = true; // although not mentioned in [Stevens, W.R.: TCP/IP Illustrated, Volume 2, page 861] seems like we have to set ack_now } } // tcpAlgorithm decides when and how to do ACKs tcpAlgorithm->receiveSeqChanged(); } if ((fsm.getState()==TCP_S_ESTABLISHED || fsm.getState()==TCP_S_SYN_RCVD) && state->send_fin && state->snd_nxt==state->snd_fin_seq+1) { // if the user issued the CLOSE command a long time ago and we've just // managed to send off FIN, we simulate a CLOSE command now (we had to // defer it at that time because we still had data in the send queue.) // This CLOSE will take us into the FIN_WAIT_1 state. tcpEV << "Now we can do the CLOSE which was deferred a while ago\n"; event = TCP_E_CLOSE; } if (fsm.getState()==TCP_S_CLOSE_WAIT && state->send_fin && state->snd_nxt==state->snd_fin_seq+1 && old_snd_nxt!=state->snd_nxt) { // if we're in CLOSE_WAIT and we just got to sent our long-pending FIN, // we simulate a CLOSE command now (we had to defer it at that time because // we still had data in the send queue.) This CLOSE will take us into the // LAST_ACK state. tcpEV << "Now we can do the CLOSE which was deferred a while ago\n"; event = TCP_E_CLOSE; } return event; }
TCPEventCode TCPConnection::processSegmentInListen | ( | TCPSegment * | tcpseg, | |
IPvXAddress | src, | |||
IPvXAddress | dest | |||
) | [protected, virtual] |
Definition at line 732 of file TCPConnectionRcvSegment.cc.
Referenced by process_RCV_SEGMENT().
{ tcpEV2 << "Processing segment in LISTEN\n"; //" // first check for an RST // An incoming RST should be ignored. Return. //" if (tcpseg->getRstBit()) { tcpEV << "RST bit set: dropping segment\n"; return TCP_E_IGNORE; } //" // second check for an ACK // Any acknowledgment is bad if it arrives on a connection still in // the LISTEN state. An acceptable reset segment should be formed // for any arriving ACK-bearing segment. The RST should be // formatted as follows: // // <SEQ=SEG.ACK><CTL=RST> // // Return. //" if (tcpseg->getAckBit()) { tcpEV << "ACK bit set: dropping segment and sending RST\n"; sendRst(tcpseg->getAckNo(),destAddr,srcAddr,tcpseg->getDestPort(),tcpseg->getSrcPort()); return TCP_E_IGNORE; } //" // third check for a SYN //" if (tcpseg->getSynBit()) { if (tcpseg->getFinBit()) { // Looks like implementations vary on how to react to SYN+FIN. // Some treat it as plain SYN (and reply with SYN+ACK), some send RST+ACK. // Let's just do the former here. tcpEV << "SYN+FIN received: ignoring FIN\n"; } tcpEV << "SYN bit set: filling in foreign socket and sending SYN+ACK\n"; //" // If the listen was not fully specified (i.e., the foreign socket was not // fully specified), then the unspecified fields should be filled in now. //" // // Also, we may need to fork, in order to leave another connection // LISTENing on the port. Note: forking will change our connId. // if (state->fork) { TCPConnection *conn = cloneListeningConnection(); // "conn" is the clone which will stay LISTENing, while "this" gets updated with the remote address tcpMain->addForkedConnection(this, conn, destAddr, srcAddr, tcpseg->getDestPort(), tcpseg->getSrcPort()); tcpEV << "Connection forked: this connection got new connId=" << connId << ", " "spinoff keeps LISTENing with connId=" << conn->connId << "\n"; } else { tcpMain->updateSockPair(this, destAddr, srcAddr, tcpseg->getDestPort(), tcpseg->getSrcPort()); } //" // Set RCV.NXT to SEG.SEQ+1, IRS is set to SEG.SEQ and any other // control or text should be queued for processing later. ISS // should be selected and a SYN segment sent of the form: // // <SEQ=ISS><ACK=RCV.NXT><CTL=SYN,ACK> // // SND.NXT is set to ISS+1 and SND.UNA to ISS. The connection // state should be changed to SYN-RECEIVED. //" state->rcv_nxt = tcpseg->getSequenceNo()+1; state->rcv_adv = state->rcv_nxt + state->rcv_wnd; if (rcvAdvVector) rcvAdvVector->record(state->rcv_adv); state->irs = tcpseg->getSequenceNo(); receiveQueue->init(state->rcv_nxt); // FIXME may init twice... selectInitialSeqNum(); // although not mentioned in RFC 793, seems like we have to pick up // initial snd_wnd from the segment here. updateWndInfo(tcpseg, true); if (tcpseg->getHeaderLength() > TCP_HEADER_OCTETS) // Header options present? TCP_HEADER_OCTETS = 20 readHeaderOptions(tcpseg); state->ack_now = true; sendSynAck(); startSynRexmitTimer(); if (!connEstabTimer->isScheduled()) scheduleTimeout(connEstabTimer, TCP_TIMEOUT_CONN_ESTAB); //" // Note that any other incoming control or data (combined with SYN) // will be processed in the SYN-RECEIVED state, but processing of SYN // and ACK should not be repeated. //" // We don't send text in SYN or SYN+ACK, but accept it. Otherwise // there isn't much left to do: RST, SYN, ACK, FIN got processed already, // so there's only URG and PSH left to handle. // if (tcpseg->getPayloadLength()>0) { updateRcvQueueVars(); if (state->freeRcvBuffer >= tcpseg->getPayloadLength()) // enough freeRcvBuffer in rcvQueue for new segment? { receiveQueue->insertBytesFromSegment(tcpseg); } else // not enough freeRcvBuffer in rcvQueue for new segment { state->tcpRcvQueueDrops++; // update current number of tcp receive queue drops if (tcpRcvQueueDropsVector) tcpRcvQueueDropsVector->record(state->tcpRcvQueueDrops); tcpEV << "RcvQueueBuffer has run out, dropping segment\n"; return TCP_E_IGNORE; } } if (tcpseg->getUrgBit() || tcpseg->getPshBit()) tcpEV << "Ignoring URG and PSH bits in SYN\n"; // TBD return TCP_E_RCV_SYN; // this will take us to SYN_RCVD } //" // fourth other text or control // So you are unlikely to get here, but if you do, drop the segment, and return. //" tcpEV << "Unexpected segment: dropping it\n"; return TCP_E_IGNORE; }
TCPEventCode TCPConnection::processSegmentInSynSent | ( | TCPSegment * | tcpseg, | |
IPvXAddress | src, | |||
IPvXAddress | dest | |||
) | [protected, virtual] |
Definition at line 869 of file TCPConnectionRcvSegment.cc.
Referenced by process_RCV_SEGMENT().
{ tcpEV2 << "Processing segment in SYN_SENT\n"; //" // first check the ACK bit // // If the ACK bit is set // // If SEG.ACK =< ISS, or SEG.ACK > SND.NXT, send a reset (unless // the RST bit is set, if so drop the segment and return) // // <SEQ=SEG.ACK><CTL=RST> // // and discard the segment. Return. // // If SND.UNA =< SEG.ACK =< SND.NXT then the ACK is acceptable. //" if (tcpseg->getAckBit()) { if (seqLE(tcpseg->getAckNo(),state->iss) || seqGreater(tcpseg->getAckNo(),state->snd_nxt)) { tcpEV << "ACK bit set but wrong AckNo, sending RST\n"; sendRst(tcpseg->getAckNo(),destAddr,srcAddr,tcpseg->getDestPort(),tcpseg->getSrcPort()); return TCP_E_IGNORE; } tcpEV << "ACK bit set, AckNo acceptable\n"; } //" // second check the RST bit // // If the RST bit is set // // If the ACK was acceptable then signal the user "error: // connection reset", drop the segment, enter CLOSED state, // delete TCB, and return. Otherwise (no ACK) drop the segment // and return. //" if (tcpseg->getRstBit()) { if (tcpseg->getAckBit()) { tcpEV << "RST+ACK: performing connection reset\n"; sendIndicationToApp(TCP_I_CONNECTION_RESET); return TCP_E_RCV_RST; } else { tcpEV << "RST without ACK: dropping segment\n"; return TCP_E_IGNORE; } } //" // third check the security and precedence -- not done // // fourth check the SYN bit // // This step should be reached only if the ACK is ok, or there is // no ACK, and it the segment did not contain a RST. // // If the SYN bit is on and the security/compartment and precedence // are acceptable then, //" if (tcpseg->getSynBit()) { // // RCV.NXT is set to SEG.SEQ+1, IRS is set to // SEG.SEQ. SND.UNA should be advanced to equal SEG.ACK (if there // is an ACK), and any segments on the retransmission queue which // are thereby acknowledged should be removed. // state->rcv_nxt = tcpseg->getSequenceNo()+1; state->rcv_adv = state->rcv_nxt + state->rcv_wnd; if (rcvAdvVector) rcvAdvVector->record(state->rcv_adv); state->irs = tcpseg->getSequenceNo(); receiveQueue->init(state->rcv_nxt); if (tcpseg->getAckBit()) { state->snd_una = tcpseg->getAckNo(); sendQueue->discardUpTo(state->snd_una); if (state->sack_enabled) rexmitQueue->discardUpTo(state->snd_una); // although not mentioned in RFC 793, seems like we have to pick up // initial snd_wnd from the segment here. updateWndInfo(tcpseg, true); } // this also seems to be a good time to learn our local IP address // (was probably unspecified at connection open) tcpMain->updateSockPair(this, destAddr, srcAddr, tcpseg->getDestPort(), tcpseg->getSrcPort()); //" // If SND.UNA > ISS (our SYN has been ACKed), change the connection // state to ESTABLISHED, form an ACK segment // // <SEQ=SND.NXT><ACK=RCV.NXT><CTL=ACK> // // and send it. Data or controls which were queued for // transmission may be included. If there are other controls or // text in the segment then continue processing at the sixth step // below where the URG bit is checked, otherwise return. //" if (seqGreater(state->snd_una, state->iss)) { tcpEV << "SYN+ACK bits set, connection established.\n"; // RFC says "continue processing at the sixth step below where // the URG bit is checked". Those steps deal with: URG, segment text // (and PSH), and FIN. // Now: URG and PSH we don't support yet; in SYN+FIN we ignore FIN; // with segment text we just take it easy and put it in the receiveQueue // -- we'll forward it to the user when more data arrives. if (tcpseg->getFinBit()) tcpEV << "SYN+ACK+FIN received: ignoring FIN\n"; if (tcpseg->getPayloadLength()>0) { updateRcvQueueVars(); if (state->freeRcvBuffer >= tcpseg->getPayloadLength()) // enough freeRcvBuffer in rcvQueue for new segment? { receiveQueue->insertBytesFromSegment(tcpseg); // TBD forward to app, etc. } else // not enough freeRcvBuffer in rcvQueue for new segment { state->tcpRcvQueueDrops++; // update current number of tcp receive queue drops if (tcpRcvQueueDropsVector) tcpRcvQueueDropsVector->record(state->tcpRcvQueueDrops); tcpEV << "RcvQueueBuffer has run out, dropping segment\n"; return TCP_E_IGNORE; } } if (tcpseg->getUrgBit() || tcpseg->getPshBit()) tcpEV << "Ignoring URG and PSH bits in SYN+ACK\n"; // TBD if (tcpseg->getHeaderLength() > TCP_HEADER_OCTETS) // Header options present? TCP_HEADER_OCTETS = 20 readHeaderOptions(tcpseg); // notify tcpAlgorithm (it has to send ACK of SYN) and app layer state->ack_now = true; tcpAlgorithm->established(true); sendEstabIndicationToApp(); // This will trigger transition to ESTABLISHED. Timers and notifying // app will be taken care of in stateEntered(). return TCP_E_RCV_SYN_ACK; } //" // Otherwise enter SYN-RECEIVED, form a SYN,ACK segment // // <SEQ=ISS><ACK=RCV.NXT><CTL=SYN,ACK> // // and send it. If there are other controls or text in the // segment, queue them for processing after the ESTABLISHED state // has been reached, return. //" tcpEV << "SYN bit set: sending SYN+ACK\n"; state->snd_max = state->snd_nxt = state->iss; sendSynAck(); startSynRexmitTimer(); // Note: code below is similar to processing SYN in LISTEN. // For consistency with that code, we ignore SYN+FIN here if (tcpseg->getFinBit()) tcpEV << "SYN+FIN received: ignoring FIN\n"; // We don't send text in SYN or SYN+ACK, but accept it. Otherwise // there isn't much left to do: RST, SYN, ACK, FIN got processed already, // so there's only URG and PSH left to handle. if (tcpseg->getPayloadLength()>0) { updateRcvQueueVars(); if (state->freeRcvBuffer >= tcpseg->getPayloadLength()) // enough freeRcvBuffer in rcvQueue for new segment? { receiveQueue->insertBytesFromSegment(tcpseg); // TBD forward to app, etc. } else // not enough freeRcvBuffer in rcvQueue for new segment { state->tcpRcvQueueDrops++; // update current number of tcp receive queue drops if (tcpRcvQueueDropsVector) tcpRcvQueueDropsVector->record(state->tcpRcvQueueDrops); tcpEV << "RcvQueueBuffer has run out, dropping segment\n"; return TCP_E_IGNORE; } } if (tcpseg->getUrgBit() || tcpseg->getPshBit()) tcpEV << "Ignoring URG and PSH bits in SYN\n"; // TBD return TCP_E_RCV_SYN; } //" // fifth, if neither of the SYN or RST bits is set then drop the // segment and return. //" return TCP_E_IGNORE; }
bool TCPConnection::processTCPSegment | ( | TCPSegment * | tcpSeg, | |
IPvXAddress | srcAddr, | |||
IPvXAddress | destAddr | |||
) | [virtual] |
Process incoming TCP segment. Normally returns true. A return value of false means that the connection structure must be deleted by the caller (TCP).
Definition at line 323 of file TCPConnectionBase.cc.
Referenced by TCP::handleMessage().
{ printConnBrief(); if (!localAddr.isUnspecified()) { ASSERT(localAddr==segDestAddr); ASSERT(localPort==tcpseg->getDestPort()); } if (!remoteAddr.isUnspecified()) { ASSERT(remoteAddr==segSrcAddr); ASSERT(remotePort==tcpseg->getSrcPort()); } if (tryFastRoute(tcpseg)) return true; // first do actions TCPEventCode event = process_RCV_SEGMENT(tcpseg, segSrcAddr, segDestAddr); // then state transitions return performStateTransition(event); }
bool TCPConnection::processTimer | ( | cMessage * | msg | ) | [virtual] |
Process self-messages (timers). Normally returns true. A return value of false means that the connection structure must be deleted by the caller (TCP).
Definition at line 286 of file TCPConnectionBase.cc.
Referenced by TCP::handleMessage().
{ printConnBrief(); tcpEV << msg->getName() << " timer expired\n"; // first do actions TCPEventCode event; if (msg==the2MSLTimer) { event = TCP_E_TIMEOUT_2MSL; process_TIMEOUT_2MSL(); } else if (msg==connEstabTimer) { event = TCP_E_TIMEOUT_CONN_ESTAB; process_TIMEOUT_CONN_ESTAB(); } else if (msg==finWait2Timer) { event = TCP_E_TIMEOUT_FIN_WAIT_2; process_TIMEOUT_FIN_WAIT_2(); } else if (msg==synRexmitTimer) { event = TCP_E_IGNORE; process_TIMEOUT_SYN_REXMIT(event); } else { event = TCP_E_IGNORE; tcpAlgorithm->processTimer(msg, event); } // then state transitions return performStateTransition(event); }
bool TCPConnection::processTSOption | ( | TCPSegment * | tcpseg, | |
const TCPOption & | option | |||
) | [protected, virtual] |
Definition at line 921 of file TCPConnectionUtil.cc.
Referenced by readHeaderOptions().
{ if (option.getLength() != 10) { tcpEV << "ERROR: length incorrect\n"; return false; } if ((!state->ts_enabled && fsm.getState() != TCP_S_LISTEN && fsm.getState() != TCP_S_SYN_SENT) || (state->ts_enabled && fsm.getState() != TCP_S_SYN_RCVD && fsm.getState() != TCP_S_ESTABLISHED && fsm.getState() != TCP_S_FIN_WAIT_1 && fsm.getState() != TCP_S_FIN_WAIT_2)) { tcpEV << "ERROR: TCP Header Option TS received, but in unexpected state\n"; return false; } if (option.getValuesArraySize() != 2) { // since option.getLength() was already checked, this is a programming error not a TCP error throw cRuntimeError("TCPOption for TS does not contain the data its getLength() promises"); } if (!state->ts_enabled) { state->rcv_initial_ts = true; state->ts_enabled = state->ts_support && state->snd_initial_ts && state->rcv_initial_ts; tcpEV << "TCP Header Option TS(TSval=" << option.getValues(0) << ", TSecr=" << option.getValues(1) << ") received, TS (ts_enabled) is set to: " << state->ts_enabled << "\n"; } else tcpEV << "TCP Header Option TS(TSval=" << option.getValues(0) << ", TSecr=" << option.getValues(1) << ") received\n"; // RFC 1323, page 35: // "Check whether the segment contains a Timestamps option and bit // Snd.TS.OK is on. If so: // If SEG.TSval < TS.Recent, then test whether connection has // been idle less than 24 days; if both are true, then the // segment is not acceptable; follow steps below for an // unacceptable segment. // If SEG.SEQ is equal to Last.ACK.sent, then save SEG.[TSval] in // variable TS.Recent." if (state->ts_enabled) { if (seqLess(option.getValues(0), state->ts_recent)) { if ((simTime() - state->time_last_data_sent) > PAWS_IDLE_TIME_THRESH) // PAWS_IDLE_TIME_THRESH = 24 days { tcpEV << "PAWS: Segment is not acceptable, TSval=" << option.getValues(0) << " in " << stateName(fsm.getState()) << " state received: dropping segment\n"; return false; } } else if (seqLE(tcpseg->getSequenceNo(), state->last_ack_sent)) // Note: test is modified according to the latest proposal of the tcplw@cray.com list (Braden 1993/04/26) { state->ts_recent = option.getValues(0); tcpEV << "Updating ts_recent from segment: new ts_recent=" << state->ts_recent << "\n"; } } return true; }
bool TCPConnection::processWSOption | ( | TCPSegment * | tcpseg, | |
const TCPOption & | option | |||
) | [protected, virtual] |
Definition at line 889 of file TCPConnectionUtil.cc.
Referenced by readHeaderOptions().
{ if (option.getLength() != 3) { tcpEV << "ERROR: length incorrect\n"; return false; } if (fsm.getState() != TCP_S_LISTEN && fsm.getState() != TCP_S_SYN_SENT) { tcpEV << "ERROR: TCP Header Option WS received, but in unexpected state\n"; return false; } if (option.getValuesArraySize() == 0) { // since option.getLength() was already checked, this is a programming error not a TCP error throw cRuntimeError("TCPOption for WS does not contain the data its getLength() promises"); } state->rcv_ws = true; state->ws_enabled = state->ws_support && state->snd_ws && state->rcv_ws; state->snd_wnd_scale = option.getValues(0); tcpEV << "TCP Header Option WS(=" << state->snd_wnd_scale << ") received, WS (ws_enabled) is set to: " << state->ws_enabled << "\n"; if (state->snd_wnd_scale > 14) // RFC 1323, page 11: "the shift count must be limited to 14" { tcpEV << "ERROR: TCP Header Option WS received but shift count value is exceeding 14\n"; state->snd_wnd_scale = 14; } return true; }
void TCPConnection::readHeaderOptions | ( | TCPSegment * | tcpseg | ) | [protected, virtual] |
Utility: readHeaderOptions (Currently only EOL, NOP, MSS, WS, SACK_PERMITTED, SACK and TS are implemented)
Definition at line 802 of file TCPConnectionUtil.cc.
Referenced by processSegment1stThru8th(), processSegmentInListen(), and processSegmentInSynSent().
{ tcpEV << "TCP Header Option(s) received:\n"; for (uint i=0; i<tcpseg->getOptionsArraySize(); i++) { const TCPOption& option = tcpseg->getOptions(i); short kind = option.getKind(); short length = option.getLength(); tcpEV << "Option type " << kind << " (" << optionName(kind) << "), length " << length << "\n"; bool ok = true; switch(kind) { case TCPOPTION_END_OF_OPTION_LIST: // EOL=0 case TCPOPTION_NO_OPERATION: // NOP=1 if (length != 1) { tcpEV << "ERROR: option length incorrect\n"; ok = false; } break; case TCPOPTION_MAXIMUM_SEGMENT_SIZE: // MSS=2 ok = processMSSOption(tcpseg, option); break; case TCPOPTION_WINDOW_SCALE: // WS=3 ok = processWSOption(tcpseg, option); break; case TCPOPTION_SACK_PERMITTED: // SACK_PERMITTED=4 ok = processSACKPermittedOption(tcpseg, option); break; case TCPOPTION_SACK: // SACK=5 ok = processSACKOption(tcpseg, option); break; case TCPOPTION_TIMESTAMP: // TS=8 ok = processTSOption(tcpseg, option); break; // TODO add new TCPOptions here once they are implemented // TODO delegate to TCPAlgorithm as well -- it may want to recognized additional options default: tcpEV << "ERROR: Unsupported TCP option kind " << kind << "\n"; break; } (void)ok; // unused } }
void TCPConnection::retransmitData | ( | ) | [virtual] |
Utility: retransmit all from snd_una to snd_max
Definition at line 780 of file TCPConnectionUtil.cc.
Referenced by DumbTCP::processTimer().
{ // retransmit everything from snd_una state->snd_nxt = state->snd_una; uint32 bytesToSend = state->snd_max - state->snd_nxt; ASSERT(bytesToSend!=0); // TBD - avoid to send more than allowed - check cwnd and rwnd before retransmitting data! while (bytesToSend>0) { uint32 bytes = std::min(bytesToSend, state->snd_mss); bytes = std::min(bytes, (uint32)(sendQueue->getBytesAvailable(state->snd_nxt))); sendSegment(bytes); // Do not send packets after the FIN. // fixes bug that occurs in examples/inet/bulktransfer at event #64043 T=13.861159213744 if (state->send_fin && state->snd_nxt==state->snd_fin_seq+1) break; bytesToSend -= state->sentBytes; } }
void TCPConnection::retransmitOneSegment | ( | bool | called_at_rto | ) | [virtual] |
Utility: retransmit one segment from snd_una
Definition at line 747 of file TCPConnectionUtil.cc.
Referenced by TCPTahoe::processRexmitTimer(), TCPReno::processRexmitTimer(), TCPNoCongestionControl::processRexmitTimer(), TCPNewReno::processRexmitTimer(), TCPNewReno::receivedDataAck(), TCPTahoe::receivedDuplicateAck(), TCPReno::receivedDuplicateAck(), and TCPNewReno::receivedDuplicateAck().
{ uint32 old_snd_nxt = state->snd_nxt; // retransmit one segment at snd_una, and set snd_nxt accordingly (if not called at RTO) state->snd_nxt = state->snd_una; // When FIN sent the snd_max-snd_nxt larger than bytes available in queue ulong bytes = std::min((ulong)std::min(state->snd_mss, state->snd_max - state->snd_nxt), sendQueue->getBytesAvailable(state->snd_nxt)); ASSERT(bytes!=0); sendSegment(bytes); if (!called_at_rto) { if (seqGreater(old_snd_nxt, state->snd_nxt)) state->snd_nxt = old_snd_nxt; } // notify tcpAlgorithm->ackSent(); if (state->sack_enabled) { // RFC 3517, page 7: "(3) Retransmit the first data segment presumed dropped -- the segment // starting with sequence number HighACK + 1. To prevent repeated // retransmission of the same data, set HighRxt to the highest // sequence number in the retransmitted segment." state->highRxt = rexmitQueue->getHighestRexmittedSeqNum(); } }
void TCPConnection::scheduleTimeout | ( | cMessage * | msg, | |
simtime_t | timeout | |||
) | [inline] |
Utility: start a timer
Definition at line 510 of file TCPConnection.h.
Referenced by DumbTCP::dataSent(), process_OPEN_ACTIVE(), process_SEND(), process_TIMEOUT_SYN_REXMIT(), TCPBaseAlg::processPersistTimer(), TCPBaseAlg::processRexmitTimer(), processSegment1stThru8th(), processSegmentInListen(), DumbTCP::processTimer(), TCPBaseAlg::receivedDataAck(), TCPBaseAlg::receiveSeqChanged(), TCPBaseAlg::startRexmitTimer(), and startSynRexmitTimer().
{tcpMain->scheduleAt(simTime()+timeout, msg);}
void TCPConnection::segmentArrivalWhileClosed | ( | TCPSegment * | tcpseg, | |
IPvXAddress | src, | |||
IPvXAddress | dest | |||
) | [virtual] |
This method gets invoked from TCP when a segment arrives which doesn't belong to an existing connection. TCP creates a temporary connection object so that it can call this method, then immediately deletes it.
Definition at line 37 of file TCPConnectionRcvSegment.cc.
Referenced by TCP::segmentArrivalWhileClosed().
{ tcpEV << "Seg arrived: "; printSegmentBrief(tcpseg); // This segment doesn't belong to any connection, so this object // must be a temp object created solely for the purpose of calling us ASSERT(state==NULL); tcpEV << "Segment doesn't belong to any existing connection\n"; // RFC 793: //" // all data in the incoming segment is discarded. An incoming // segment containing a RST is discarded. An incoming segment not // containing a RST causes a RST to be sent in response. The // acknowledgment and sequence field values are selected to make the // reset sequence acceptable to the TCP that sent the offending // segment. // // If the ACK bit is off, sequence number zero is used, // // <SEQ=0><ACK=SEG.SEQ+SEG.LEN><CTL=RST,ACK> // // If the ACK bit is on, // // <SEQ=SEG.ACK><CTL=RST> //" if (tcpseg->getRstBit()) { tcpEV << "RST bit set: dropping segment\n"; return; } if (!tcpseg->getAckBit()) { tcpEV << "ACK bit not set: sending RST+ACK\n"; uint32 ackNo = tcpseg->getSequenceNo() + (uint32)tcpseg->getPayloadLength(); sendRstAck(0,ackNo,destAddr,srcAddr,tcpseg->getDestPort(),tcpseg->getSrcPort()); } else { tcpEV << "ACK bit set: sending RST\n"; sendRst(tcpseg->getAckNo(),destAddr,srcAddr,tcpseg->getDestPort(),tcpseg->getSrcPort()); } }
void TCPConnection::selectInitialSeqNum | ( | ) | [protected, virtual] |
Utility: generates ISS and initializes corresponding state variables
Definition at line 382 of file TCPConnectionUtil.cc.
Referenced by process_OPEN_ACTIVE(), process_SEND(), and processSegmentInListen().
{ // set the initial send sequence number state->iss = (unsigned long)fmod(SIMTIME_DBL(simTime())*250000.0, 1.0+(double)(unsigned)0xffffffffUL) & 0xffffffffUL; state->snd_una = state->snd_nxt = state->snd_max = state->iss; sendQueue->init(state->iss+1); // +1 is for SYN rexmitQueue->init(state->iss + 1); // +1 is for SYN }
void TCPConnection::sendAck | ( | ) | [virtual] |
Utility: send ACK
Definition at line 509 of file TCPConnectionUtil.cc.
Referenced by TCPBaseAlg::established(), DumbTCP::established(), TCPBaseAlg::processDelayedAckTimer(), processSegment1stThru8th(), TCPBaseAlg::receivedAckForDataNotYetSent(), DumbTCP::receivedAckForDataNotYetSent(), TCPBaseAlg::receivedOutOfOrderSegment(), DumbTCP::receivedOutOfOrderSegment(), TCPBaseAlg::receiveSeqChanged(), and DumbTCP::receiveSeqChanged().
{ TCPSegment *tcpseg = createTCPSegment("ACK"); tcpseg->setAckBit(true); tcpseg->setSequenceNo(state->snd_nxt); tcpseg->setAckNo(state->rcv_nxt); tcpseg->setWindow(updateRcvWnd()); // write header options writeHeaderOptions(tcpseg); // send it sendToIP(tcpseg); // notify tcpAlgorithm->ackSent(); }
bool TCPConnection::sendData | ( | bool | fullSegmentsOnly, | |
uint32 | congestionWindow | |||
) | [virtual] |
Utility: Send data from sendQueue, at most congestionWindow. If fullSegmentsOnly is set, don't send segments smaller than SMSS (needed for Nagle). Returns true if some data was actually sent.
Definition at line 610 of file TCPConnectionUtil.cc.
Referenced by DumbTCP::established(), DumbTCP::receivedDataAck(), DumbTCP::sendCommandInvoked(), and TCPBaseAlg::sendData().
{ if (!state->afterRto) { // we'll start sending from snd_max state->snd_nxt = state->snd_max; } uint32 old_highRxt = 0; if (state->sack_enabled) old_highRxt = rexmitQueue->getHighestRexmittedSeqNum(); // check how many bytes we have ulong buffered = sendQueue->getBytesAvailable(state->snd_nxt); if (buffered==0) return false; // maxWindow is minimum of snd_wnd and congestionWindow (snd_cwnd) ulong maxWindow = std::min(state->snd_wnd, congestionWindow); // effectiveWindow: number of bytes we're allowed to send now long effectiveWin = maxWindow - (state->snd_nxt - state->snd_una); if (effectiveWin <= 0) { tcpEV << "Effective window is zero (advertised window " << state->snd_wnd << ", congestion window " << congestionWindow << "), cannot send.\n"; return false; } ulong bytesToSend = effectiveWin; if (bytesToSend > buffered) bytesToSend = buffered; uint32 effectiveMaxBytesSend = state->snd_mss; if (state->ts_enabled) effectiveMaxBytesSend -= TCP_OPTION_TS_SIZE; // last segment could be less than state->snd_mss (or less than snd_mss-TCP_OPTION_TS_SIZE is using TS option) if (fullSegmentsOnly && buffered > (ulong)effectiveWin && (bytesToSend < (effectiveMaxBytesSend))) { tcpEV << "Cannot send, not enough data for a full segment (SMSS=" << state->snd_mss << ", in buffer " << buffered << ")\n"; return false; } // start sending 'bytesToSend' bytes tcpEV << "Will send " << bytesToSend << " bytes (effectiveWindow " << effectiveWin << ", in buffer " << buffered << " bytes)\n"; uint32 old_snd_nxt = state->snd_nxt; ASSERT(bytesToSend>0); #ifdef TCP_SENDFRAGMENTS /* normally undefined */ // make agressive use of the window until the last byte while (bytesToSend>0) { ulong bytes = std::min(bytesToSend, state->snd_mss); sendSegment(bytes); bytesToSend -= state->sentBytes; } #else // send <MSS segments only if it's the only segment we can send now - Note: If bytesToSend=1010, MSS=1012, ts_enabled=true => we may send 2 segments (1000 payload + optionsHeader and 10 payload + optionsHeader) // FIXME this should probably obey Nagle's alg -- to be checked if (bytesToSend <= state->snd_mss) { sendSegment(bytesToSend); bytesToSend -= state->sentBytes; } else // send whole segments only (nagle_enabled) { while (bytesToSend >= effectiveMaxBytesSend) { sendSegment(state->snd_mss); bytesToSend -= state->sentBytes; } } // check how many bytes we have - last segment could be less than state->snd_mss buffered = sendQueue->getBytesAvailable(state->snd_nxt); if (bytesToSend==buffered && buffered!=0) // last segment? sendSegment(bytesToSend); else if (bytesToSend>0) tcpEV << bytesToSend << " bytes of space left in effectiveWindow\n"; #endif // remember highest seq sent (snd_nxt may be set back on retransmission, // but we'll need snd_max to check validity of ACKs -- they must ack // something we really sent) if (seqGreater(state->snd_nxt, state->snd_max)) state->snd_max = state->snd_nxt; if (unackedVector) unackedVector->record(state->snd_max - state->snd_una); // notify (once is enough) tcpAlgorithm->ackSent(); if (state->sack_enabled && state->lossRecovery && old_highRxt != state->highRxt) { // Note: Restart of REXMIT timer on retransmission is not part of RFC 2581, however optional in RFC 3517 if sent during recovery. tcpEV << "Retransmission sent during recovery, restarting REXMIT timer.\n"; tcpAlgorithm->restartRexmitTimer(); } else // don't measure RTT for retransmitted packets tcpAlgorithm->dataSent(old_snd_nxt); return true; }
void TCPConnection::sendDataDuringLossRecoveryPhase | ( | uint32 | congestionWindow | ) | [virtual] |
Utility: send data during Loss Recovery phase (if SACK is enabled).
Definition at line 1875 of file TCPConnectionUtil.cc.
Referenced by TCPReno::receivedDataAck(), and TCPReno::receivedDuplicateAck().
{ ASSERT (state->sack_enabled && state->lossRecovery); // RFC 3517 pages 7 and 8: "(5) In order to take advantage of potential additional available // cwnd, proceed to step (C) below. // (...) // (C) If cwnd - pipe >= 1 SMSS the sender SHOULD transmit one or more // segments as follows: // (...) // (C.5) If cwnd - pipe >= 1 SMSS, return to (C.1)" while (((int)congestionWindow - (int)state->pipe) >= (int)state->snd_mss) // Note: Typecast needed to avoid prohibited transmissions { // RFC 3517 pages 7 and 8: "(C.1) The scoreboard MUST be queried via NextSeg () for the // sequence number range of the next segment to transmit (if any), // and the given segment sent. If NextSeg () returns failure (no // data to send) return without sending anything (i.e., terminate // steps C.1 -- C.5)." uint32 seqNum = nextSeg(); // if nextSeg() returns 0 (=failure): terminate steps C.1 -- C.5 if (seqNum != 0) { sendSegmentDuringLossRecoveryPhase(seqNum); // RFC 3517 page 8: "(C.4) The estimate of the amount of data outstanding in the // network must be updated by incrementing pipe by the number of // octets transmitted in (C.1)." state->pipe += state->sentBytes; } else // nextSeg () returns failure: terminate steps C.1 -- C.5 break; } }
void TCPConnection::sendEstabIndicationToApp | ( | ) | [protected, virtual] |
Utility: sends TCP_I_ESTABLISHED indication with TCPConnectInfo to application
Definition at line 294 of file TCPConnectionUtil.cc.
Referenced by processSegment1stThru8th(), and processSegmentInSynSent().
{ tcpEV << "Notifying app: " << indicationName(TCP_I_ESTABLISHED) << "\n"; cMessage *msg = new cMessage(indicationName(TCP_I_ESTABLISHED)); msg->setKind(TCP_I_ESTABLISHED); TCPConnectInfo *ind = new TCPConnectInfo(); ind->setConnId(connId); ind->setLocalAddr(localAddr); ind->setRemoteAddr(remoteAddr); ind->setLocalPort(localPort); ind->setRemotePort(remotePort); msg->setControlInfo(ind); tcpMain->send(msg, "appOut", appGateIndex); }
void TCPConnection::sendFin | ( | ) | [virtual] |
Utility: sends FIN
Definition at line 528 of file TCPConnectionUtil.cc.
Referenced by process_CLOSE().
{ TCPSegment *tcpseg = createTCPSegment("FIN"); // Note: ACK bit *must* be set for both FIN and FIN+ACK. What makes // the difference for FIN+ACK is that its ackNo acks the remote TCP's FIN. tcpseg->setFinBit(true); tcpseg->setAckBit(true); tcpseg->setAckNo(state->rcv_nxt); tcpseg->setSequenceNo(state->snd_nxt); tcpseg->setWindow(updateRcvWnd()); // send it sendToIP(tcpseg); // notify tcpAlgorithm->ackSent(); }
void TCPConnection::sendIndicationToApp | ( | int | code | ) | [protected, virtual] |
Utility: sends status indication (TCP_I_xxx) to application
Definition at line 283 of file TCPConnectionUtil.cc.
Referenced by process_TIMEOUT_CONN_ESTAB(), process_TIMEOUT_FIN_WAIT_2(), processRstInSynReceived(), processSegment1stThru8th(), processSegmentInSynSent(), and signalConnectionTimeout().
{ tcpEV << "Notifying app: " << indicationName(code) << "\n"; cMessage *msg = new cMessage(indicationName(code)); msg->setKind(code); TCPCommand *ind = new TCPCommand(); ind->setConnId(connId); msg->setControlInfo(ind); tcpMain->send(msg, "appOut", appGateIndex); }
void TCPConnection::sendOneNewSegment | ( | bool | fullSegmentsOnly, | |
uint32 | congestionWindow | |||
) | [virtual] |
Utility: send one new segment from snd_max if allowed (RFC 3042).
Definition at line 1970 of file TCPConnectionUtil.cc.
Referenced by TCPBaseAlg::receivedDuplicateAck().
{ ASSERT (state->limited_transmit_enabled); // RFC 3042, page 3: // "When a TCP sender has previously unsent data queued for transmission // it SHOULD use the Limited Transmit algorithm, which calls for a TCP // sender to transmit new data upon the arrival of the first two // consecutive duplicate ACKs when the following conditions are // satisfied: // // * The receiver's advertised window allows the transmission of the // segment. // // * The amount of outstanding data would remain less than or equal // to the congestion window plus 2 segments. In other words, the // sender can only send two segments beyond the congestion window // (cwnd). // // The congestion window (cwnd) MUST NOT be changed when these new // segments are transmitted. Assuming that these new segments and the // corresponding ACKs are not dropped, this procedure allows the sender // to infer loss using the standard Fast Retransmit threshold of three // duplicate ACKs [RFC2581]. This is more robust to reordered packets // than if an old packet were retransmitted on the first or second // duplicate ACK. // // Note: If the connection is using selective acknowledgments [RFC2018], // the data sender MUST NOT send new segments in response to duplicate // ACKs that contain no new SACK information, as a misbehaving receiver // can generate such ACKs to trigger inappropriate transmission of data // segments. See [SCWA99] for a discussion of attacks by misbehaving // receivers." if (!state->sack_enabled || (state->sack_enabled && state->sackedBytes_old!=state->sackedBytes)) { // check how many bytes we have ulong buffered = sendQueue->getBytesAvailable(state->snd_max); if (buffered >= state->snd_mss || (!fullSegmentsOnly && buffered > 0)) { ulong outstandingData = state->snd_max - state->snd_una; // check conditions from RFC 3042 if (outstandingData + state->snd_mss <= state->snd_wnd && outstandingData + state->snd_mss <= congestionWindow + 2*state->snd_mss) { uint32 effectiveWin = std::min (state->snd_wnd, congestionWindow) - outstandingData + 2*state->snd_mss; // RFC 3042, page 3: "(...)the sender can only send two segments beyond the congestion window (cwnd)." // bytes: number of bytes we're allowed to send now uint32 bytes = std::min(effectiveWin, state->snd_mss); if (bytes >= state->snd_mss || (!fullSegmentsOnly && bytes > 0)) { uint32 old_snd_nxt = state->snd_nxt; // we'll start sending from snd_max state->snd_nxt = state->snd_max; tcpEV << "Limited Transmit algorithm enabled. Sending one new segment.\n"; sendSegment(bytes); if (seqGreater(state->snd_nxt, state->snd_max)) state->snd_max = state->snd_nxt; if (unackedVector) unackedVector->record(state->snd_max - state->snd_una); // reset snd_nxt if needed if (state->afterRto) state->snd_nxt = old_snd_nxt + state->sentBytes; // notify tcpAlgorithm->ackSent(); tcpAlgorithm->dataSent(old_snd_nxt); } } } } }
bool TCPConnection::sendProbe | ( | ) | [virtual] |
Utility: sends 1 bytes as "probe", called by the "persist" mechanism
Definition at line 717 of file TCPConnectionUtil.cc.
Referenced by TCPBaseAlg::processPersistTimer().
{ // we'll start sending from snd_max state->snd_nxt = state->snd_max; // check we have 1 byte to send if (sendQueue->getBytesAvailable(state->snd_nxt)==0) { tcpEV << "Cannot send probe because send buffer is empty\n"; return false; } uint32 old_snd_nxt = state->snd_nxt; tcpEV << "Sending 1 byte as probe, with seq=" << state->snd_nxt << "\n"; sendSegment(1); // remember highest seq sent (snd_nxt may be set back on retransmission, // but we'll need snd_max to check validity of ACKs -- they must ack // something we really sent) state->snd_max = state->snd_nxt; if (unackedVector) unackedVector->record(state->snd_max - state->snd_una); // notify tcpAlgorithm->ackSent(); tcpAlgorithm->dataSent(old_snd_nxt); return true; }
void TCPConnection::sendRst | ( | uint32 | seq, | |
IPvXAddress | src, | |||
IPvXAddress | dest, | |||
int | srcPort, | |||
int | destPort | |||
) | [virtual] |
Utility: sends RST; does not use connection state
Definition at line 476 of file TCPConnectionUtil.cc.
{ TCPSegment *tcpseg = createTCPSegment("RST"); tcpseg->setSrcPort(srcPort); tcpseg->setDestPort(destPort); tcpseg->setRstBit(true); tcpseg->setSequenceNo(seq); // send it sendToIP(tcpseg, src, dest); }
void TCPConnection::sendRst | ( | uint32 | seqNo | ) | [virtual] |
Utility: sends RST
Definition at line 471 of file TCPConnectionUtil.cc.
Referenced by process_ABORT(), processSegment1stThru8th(), processSegmentInListen(), processSegmentInSynSent(), and segmentArrivalWhileClosed().
{ sendRst(seqNo, localAddr, remoteAddr, localPort, remotePort); }
void TCPConnection::sendRstAck | ( | uint32 | seq, | |
uint32 | ack, | |||
IPvXAddress | src, | |||
IPvXAddress | dest, | |||
int | srcPort, | |||
int | destPort | |||
) | [virtual] |
Utility: sends RST+ACK; does not use connection state
Definition at line 490 of file TCPConnectionUtil.cc.
Referenced by segmentArrivalWhileClosed().
{ TCPSegment *tcpseg = createTCPSegment("RST+ACK"); tcpseg->setSrcPort(srcPort); tcpseg->setDestPort(destPort); tcpseg->setRstBit(true); tcpseg->setAckBit(true); tcpseg->setSequenceNo(seq); tcpseg->setAckNo(ack); // send it sendToIP(tcpseg, src, dest); // notify tcpAlgorithm->ackSent(); }
void TCPConnection::sendSegment | ( | uint32 | bytes | ) | [virtual] |
Utility: sends one segment of 'bytes' bytes from snd_nxt, and advances snd_nxt. sendData(), sendProbe() and retransmitData() internally all rely on this one.
Definition at line 547 of file TCPConnectionUtil.cc.
Referenced by retransmitData(), retransmitOneSegment(), sendData(), sendOneNewSegment(), sendProbe(), and sendSegmentDuringLossRecoveryPhase().
{ if (state->sack_enabled && state->afterRto) { // check rexmitQ and try to forward snd_nxt before sending new data uint32 forward = rexmitQueue->checkRexmitQueueForSackedOrRexmittedSegments(state->snd_nxt); state->snd_nxt = state->snd_nxt + forward; } ulong buffered = sendQueue->getBytesAvailable(state->snd_nxt); if (bytes > buffered) // last segment? bytes = buffered; // if header options will be added, this could reduce the number of data bytes allowed for this segment, // because following condition must to be respected: // bytes + options_len <= snd_mss TCPSegment *tcpseg_temp = createTCPSegment(NULL); tcpseg_temp->setAckBit(true); // needed for TS option, otherwise TSecr will be set to 0 writeHeaderOptions(tcpseg_temp); uint options_len = tcpseg_temp->getHeaderLength() - TCP_HEADER_OCTETS; // TCP_HEADER_OCTETS = 20 while (bytes + options_len > state->snd_mss) bytes--; state->sentBytes = bytes; // send one segment of 'bytes' bytes from snd_nxt, and advance snd_nxt TCPSegment *tcpseg = sendQueue->createSegmentWithBytes(state->snd_nxt, bytes); // if sack_enabled copy region of tcpseg to rexmitQueue if (state->sack_enabled) rexmitQueue->enqueueSentData(state->snd_nxt, state->snd_nxt+bytes); tcpseg->setAckNo(state->rcv_nxt); tcpseg->setAckBit(true); tcpseg->setWindow(updateRcvWnd()); // TBD when to set PSH bit? // TBD set URG bit if needed ASSERT(bytes==tcpseg->getPayloadLength()); state->snd_nxt += bytes; // check if afterRto bit can be reset if (state->afterRto && seqGE(state->snd_nxt, state->snd_max)) state->afterRto = false; if (state->send_fin && state->snd_nxt==state->snd_fin_seq) { tcpEV << "Setting FIN on segment\n"; tcpseg->setFinBit(true); state->snd_nxt = state->snd_fin_seq+1; } // add header options and update header length (from tcpseg_temp) tcpseg->setOptionsArraySize(tcpseg_temp->getOptionsArraySize()); for (uint i=0; i<tcpseg_temp->getOptionsArraySize(); i++) tcpseg->setOptions(i, tcpseg_temp->getOptions(i)); tcpseg->setHeaderLength(tcpseg_temp->getHeaderLength()); delete tcpseg_temp; // send it sendToIP(tcpseg); }
void TCPConnection::sendSegmentDuringLossRecoveryPhase | ( | uint32 | seqNum | ) | [virtual] |
Utility: send segment during Loss Recovery phase (if SACK is enabled).
Definition at line 1906 of file TCPConnectionUtil.cc.
Referenced by sendDataDuringLossRecoveryPhase().
{ ASSERT (state->sack_enabled && state->lossRecovery); // start sending from seqNum state->snd_nxt = seqNum; uint32 old_highRxt = rexmitQueue->getHighestRexmittedSeqNum(); // no need to check cwnd and rwnd - has already be done before // no need to check nagle - sending mss bytes sendSegment(state->snd_mss); uint32 sentSeqNum = seqNum + state->sentBytes; // RFC 3517 page 8: "(C.2) If any of the data octets sent in (C.1) are below HighData, // HighRxt MUST be set to the highest sequence number of the // retransmitted segment." if (seqLE(sentSeqNum, state->snd_max)) // HighData = snd_max { ASSERT (sentSeqNum==rexmitQueue->getHighestRexmittedSeqNum()); state->highRxt = rexmitQueue->getHighestRexmittedSeqNum(); } // RFC 3517 page 8: "(C.3) If any of the data octets sent in (C.1) are above HighData, // HighData must be updated to reflect the transmission of // previously unsent data." else if (seqGE(sentSeqNum, state->snd_max)) // HighData = snd_max state->snd_max = sentSeqNum; if (unackedVector) unackedVector->record(state->snd_max - state->snd_una); // RFC 3517, page 9: "6 Managing the RTO Timer // // The standard TCP RTO estimator is defined in [RFC2988]. Due to the // fact that the SACK algorithm in this document can have an impact on // the behavior of the estimator, implementers may wish to consider how // the timer is managed. [RFC2988] calls for the RTO timer to be // re-armed each time an ACK arrives that advances the cumulative ACK // point. Because the algorithm presented in this document can keep the // ACK clock going through a fairly significant loss event, // (comparatively longer than the algorithm described in [RFC2581]), on // some networks the loss event could last longer than the RTO. In this // case the RTO timer would expire prematurely and a segment that need // not be retransmitted would be resent. // // Therefore we give implementers the latitude to use the standard // [RFC2988] style RTO management or, optionally, a more careful variant // that re-arms the RTO timer on each retransmission that is sent during // recovery MAY be used. This provides a more conservative timer than // specified in [RFC2988], and so may not always be an attractive // alternative. However, in some cases it may prevent needless // retransmissions, go-back-N transmission and further reduction of the // congestion window." tcpAlgorithm->ackSent(); if (old_highRxt != state->highRxt) { // Note: Restart of REXMIT timer on retransmission is not part of RFC 2581, however optional in RFC 3517 if sent during recovery. tcpEV << "Retransmission sent during recovery, restarting REXMIT timer.\n"; tcpAlgorithm->restartRexmitTimer(); } else // don't measure RTT for retransmitted packets tcpAlgorithm->dataSent(seqNum); // seqNum = old_snd_nxt }
void TCPConnection::sendSyn | ( | ) | [protected, virtual] |
Utility: send SYN
Definition at line 425 of file TCPConnectionUtil.cc.
Referenced by process_OPEN_ACTIVE(), process_SEND(), and process_TIMEOUT_SYN_REXMIT().
{ if (remoteAddr.isUnspecified() || remotePort==-1) opp_error("Error processing command OPEN_ACTIVE: foreign socket unspecified"); if (localPort==-1) opp_error("Error processing command OPEN_ACTIVE: local port unspecified"); // create segment TCPSegment *tcpseg = createTCPSegment("SYN"); tcpseg->setSequenceNo(state->iss); tcpseg->setSynBit(true); updateRcvWnd(); tcpseg->setWindow(state->rcv_wnd); state->snd_max = state->snd_nxt = state->iss+1; // write header options writeHeaderOptions(tcpseg); // send it sendToIP(tcpseg); }
void TCPConnection::sendSynAck | ( | ) | [protected, virtual] |
Utility: send SYN+ACK
Definition at line 448 of file TCPConnectionUtil.cc.
Referenced by process_TIMEOUT_SYN_REXMIT(), processSegmentInListen(), and processSegmentInSynSent().
{ // create segment TCPSegment *tcpseg = createTCPSegment("SYN+ACK"); tcpseg->setSequenceNo(state->iss); tcpseg->setAckNo(state->rcv_nxt); tcpseg->setSynBit(true); tcpseg->setAckBit(true); updateRcvWnd(); tcpseg->setWindow(state->rcv_wnd); state->snd_max = state->snd_nxt = state->iss+1; // write header options writeHeaderOptions(tcpseg); // send it sendToIP(tcpseg); // notify tcpAlgorithm->ackSent(); }
void TCPConnection::sendToApp | ( | cMessage * | msg | ) | [protected, virtual] |
Utility: sends packet to application
Definition at line 311 of file TCPConnectionUtil.cc.
Referenced by process_STATUS(), and processSegment1stThru8th().
{ tcpMain->send(msg, "appOut", appGateIndex); }
void TCPConnection::sendToIP | ( | TCPSegment * | tcpseg, | |
IPvXAddress | src, | |||
IPvXAddress | dest | |||
) | [static, protected] |
Utility: send IP packet
Definition at line 244 of file TCPConnectionUtil.cc.
{ tcpEV << "Sending: "; printSegmentBrief(tcpseg); if (!dest.isIPv6()) { // send over IPv4 IPControlInfo *controlInfo = new IPControlInfo(); controlInfo->setProtocol(IP_PROT_TCP); controlInfo->setSrcAddr(src.get4()); controlInfo->setDestAddr(dest.get4()); tcpseg->setControlInfo(controlInfo); check_and_cast<TCP *>(simulation.getContextModule())->send(tcpseg,"ipOut"); } else { // send over IPv6 IPv6ControlInfo *controlInfo = new IPv6ControlInfo(); controlInfo->setProtocol(IP_PROT_TCP); controlInfo->setSrcAddr(src.get6()); controlInfo->setDestAddr(dest.get6()); tcpseg->setControlInfo(controlInfo); check_and_cast<TCP *>(simulation.getContextModule())->send(tcpseg,"ipv6Out"); } }
void TCPConnection::sendToIP | ( | TCPSegment * | tcpseg | ) | [virtual] |
Utility: adds control info to segment and sends it to IP
Definition at line 199 of file TCPConnectionUtil.cc.
Referenced by sendAck(), sendFin(), sendRst(), sendRstAck(), sendSegment(), sendSyn(), and sendSynAck().
{ // record seq (only if we do send data) and ackno if (sndNxtVector && tcpseg->getPayloadLength()!=0) sndNxtVector->record(tcpseg->getSequenceNo()); if (sndAckVector) sndAckVector->record(tcpseg->getAckNo()); // final touches on the segment before sending tcpseg->setSrcPort(localPort); tcpseg->setDestPort(remotePort); ASSERT(tcpseg->getHeaderLength() >= TCP_HEADER_OCTETS); // TCP_HEADER_OCTETS = 20 (without options) ASSERT(tcpseg->getHeaderLength() <= TCP_MAX_HEADER_OCTETS); // TCP_MAX_HEADER_OCTETS = 60 tcpseg->setByteLength(tcpseg->getHeaderLength() + tcpseg->getPayloadLength()); state->sentBytes = tcpseg->getPayloadLength(); // resetting sentBytes to 0 if sending a segment without data (e.g. ACK) tcpEV << "Sending: "; printSegmentBrief(tcpseg); // TBD reuse next function for sending if (!remoteAddr.isIPv6()) { // send over IPv4 IPControlInfo *controlInfo = new IPControlInfo(); controlInfo->setProtocol(IP_PROT_TCP); controlInfo->setSrcAddr(localAddr.get4()); controlInfo->setDestAddr(remoteAddr.get4()); tcpseg->setControlInfo(controlInfo); tcpMain->send(tcpseg,"ipOut"); } else { // send over IPv6 IPv6ControlInfo *controlInfo = new IPv6ControlInfo(); controlInfo->setProtocol(IP_PROT_TCP); controlInfo->setSrcAddr(localAddr.get6()); controlInfo->setDestAddr(remoteAddr.get6()); tcpseg->setControlInfo(controlInfo); tcpMain->send(tcpseg,"ipv6Out"); } }
void TCPConnection::setPipe | ( | ) | [virtual] |
For SACK TCP. RFC 3517, page 3: "This routine traverses the sequence space from HighACK to HighData and MUST set the "pipe" variable to an estimate of the number of octets that are currently in transit between the TCP sender and the TCP receiver."
Definition at line 1691 of file TCPConnectionUtil.cc.
Referenced by TCPReno::receivedDataAck(), and TCPReno::receivedDuplicateAck().
{ ASSERT (state->sack_enabled); // RFC 3517, pages 1 and 2: " // "HighACK" is the sequence number of the highest byte of data that // has been cumulatively ACKed at a given point. // // "HighData" is the highest sequence number transmitted at a given // point. // // "HighRxt" is the highest sequence number which has been // retransmitted during the current loss recovery phase. // // "Pipe" is a sender's estimate of the number of bytes outstanding // in the network. This is used during recovery for limiting the // sender's sending rate. The pipe variable allows TCP to use a // fundamentally different congestion control than specified in // [RFC2581]. The algorithm is often referred to as the "pipe // algorithm"." // HighAck = snd_una // HighData = snd_max state->highRxt = rexmitQueue->getHighestRexmittedSeqNum(); state->pipe = 0; uint32 shift = state->snd_mss; if (state->ts_enabled) shift -= TCP_OPTION_TS_SIZE; // RFC 3517, page 3: "This routine traverses the sequence space from HighACK to HighData // and MUST set the "pipe" variable to an estimate of the number of // octets that are currently in transit between the TCP sender and // the TCP receiver. After initializing pipe to zero the following // steps are taken for each octet 'S1' in the sequence space between // HighACK and HighData that has not been SACKed:" for (uint32 s1=state->snd_una; s1<state->snd_max; s1=s1+shift) { if (rexmitQueue->getSackedBit(s1)==false) { // RFC 3517, page 3: "(a) If IsLost (S1) returns false: // // Pipe is incremented by 1 octet. // // The effect of this condition is that pipe is incremented for // packets that have not been SACKed and have not been determined // to have been lost (i.e., those segments that are still assumed // to be in the network)." if (isLost(s1)==false) state->pipe++; // RFC 3517, pages 3 and 4: "(b) If S1 <= HighRxt: // // Pipe is incremented by 1 octet. // // The effect of this condition is that pipe is incremented for // the retransmission of the octet. // // Note that octets retransmitted without being considered lost are // counted twice by the above mechanism." if (seqLE(s1,state->highRxt)) state->pipe++; } } state->pipe = state->pipe * shift; if (pipeVector) pipeVector->record(state->pipe); }
void TCPConnection::signalConnectionTimeout | ( | ) | [virtual] |
Utility: signal to user that connection timed out
Definition at line 278 of file TCPConnectionUtil.cc.
Referenced by TCPBaseAlg::processRexmitTimer().
{ sendIndicationToApp(TCP_I_TIMED_OUT); }
void TCPConnection::startSynRexmitTimer | ( | ) | [virtual] |
Utility: start SYN-REXMIT timer
Definition at line 1301 of file TCPConnectionRcvSegment.cc.
Referenced by process_OPEN_ACTIVE(), process_SEND(), processSegmentInListen(), and processSegmentInSynSent().
{ state->syn_rexmit_count = 0; state->syn_rexmit_timeout = TCP_TIMEOUT_SYN_REXMIT; if (synRexmitTimer->isScheduled()) cancelEvent(synRexmitTimer); scheduleTimeout(synRexmitTimer, state->syn_rexmit_timeout); }
void TCPConnection::stateEntered | ( | int | state | ) | [protected, virtual] |
Perform cleanup necessary when entering a new state, e.g. cancelling timers
Definition at line 551 of file TCPConnectionBase.cc.
Referenced by performStateTransition().
{ // cancel timers switch (state) { case TCP_S_INIT: // we'll never get back to INIT break; case TCP_S_LISTEN: // we may get back to LISTEN from SYN_RCVD ASSERT(connEstabTimer && synRexmitTimer); cancelEvent(connEstabTimer); cancelEvent(synRexmitTimer); break; case TCP_S_SYN_RCVD: case TCP_S_SYN_SENT: break; case TCP_S_ESTABLISHED: // we're in ESTABLISHED, these timers are no longer needed delete cancelEvent(connEstabTimer); delete cancelEvent(synRexmitTimer); connEstabTimer = synRexmitTimer = NULL; // TCP_I_ESTAB notification moved inside event processing break; case TCP_S_CLOSE_WAIT: case TCP_S_LAST_ACK: case TCP_S_FIN_WAIT_1: case TCP_S_FIN_WAIT_2: case TCP_S_CLOSING: case TCP_S_TIME_WAIT: // whether connection setup succeeded (ESTABLISHED) or not (others), // cancel these timers if (connEstabTimer) cancelEvent(connEstabTimer); if (synRexmitTimer) cancelEvent(synRexmitTimer); break; case TCP_S_CLOSED: // all timers need to be cancelled if (the2MSLTimer) cancelEvent(the2MSLTimer); if (connEstabTimer) cancelEvent(connEstabTimer); if (finWait2Timer) cancelEvent(finWait2Timer); if (synRexmitTimer) cancelEvent(synRexmitTimer); tcpAlgorithm->connectionClosed(); break; } }
const char * TCPConnection::stateName | ( | int | state | ) | [static] |
Utility: returns name of TCP_S_xxx constants
Definition at line 37 of file TCPConnectionUtil.cc.
Referenced by performStateTransition(), printConnBrief(), process_STATUS(), process_TIMEOUT_2MSL(), process_TIMEOUT_CONN_ESTAB(), process_TIMEOUT_FIN_WAIT_2(), process_TIMEOUT_SYN_REXMIT(), processSegment1stThru8th(), and processTSOption().
{ #define CASE(x) case x: s=#x+6; break const char *s = "unknown"; switch (state) { CASE(TCP_S_INIT); CASE(TCP_S_CLOSED); CASE(TCP_S_LISTEN); CASE(TCP_S_SYN_SENT); CASE(TCP_S_SYN_RCVD); CASE(TCP_S_ESTABLISHED); CASE(TCP_S_CLOSE_WAIT); CASE(TCP_S_LAST_ACK); CASE(TCP_S_FIN_WAIT_1); CASE(TCP_S_FIN_WAIT_2); CASE(TCP_S_CLOSING); CASE(TCP_S_TIME_WAIT); } return s; #undef CASE }
bool TCPConnection::tryFastRoute | ( | TCPSegment * | tcpseg | ) | [protected, virtual] |
Shortcut to process most common case as fast as possible. Returns false if segment requires normal (slow) route.
Definition at line 30 of file TCPConnectionRcvSegment.cc.
Referenced by processTCPSegment().
{ // fast route processing not yet implemented return false; }
void TCPConnection::updateRcvQueueVars | ( | ) | [virtual] |
Utility: update receiver queue related variables and statistics - called before setting rcv_wnd
Definition at line 1578 of file TCPConnectionUtil.cc.
Referenced by processSegment1stThru8th(), processSegmentInListen(), processSegmentInSynSent(), and updateRcvWnd().
{ // update receive queue related state variables state->freeRcvBuffer = receiveQueue->getAmountOfFreeBytes(state->maxRcvBuffer); state->usedRcvBuffer = state->maxRcvBuffer - state->freeRcvBuffer; // update receive queue related statistics if (tcpRcvQueueBytesVector) tcpRcvQueueBytesVector->record(state->usedRcvBuffer); // tcpEV << "receiveQ: receiveQLength=" << receiveQueue->getQueueLength() << " maxRcvBuffer=" << state->maxRcvBuffer << " usedRcvBuffer=" << state->usedRcvBuffer << " freeRcvBuffer=" << state->freeRcvBuffer << "\n"; }
unsigned short TCPConnection::updateRcvWnd | ( | ) | [virtual] |
Utility: update receive window (rcv_wnd), and calculate scaled value if window scaling enabled. Returns the (scaled) receive window size.
Definition at line 1591 of file TCPConnectionUtil.cc.
Referenced by sendAck(), sendFin(), sendSegment(), sendSyn(), and sendSynAck().
{ uint32 win = 0; // update receive queue related state variables and statistics updateRcvQueueVars(); win = state->freeRcvBuffer; // Following lines are based on [Stevens, W.R.: TCP/IP Illustrated, Volume 2, pages 878-879]: // Don't advertise less than one full-sized segment to avoid SWS if (win < (state->maxRcvBuffer / 4) && win < state->snd_mss) win = 0; // Do not shrink window // (rcv_adv minus rcv_nxt) is the amount of space still available to the sender that was previously advertised if (win < state->rcv_adv - state->rcv_nxt) win = state->rcv_adv - state->rcv_nxt; // Observe upper limit for advertised window on this connection if (win > TCP_MAX_WIN && !state->ws_enabled) // TCP_MAX_WIN = 65535 (16 bit) win = TCP_MAX_WIN; // Note: The window size is limited to a 16 bit value in the TCP header if WINDOW SCALE option (RFC 1323) is not used // Note: The order of the "Do not shrink window" and "Observe upper limit" parts has been changed to the order used in FreeBSD Release 7.1 // update rcv_adv if needed if (win > 0 && seqGE(state->rcv_nxt + win, state->rcv_adv)) { state->rcv_adv = state->rcv_nxt + win; if (rcvAdvVector) rcvAdvVector->record(state->rcv_adv); } state->rcv_wnd = win; if (rcvWndVector) rcvWndVector->record(state->rcv_wnd); // scale rcv_wnd: uint32 scaled_rcv_wnd = state->rcv_wnd; state->rcv_wnd_scale = 0; if (state->ws_enabled) { while (scaled_rcv_wnd > TCP_MAX_WIN && state->rcv_wnd_scale < 14) // RFC 1323, page 11: "the shift count must be limited to 14" { scaled_rcv_wnd = scaled_rcv_wnd >> 1; state->rcv_wnd_scale++; } } ASSERT(scaled_rcv_wnd == (unsigned short)scaled_rcv_wnd); return (unsigned short) scaled_rcv_wnd; }
void TCPConnection::updateWndInfo | ( | TCPSegment * | tcpseg, | |
bool | doAlways = false | |||
) | [virtual] |
Utility: update window information (snd_wnd, snd_wl1, snd_wl2)
Definition at line 1642 of file TCPConnectionUtil.cc.
Referenced by processAckInEstabEtc(), processSegmentInListen(), and processSegmentInSynSent().
{ uint32 true_window = tcpseg->getWindow(); // RFC 1323, page 10: // "The window field (SEG.WND) in the header of every incoming // segment, with the exception of SYN segments, is left-shifted // by Snd.Wind.Scale bits before updating SND.WND: // SND.WND = SEG.WND << Snd.Wind.Scale" if (state->ws_enabled && !tcpseg->getSynBit()) true_window = tcpseg->getWindow() << state->snd_wnd_scale; // Following lines are based on [Stevens, W.R.: TCP/IP Illustrated, Volume 2, page 982]: if (doAlways || (tcpseg->getAckBit() && (seqLess(state->snd_wl1, tcpseg->getSequenceNo()) || (state->snd_wl1 == tcpseg->getSequenceNo() && seqLE(state->snd_wl2, tcpseg->getAckNo())) || (state->snd_wl2 == tcpseg->getAckNo() && true_window > state->snd_wnd)))) { // send window should be updated state->snd_wnd = true_window; tcpEV << "Updating send window from segment: new wnd=" << state->snd_wnd << "\n"; state->snd_wl1 = tcpseg->getSequenceNo(); state->snd_wl2 = tcpseg->getAckNo(); if (sndWndVector) sndWndVector->record(state->snd_wnd); } }
TCPSegment TCPConnection::writeHeaderOptions | ( | TCPSegment * | tcpseg | ) | [protected, virtual] |
Utility: writeHeaderOptions (Currently only EOL, NOP, MSS, WS, SACK_PERMITTED, SACK and TS are implemented)
Definition at line 1085 of file TCPConnectionUtil.cc.
Referenced by sendAck(), sendSegment(), sendSyn(), and sendSynAck().
{ TCPOption option; uint t = 0; if (tcpseg->getSynBit() && (fsm.getState() == TCP_S_INIT || fsm.getState() == TCP_S_LISTEN || ((fsm.getState()==TCP_S_SYN_SENT || fsm.getState()==TCP_S_SYN_RCVD) && state->syn_rexmit_count>0))) // SYN flag set and connetion in INIT or LISTEN state (or after synRexmit timeout) { // MSS header option if (state->snd_mss > 0) { option.setKind(TCPOPTION_MAXIMUM_SEGMENT_SIZE); // MSS option.setLength(4); option.setValuesArraySize(1); // Update MSS option.setValues(0,state->snd_mss); tcpEV << "TCP Header Option MSS(=" << state->snd_mss << ") sent\n"; tcpseg->setOptionsArraySize(tcpseg->getOptionsArraySize()+1); tcpseg->setOptions(t,option); t++; } // WS header option if (state->ws_support && (state->rcv_ws || (fsm.getState() == TCP_S_INIT || (fsm.getState()==TCP_S_SYN_SENT && state->syn_rexmit_count>0)))) // Is WS supported by host? { // 1 padding byte option.setKind(TCPOPTION_NO_OPERATION); // NOP option.setLength(1); option.setValuesArraySize(0); tcpseg->setOptionsArraySize(tcpseg->getOptionsArraySize()+1); tcpseg->setOptions(t,option); t++; option.setKind(TCPOPTION_WINDOW_SCALE); option.setLength(3); option.setValuesArraySize(1); // Update WS variables ulong scaled_rcv_wnd = receiveQueue->getAmountOfFreeBytes(state->maxRcvBuffer); state->rcv_wnd_scale = 0; while (scaled_rcv_wnd > TCP_MAX_WIN && state->rcv_wnd_scale < 14) // RFC 1323, page 11: "the shift count must be limited to 14" { scaled_rcv_wnd = scaled_rcv_wnd >> 1; state->rcv_wnd_scale++; } option.setValues(0,state->rcv_wnd_scale); // rcv_wnd_scale is also set in scaleRcvWnd() state->snd_ws = true; state->ws_enabled = state->ws_support && state->snd_ws && state->rcv_ws; tcpEV << "TCP Header Option WS(=" << option.getValues(0) << ") sent, WS (ws_enabled) is set to: " << state->ws_enabled << "\n"; tcpseg->setOptionsArraySize(tcpseg->getOptionsArraySize()+1); tcpseg->setOptions(t,option); t++; } // SACK_PERMITTED header option if (state->sack_support && (state->rcv_sack_perm || (fsm.getState() == TCP_S_INIT || (fsm.getState()==TCP_S_SYN_SENT && state->syn_rexmit_count>0)))) // Is SACK supported by host? { if (!state->ts_support) // if TS is supported by host, do not add NOPs to this segment { // 2 padding bytes option.setKind(TCPOPTION_NO_OPERATION); // NOP option.setLength(1); option.setValuesArraySize(0); tcpseg->setOptionsArraySize(tcpseg->getOptionsArraySize()+2); tcpseg->setOptions(t,option); t++; tcpseg->setOptions(t,option); t++; } option.setKind(TCPOPTION_SACK_PERMITTED); option.setLength(2); option.setValuesArraySize(0); // Update SACK variables state->snd_sack_perm = true; state->sack_enabled = state->sack_support && state->snd_sack_perm && state->rcv_sack_perm; tcpEV << "TCP Header Option SACK_PERMITTED sent, SACK (sack_enabled) is set to: " << state->sack_enabled << "\n"; tcpseg->setOptionsArraySize(tcpseg->getOptionsArraySize()+1); tcpseg->setOptions(t,option); t++; } // TS header option if (state->ts_support && (state->rcv_initial_ts || (fsm.getState() == TCP_S_INIT || (fsm.getState()==TCP_S_SYN_SENT && state->syn_rexmit_count>0)))) // Is TS supported by host? { if (!state->sack_support) // if SACK is supported by host, do not add NOPs to this segment { // 2 padding bytes option.setKind(TCPOPTION_NO_OPERATION); // NOP option.setLength(1); option.setValuesArraySize(0); tcpseg->setOptionsArraySize(tcpseg->getOptionsArraySize()+2); tcpseg->setOptions(t,option); t++; tcpseg->setOptions(t,option); t++; } option.setKind(TCPOPTION_TIMESTAMP); option.setLength(10); option.setValuesArraySize(2); // Update TS variables // RFC 1323, page 13: "The Timestamp Value field (TSval) contains the current value of the timestamp clock of the TCP sending the option." option.setValues(0,convertSimtimeToTS(simTime())); // RFC 1323, page 16: "(3) When a TSopt is sent, its TSecr field is set to the current TS.Recent value." // RFC 1323, page 13: // "The Timestamp Echo Reply field (TSecr) is only valid if the ACK // bit is set in the TCP header; if it is valid, it echos a times- // tamp value that was sent by the remote TCP in the TSval field // of a Timestamps option. When TSecr is not valid, its value // must be zero." if (tcpseg->getAckBit()) option.setValues(1,state->ts_recent); else option.setValues(1,0); state->snd_initial_ts = true; state->ts_enabled = state->ts_support && state->snd_initial_ts && state->rcv_initial_ts; tcpEV << "TCP Header Option TS(TSval=" << option.getValues(0) << ", TSecr=" << option.getValues(1) << ") sent, TS (ts_enabled) is set to: " << state->ts_enabled << "\n"; tcpseg->setOptionsArraySize(tcpseg->getOptionsArraySize()+1); tcpseg->setOptions(t,option); t++; } // TODO add new TCPOptions here once they are implemented } else if (fsm.getState()==TCP_S_SYN_SENT || fsm.getState()==TCP_S_SYN_RCVD || fsm.getState()==TCP_S_ESTABLISHED || fsm.getState()==TCP_S_FIN_WAIT_1 || fsm.getState()==TCP_S_FIN_WAIT_2) // connetion is not in INIT or LISTEN state { // TS header option if (state->ts_enabled) // Is TS enabled? { if (!(state->sack_enabled && (state->snd_sack || state->snd_dsack))) // if SACK is enabled and SACKs need to be added, do not add NOPs to this segment { // 2 padding bytes option.setKind(TCPOPTION_NO_OPERATION); // NOP option.setLength(1); option.setValuesArraySize(0); tcpseg->setOptionsArraySize(tcpseg->getOptionsArraySize()+2); tcpseg->setOptions(t,option); t++; tcpseg->setOptions(t,option); t++; } option.setKind(TCPOPTION_TIMESTAMP); option.setLength(10); option.setValuesArraySize(2); // Update TS variables // RFC 1323, page 13: "The Timestamp Value field (TSval) contains the current value of the timestamp clock of the TCP sending the option." option.setValues(0,convertSimtimeToTS(simTime())); // RFC 1323, page 16: "(3) When a TSopt is sent, its TSecr field is set to the current TS.Recent value." // RFC 1323, page 13: // "The Timestamp Echo Reply field (TSecr) is only valid if the ACK // bit is set in the TCP header; if it is valid, it echos a times- // tamp value that was sent by the remote TCP in the TSval field // of a Timestamps option. When TSecr is not valid, its value // must be zero." if (tcpseg->getAckBit()) option.setValues(1,state->ts_recent); else option.setValues(1,0); tcpEV << "TCP Header Option TS(TSval=" << option.getValues(0) << ", TSecr=" << option.getValues(1) << ") sent\n"; tcpseg->setOptionsArraySize(tcpseg->getOptionsArraySize()+1); tcpseg->setOptions(t,option); t++; } // SACK header option // RFC 2018, page 4: // "If sent at all, SACK options SHOULD be included in all ACKs which do // not ACK the highest sequence number in the data receiver's queue. In // this situation the network has lost or mis-ordered data, such that // the receiver holds non-contiguous data in its queue. RFC 1122, // Section 4.2.2.21, discusses the reasons for the receiver to send ACKs // in response to additional segments received in this state. The // receiver SHOULD send an ACK for every valid segment that arrives // containing new data, and each of these "duplicate" ACKs SHOULD bear a // SACK option." if (state->sack_enabled && (state->snd_sack || state->snd_dsack)) { if (!state->ts_enabled) // if TS is enabled, do not add NOPs to this segment { // 2 padding bytes option.setKind(TCPOPTION_NO_OPERATION); // NOP option.setLength(1); option.setValuesArraySize(0); tcpseg->setOptionsArraySize(tcpseg->getOptionsArraySize()+2); tcpseg->setOptions(t,option); t++; tcpseg->setOptions(t,option); t++; } addSacks(tcpseg); t++; } // TODO add new TCPOptions here once they are implemented // TODO delegate to TCPAlgorithm as well -- it may want to append additional options } if (tcpseg->getOptionsArraySize() != 0) { uint options_len = 0; for (uint i=0; i<tcpseg->getOptionsArraySize(); i++) options_len = options_len + tcpseg->getOptions(i).getLength(); if (options_len <= 40) // Options length allowed? - maximum: 40 Bytes tcpseg->setHeaderLength(TCP_HEADER_OCTETS+options_len); // TCP_HEADER_OCTETS = 20 else { tcpseg->setHeaderLength(TCP_HEADER_OCTETS); // TCP_HEADER_OCTETS = 20 tcpseg->setOptionsArraySize(0); // drop all options tcpEV << "ERROR: Options length exceeded! Segment will be sent without options" << "\n"; } } return *tcpseg; }
Definition at line 311 of file TCPConnection.h.
Referenced by TCP::addForkedConnection(), cloneListeningConnection(), printConnBrief(), TCP::removeConnection(), sendEstabIndicationToApp(), sendIndicationToApp(), sendToApp(), and TCPConnection().
cMessage* TCPConnection::connEstabTimer [protected] |
Definition at line 342 of file TCPConnection.h.
Referenced by process_OPEN_ACTIVE(), process_SEND(), processSegmentInListen(), processTimer(), stateEntered(), TCPConnection(), and ~TCPConnection().
Definition at line 312 of file TCPConnection.h.
Referenced by TCP::addForkedConnection(), cloneListeningConnection(), printConnBrief(), processSegment1stThru8th(), processSegmentInListen(), TCP::removeConnection(), sendEstabIndicationToApp(), sendIndicationToApp(), and TCPConnection().
cOutVector* TCPConnection::dupAcksVector [protected] |
Definition at line 356 of file TCPConnection.h.
Referenced by processAckInEstabEtc(), processSegment1stThru8th(), TCPConnection(), and ~TCPConnection().
cMessage* TCPConnection::finWait2Timer [protected] |
Definition at line 343 of file TCPConnection.h.
Referenced by processSegment1stThru8th(), processTimer(), stateEntered(), TCPConnection(), and ~TCPConnection().
cFSM TCPConnection::fsm [protected] |
Definition at line 324 of file TCPConnection.h.
Referenced by cloneListeningConnection(), performStateTransition(), printConnBrief(), process_ABORT(), process_CLOSE(), process_OPEN_ACTIVE(), process_OPEN_PASSIVE(), process_RCV_SEGMENT(), process_SEND(), process_STATUS(), process_TIMEOUT_2MSL(), process_TIMEOUT_CONN_ESTAB(), process_TIMEOUT_FIN_WAIT_2(), process_TIMEOUT_SYN_REXMIT(), processAckInEstabEtc(), processMSSOption(), processSACKOption(), processSACKPermittedOption(), processSegment1stThru8th(), processTSOption(), processWSOption(), TCPConnection(), and writeHeaderOptions().
Definition at line 315 of file TCPConnection.h.
Referenced by TCP::addForkedConnection(), TCP::addSockPair(), cloneListeningConnection(), printConnBrief(), process_OPEN_ACTIVE(), process_OPEN_PASSIVE(), process_STATUS(), processTCPSegment(), TCP::removeConnection(), sendEstabIndicationToApp(), sendRst(), sendToIP(), and TCP::updateSockPair().
Definition at line 317 of file TCPConnection.h.
Referenced by TCP::addForkedConnection(), TCP::addSockPair(), cloneListeningConnection(), printConnBrief(), process_OPEN_ACTIVE(), process_OPEN_PASSIVE(), process_STATUS(), processTCPSegment(), TCP::removeConnection(), sendEstabIndicationToApp(), sendRst(), sendSyn(), sendToIP(), TCPConnection(), and TCP::updateSockPair().
cOutVector* TCPConnection::pipeVector [protected] |
Definition at line 357 of file TCPConnection.h.
Referenced by setPipe(), TCPConnection(), and ~TCPConnection().
cOutVector* TCPConnection::rcvAckVector [protected] |
Definition at line 353 of file TCPConnection.h.
Referenced by process_RCV_SEGMENT(), TCPConnection(), and ~TCPConnection().
cOutVector* TCPConnection::rcvAdvVector [protected] |
Definition at line 349 of file TCPConnection.h.
Referenced by processSegmentInListen(), processSegmentInSynSent(), TCPConnection(), updateRcvWnd(), and ~TCPConnection().
cOutVector* TCPConnection::rcvOooSegVector [protected] |
Definition at line 360 of file TCPConnection.h.
Referenced by processSegment1stThru8th(), TCPConnection(), and ~TCPConnection().
cOutVector* TCPConnection::rcvSacksVector [protected] |
Definition at line 359 of file TCPConnection.h.
Referenced by processSACKOption(), TCPConnection(), and ~TCPConnection().
cOutVector* TCPConnection::rcvSeqVector [protected] |
Definition at line 352 of file TCPConnection.h.
Referenced by process_RCV_SEGMENT(), TCPConnection(), and ~TCPConnection().
cOutVector* TCPConnection::rcvWndVector [protected] |
Definition at line 348 of file TCPConnection.h.
Referenced by TCPConnection(), updateRcvWnd(), and ~TCPConnection().
TCPReceiveQueue* TCPConnection::receiveQueue [protected] |
Definition at line 331 of file TCPConnection.h.
Referenced by addSacks(), cloneListeningConnection(), initConnection(), processSegment1stThru8th(), processSegmentInListen(), processSegmentInSynSent(), TCPConnection(), updateRcvQueueVars(), writeHeaderOptions(), and ~TCPConnection().
Definition at line 316 of file TCPConnection.h.
Referenced by TCP::addForkedConnection(), TCP::addSockPair(), printConnBrief(), process_OPEN_ACTIVE(), process_STATUS(), processTCPSegment(), TCP::removeConnection(), sendEstabIndicationToApp(), sendRst(), sendSyn(), sendToIP(), and TCP::updateSockPair().
Definition at line 318 of file TCPConnection.h.
Referenced by TCP::addForkedConnection(), TCP::addSockPair(), printConnBrief(), process_OPEN_ACTIVE(), process_STATUS(), processTCPSegment(), TCP::removeConnection(), sendEstabIndicationToApp(), sendRst(), sendSyn(), sendToIP(), TCPConnection(), and TCP::updateSockPair().
Definition at line 334 of file TCPConnection.h.
Referenced by cloneListeningConnection(), initConnection(), isLost(), nextSeg(), processAckInEstabEtc(), TCPBaseAlg::processRexmitTimer(), processRstInSynReceived(), processSACKOption(), processSegmentInSynSent(), retransmitOneSegment(), selectInitialSeqNum(), sendData(), sendSegment(), sendSegmentDuringLossRecoveryPhase(), setPipe(), TCPConnection(), and ~TCPConnection().
cOutVector* TCPConnection::sackedBytesVector [protected] |
Definition at line 362 of file TCPConnection.h.
Referenced by processSACKOption(), TCPConnection(), and ~TCPConnection().
TCPSendQueue* TCPConnection::sendQueue [protected] |
Definition at line 330 of file TCPConnection.h.
Referenced by cloneListeningConnection(), initConnection(), isSendQueueEmpty(), nextSeg(), process_CLOSE(), process_SEND(), processAckInEstabEtc(), processRstInSynReceived(), processSegmentInSynSent(), retransmitData(), retransmitOneSegment(), selectInitialSeqNum(), sendData(), sendOneNewSegment(), sendProbe(), sendSegment(), TCPConnection(), and ~TCPConnection().
cOutVector* TCPConnection::sndAckVector [protected] |
Definition at line 351 of file TCPConnection.h.
Referenced by sendToIP(), TCPConnection(), and ~TCPConnection().
cOutVector* TCPConnection::sndNxtVector [protected] |
Definition at line 350 of file TCPConnection.h.
Referenced by sendToIP(), TCPConnection(), and ~TCPConnection().
cOutVector* TCPConnection::sndSacksVector [protected] |
Definition at line 358 of file TCPConnection.h.
Referenced by addSacks(), TCPConnection(), and ~TCPConnection().
cOutVector* TCPConnection::sndWndVector [protected] |
Definition at line 347 of file TCPConnection.h.
Referenced by TCPConnection(), updateWndInfo(), and ~TCPConnection().
TCPStateVariables* TCPConnection::state [protected] |
Definition at line 327 of file TCPConnection.h.
Referenced by addSacks(), cloneListeningConnection(), configureStateVariables(), initConnection(), isLost(), isSegmentAcceptable(), isSendQueueEmpty(), nextSeg(), performStateTransition(), process_ABORT(), process_CLOSE(), process_OPEN_ACTIVE(), process_OPEN_PASSIVE(), process_RCV_SEGMENT(), process_SEND(), process_STATUS(), process_TIMEOUT_CONN_ESTAB(), process_TIMEOUT_SYN_REXMIT(), processAckInEstabEtc(), processMSSOption(), processRstInSynReceived(), processSACKOption(), processSACKPermittedOption(), processSegment1stThru8th(), processSegmentInListen(), processSegmentInSynSent(), processTSOption(), processWSOption(), retransmitData(), retransmitOneSegment(), segmentArrivalWhileClosed(), selectInitialSeqNum(), sendAck(), sendData(), sendDataDuringLossRecoveryPhase(), sendFin(), sendOneNewSegment(), sendProbe(), sendSegment(), sendSegmentDuringLossRecoveryPhase(), sendSyn(), sendSynAck(), sendToIP(), setPipe(), startSynRexmitTimer(), TCPConnection(), updateRcvQueueVars(), updateRcvWnd(), updateWndInfo(), writeHeaderOptions(), and ~TCPConnection().
cMessage* TCPConnection::synRexmitTimer [protected] |
Definition at line 344 of file TCPConnection.h.
Referenced by process_TIMEOUT_SYN_REXMIT(), processTimer(), startSynRexmitTimer(), stateEntered(), TCPConnection(), and ~TCPConnection().
TCPAlgorithm* TCPConnection::tcpAlgorithm [protected] |
Definition at line 338 of file TCPConnection.h.
Referenced by cloneListeningConnection(), initConnection(), process_CLOSE(), process_SEND(), processAckInEstabEtc(), processSegment1stThru8th(), processSegmentInSynSent(), processTimer(), retransmitOneSegment(), sendAck(), sendData(), sendFin(), sendOneNewSegment(), sendProbe(), sendRstAck(), sendSegmentDuringLossRecoveryPhase(), sendSynAck(), stateEntered(), TCPConnection(), and ~TCPConnection().
TCP* TCPConnection::tcpMain [protected] |
Definition at line 321 of file TCPConnection.h.
Referenced by cloneListeningConnection(), configureStateVariables(), initConnection(), performStateTransition(), process_OPEN_ACTIVE(), process_OPEN_PASSIVE(), processSegmentInListen(), processSegmentInSynSent(), sendEstabIndicationToApp(), sendIndicationToApp(), sendToApp(), sendToIP(), and TCPConnection().
cOutVector* TCPConnection::tcpRcvQueueBytesVector [protected] |
Definition at line 363 of file TCPConnection.h.
Referenced by TCPConnection(), updateRcvQueueVars(), and ~TCPConnection().
cOutVector* TCPConnection::tcpRcvQueueDropsVector [protected] |
Definition at line 364 of file TCPConnection.h.
Referenced by processSegment1stThru8th(), processSegmentInListen(), processSegmentInSynSent(), TCPConnection(), and ~TCPConnection().
cMessage* TCPConnection::the2MSLTimer [protected] |
Definition at line 341 of file TCPConnection.h.
Referenced by processSegment1stThru8th(), processTimer(), stateEntered(), TCPConnection(), and ~TCPConnection().
cOutVector* TCPConnection::unackedVector [protected] |
Definition at line 354 of file TCPConnection.h.
Referenced by process_CLOSE(), processAckInEstabEtc(), sendData(), sendOneNewSegment(), sendProbe(), sendSegmentDuringLossRecoveryPhase(), TCPConnection(), and ~TCPConnection().