TCPConnection Class Reference

#include <TCPConnection.h>

List of all members.


Detailed Description

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:

  1. dispatch to specific methods. For example, processAppCommand() invokes one of process_OPEN_ACTIVE(), process_OPEN_PASSIVE() or process_SEND(), etc., and processTCPSegment() dispatches to processSegmentInListen(), processSegmentInSynSent() or processSegment1stThru8th(). Those methods will do the REAL JOB.
  2. after they return, we'll know the state machine event (TCPEventCode, TCP_E_xxx) for sure, so we can:
  3. invoke performStateTransition() which executes the necessary state transition (for example, TCP_E_RCV_SYN will take the state machine from TCP_S_LISTEN to TCP_S_SYN_RCVD). No other actions are taken in this step.
  4. if there was a state change (for example, we entered the TCP_S_ESTABLISHED state), performStateTransition() invokes stateEntered(), which performs some necessary housekeeping (cancel the CONN-ESTAB timer).

When the CLOSED state is reached, TCP will delete the TCPConnection object.

Public Member Functions

virtual void sendAck ()
virtual bool sendData (bool fullSegmentsOnly, int congestionWindow=-1)
virtual bool sendProbe ()
virtual void retransmitOneSegment ()
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 (int bytes)
virtual void sendToIP (TCPSegment *tcpseg)
virtual TCPSegmentcreateTCPSegment (const char *name)
virtual void startSynRexmitTimer ()
virtual void signalConnectionTimeout ()
void scheduleTimeout (cMessage *msg, simtime_t timeout)
virtual void printConnBrief ()
 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)
int getFsmState () const
TCPStateVariablesgetState ()
TCPSendQueuegetSendQueue ()
TCPReceiveQueuegetReceiveQueue ()
TCPAlgorithmgetTcpAlgorithm ()
TCPgetTcpMain ()

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)

Public Attributes

int appGateIndex
int connId
IPvXAddress localAddr
IPvXAddress remoteAddr
int localPort
int remotePort

Protected Member Functions

virtual TCPConnectioncloneListeningConnection ()
virtual void initConnection (TCPOpenCommand *openCmd)
virtual void configureStateVariables ()
virtual void selectInitialSeqNum ()
virtual bool isSegmentAcceptable (TCPSegment *tcpseg)
virtual void sendSyn ()
virtual void sendSynAck ()
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 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

TCPtcpMain
cFSM fsm
TCPStateVariablesstate
TCPSendQueuesendQueue
TCPReceiveQueuereceiveQueue
TCPAlgorithmtcpAlgorithm
cMessage * the2MSLTimer
cMessage * connEstabTimer
cMessage * finWait2Timer
cMessage * synRexmitTimer
cOutVector * sndWndVector
cOutVector * sndNxtVector
cOutVector * sndAckVector
cOutVector * rcvSeqVector
cOutVector * rcvAckVector
cOutVector * unackedVector


Constructor & Destructor Documentation

TCPConnection::TCPConnection ( TCP mod,
int  appGateIndex,
int  connId 
)

The "normal" constructor.

00112 {
00113     tcpMain = _mod;
00114     appGateIndex = _appGateIndex;
00115     connId = _connId;
00116 
00117     localPort = remotePort = -1;
00118 
00119     char fsmname[24];
00120     sprintf(fsmname, "fsm-%d", connId);
00121     fsm.setName(fsmname);
00122     fsm.setState(TCP_S_INIT);
00123 
00124 
00125     // queues and algorithm will be created on active or passive open
00126     sendQueue = NULL;
00127     receiveQueue = NULL;
00128     tcpAlgorithm = NULL;
00129     state = NULL;
00130 
00131     the2MSLTimer = new cMessage("2MSL");
00132     connEstabTimer = new cMessage("CONN-ESTAB");
00133     finWait2Timer = new cMessage("FIN-WAIT-2");
00134     synRexmitTimer = new cMessage("SYN-REXMIT");
00135 
00136     the2MSLTimer->setContextPointer(this);
00137     connEstabTimer->setContextPointer(this);
00138     finWait2Timer->setContextPointer(this);
00139     synRexmitTimer->setContextPointer(this);
00140 
00141     // statistics
00142     sndWndVector = NULL;
00143     sndNxtVector = NULL;
00144     sndAckVector = NULL;
00145     rcvSeqVector = NULL;
00146     rcvAckVector = NULL;
00147     unackedVector = NULL;
00148 
00149     if (getTcpMain()->recordStatistics)
00150     {
00151         sndWndVector = new cOutVector("send window");
00152         sndNxtVector = new cOutVector("send seq");
00153         sndAckVector = new cOutVector("sent ack");
00154         rcvSeqVector = new cOutVector("rcvd seq");
00155         rcvAckVector = new cOutVector("rcvd ack");
00156         unackedVector = new cOutVector("unacked bytes");
00157     }
00158 }

TCPConnection::TCPConnection (  ) 

Note: this default ctor is NOT used to create live connections, only temporary ones so that TCPMain can invoke their segmentArrivalWhileClosed().

Referenced by cloneListeningConnection().

00096 {
00097     // Note: this ctor is NOT used to create live connections, only
00098     // temporary ones to invoke segmentArrivalWhileClosed() on
00099     sendQueue = NULL;
00100     receiveQueue = NULL;
00101     tcpAlgorithm = NULL;
00102     state = NULL;
00103     the2MSLTimer = connEstabTimer = finWait2Timer = synRexmitTimer = NULL;
00104     sndWndVector = sndNxtVector = sndAckVector = rcvSeqVector = rcvAckVector = unackedVector = NULL;
00105 }

TCPConnection::~TCPConnection (  )  [virtual]

Destructor.

00161 {
00162     delete sendQueue;
00163     delete receiveQueue;
00164     delete tcpAlgorithm;
00165     delete state;
00166 
00167     if (the2MSLTimer)   delete cancelEvent(the2MSLTimer);
00168     if (connEstabTimer) delete cancelEvent(connEstabTimer);
00169     if (finWait2Timer)  delete cancelEvent(finWait2Timer);
00170     if (synRexmitTimer) delete cancelEvent(synRexmitTimer);
00171 
00172     // statistics
00173     delete sndWndVector;
00174     delete sndNxtVector;
00175     delete sndAckVector;
00176     delete rcvSeqVector;
00177     delete rcvAckVector;
00178     delete unackedVector;
00179 }


Member Function Documentation

TCPEventCode TCPConnection::preanalyseAppCommandEvent ( int  commandCode  )  [protected, virtual]

Maps app command codes (msg kind of app command msgs) to TCP_E_xxx event codes

Referenced by processAppCommand().

00267 {
00268     switch (commandCode)
00269     {
00270         case TCP_C_OPEN_ACTIVE:  return TCP_E_OPEN_ACTIVE;
00271         case TCP_C_OPEN_PASSIVE: return TCP_E_OPEN_PASSIVE;
00272         case TCP_C_SEND:         return TCP_E_SEND;
00273         case TCP_C_CLOSE:        return TCP_E_CLOSE;
00274         case TCP_C_ABORT:        return TCP_E_ABORT;
00275         case TCP_C_STATUS:       return TCP_E_STATUS;
00276         default: opp_error("Unknown message kind in app command");
00277                  return (TCPEventCode)0; // to satisfy compiler
00278     }
00279 }

bool TCPConnection::performStateTransition ( const TCPEventCode event  )  [protected, virtual]

Implemements the pure TCP state machine

Referenced by processAppCommand(), processTCPSegment(), and processTimer().

00282 {
00283     ASSERT(fsm.getState()!=TCP_S_CLOSED); // closed connections should be deleted immediately
00284 
00285     if (event==TCP_E_IGNORE)  // e.g. discarded segment
00286     {
00287         tcpEV << "Staying in state: " << stateName(fsm.getState()) << " (no FSM event)\n";
00288         return true;
00289     }
00290 
00291     // state machine
00292     // TBD add handling of connection timeout event (keepalive), with transition to CLOSED
00293     // Note: empty "default:" lines are for gcc's benefit which would otherwise spit warnings
00294     int oldState = fsm.getState();
00295     switch (fsm.getState())
00296     {
00297         case TCP_S_INIT:
00298             switch (event)
00299             {
00300                 case TCP_E_OPEN_PASSIVE:FSM_Goto(fsm, TCP_S_LISTEN); break;
00301                 case TCP_E_OPEN_ACTIVE: FSM_Goto(fsm, TCP_S_SYN_SENT); break;
00302                 default:;
00303             }
00304             break;
00305 
00306         case TCP_S_LISTEN:
00307             switch (event)
00308             {
00309                 case TCP_E_OPEN_ACTIVE: FSM_Goto(fsm, TCP_S_SYN_SENT); break;
00310                 case TCP_E_SEND:        FSM_Goto(fsm, TCP_S_SYN_SENT); break;
00311                 case TCP_E_CLOSE:       FSM_Goto(fsm, TCP_S_CLOSED); break;
00312                 case TCP_E_ABORT:       FSM_Goto(fsm, TCP_S_CLOSED); break;
00313                 case TCP_E_RCV_SYN:     FSM_Goto(fsm, TCP_S_SYN_RCVD);break;
00314                 default:;
00315             }
00316             break;
00317 
00318         case TCP_S_SYN_RCVD:
00319             switch (event)
00320             {
00321                 case TCP_E_CLOSE:       FSM_Goto(fsm, TCP_S_FIN_WAIT_1); break;
00322                 case TCP_E_ABORT:       FSM_Goto(fsm, TCP_S_CLOSED); break;
00323                 case TCP_E_TIMEOUT_CONN_ESTAB: FSM_Goto(fsm, state->active ? TCP_S_CLOSED : TCP_S_LISTEN); break;
00324                 case TCP_E_RCV_RST:     FSM_Goto(fsm, state->active ? TCP_S_CLOSED : TCP_S_LISTEN); break;
00325                 case TCP_E_RCV_ACK:     FSM_Goto(fsm, TCP_S_ESTABLISHED); break;
00326                 case TCP_E_RCV_FIN:     FSM_Goto(fsm, TCP_S_CLOSE_WAIT); break;
00327                 case TCP_E_RCV_UNEXP_SYN: FSM_Goto(fsm, TCP_S_CLOSED); break;
00328                 default:;
00329             }
00330             break;
00331 
00332         case TCP_S_SYN_SENT:
00333             switch (event)
00334             {
00335                 case TCP_E_CLOSE:       FSM_Goto(fsm, TCP_S_CLOSED); break;
00336                 case TCP_E_ABORT:       FSM_Goto(fsm, TCP_S_CLOSED); break;
00337                 case TCP_E_TIMEOUT_CONN_ESTAB: FSM_Goto(fsm, TCP_S_CLOSED); break;
00338                 case TCP_E_RCV_RST:     FSM_Goto(fsm, TCP_S_CLOSED); break;
00339                 case TCP_E_RCV_SYN_ACK: FSM_Goto(fsm, TCP_S_ESTABLISHED); break;
00340                 case TCP_E_RCV_SYN:     FSM_Goto(fsm, TCP_S_SYN_RCVD); break;
00341                 default:;
00342             }
00343             break;
00344 
00345         case TCP_S_ESTABLISHED:
00346             switch (event)
00347             {
00348                 case TCP_E_CLOSE:       FSM_Goto(fsm, TCP_S_FIN_WAIT_1); break;
00349                 case TCP_E_ABORT:       FSM_Goto(fsm, TCP_S_CLOSED); break;
00350                 case TCP_E_RCV_FIN:     FSM_Goto(fsm, TCP_S_CLOSE_WAIT); break;
00351                 case TCP_E_RCV_RST:     FSM_Goto(fsm, TCP_S_CLOSED); break;
00352                 case TCP_E_RCV_UNEXP_SYN: FSM_Goto(fsm, TCP_S_CLOSED); break;
00353                 default:;
00354             }
00355             break;
00356 
00357         case TCP_S_CLOSE_WAIT:
00358             switch (event)
00359             {
00360                 case TCP_E_CLOSE:       FSM_Goto(fsm, TCP_S_LAST_ACK); break;
00361                 case TCP_E_ABORT:       FSM_Goto(fsm, TCP_S_CLOSED); break;
00362                 case TCP_E_RCV_RST:     FSM_Goto(fsm, TCP_S_CLOSED); break;
00363                 case TCP_E_RCV_UNEXP_SYN: FSM_Goto(fsm, TCP_S_CLOSED); break;
00364                 default:;
00365             }
00366             break;
00367 
00368         case TCP_S_LAST_ACK:
00369             switch (event)
00370             {
00371                 case TCP_E_ABORT:       FSM_Goto(fsm, TCP_S_CLOSED); break;
00372                 case TCP_E_RCV_ACK:     FSM_Goto(fsm, TCP_S_CLOSED); break;
00373                 case TCP_E_RCV_RST:     FSM_Goto(fsm, TCP_S_CLOSED); break;
00374                 case TCP_E_RCV_UNEXP_SYN: FSM_Goto(fsm, TCP_S_CLOSED); break;
00375                 default:;
00376             }
00377             break;
00378 
00379         case TCP_S_FIN_WAIT_1:
00380             switch (event)
00381             {
00382                 case TCP_E_ABORT:       FSM_Goto(fsm, TCP_S_CLOSED); break;
00383                 case TCP_E_RCV_FIN:     FSM_Goto(fsm, TCP_S_CLOSING); break;
00384                 case TCP_E_RCV_ACK:     FSM_Goto(fsm, TCP_S_FIN_WAIT_2); break;
00385                 case TCP_E_RCV_FIN_ACK: FSM_Goto(fsm, TCP_S_TIME_WAIT); break;
00386                 case TCP_E_RCV_RST:     FSM_Goto(fsm, TCP_S_CLOSED); break;
00387                 case TCP_E_RCV_UNEXP_SYN: FSM_Goto(fsm, TCP_S_CLOSED); break;
00388                 default:;
00389             }
00390             break;
00391 
00392         case TCP_S_FIN_WAIT_2:
00393             switch (event)
00394             {
00395                 case TCP_E_ABORT:       FSM_Goto(fsm, TCP_S_CLOSED); break;
00396                 case TCP_E_RCV_FIN:     FSM_Goto(fsm, TCP_S_TIME_WAIT); break;
00397                 case TCP_E_TIMEOUT_FIN_WAIT_2: FSM_Goto(fsm, TCP_S_CLOSED); break;
00398                 case TCP_E_RCV_RST:     FSM_Goto(fsm, TCP_S_CLOSED); break;
00399                 case TCP_E_RCV_UNEXP_SYN: FSM_Goto(fsm, TCP_S_CLOSED); break;
00400                 default:;
00401             }
00402             break;
00403 
00404         case TCP_S_CLOSING:
00405             switch (event)
00406             {
00407                 case TCP_E_ABORT:       FSM_Goto(fsm, TCP_S_CLOSED); break;
00408                 case TCP_E_RCV_ACK:     FSM_Goto(fsm, TCP_S_TIME_WAIT); break;
00409                 case TCP_E_RCV_RST:     FSM_Goto(fsm, TCP_S_CLOSED); break;
00410                 case TCP_E_RCV_UNEXP_SYN: FSM_Goto(fsm, TCP_S_CLOSED); break;
00411                 default:;
00412             }
00413             break;
00414 
00415         case TCP_S_TIME_WAIT:
00416             switch (event)
00417             {
00418                 case TCP_E_ABORT:       FSM_Goto(fsm, TCP_S_CLOSED); break;
00419                 case TCP_E_TIMEOUT_2MSL: FSM_Goto(fsm, TCP_S_CLOSED); break;
00420                 case TCP_E_RCV_RST:     FSM_Goto(fsm, TCP_S_CLOSED); break;
00421                 case TCP_E_RCV_UNEXP_SYN: FSM_Goto(fsm, TCP_S_CLOSED); break;
00422                 default:;
00423             }
00424             break;
00425 
00426         case TCP_S_CLOSED:
00427             break;
00428     }
00429 
00430     if (oldState!=fsm.getState())
00431     {
00432         tcpEV << "Transition: " << stateName(oldState) << " --> " << stateName(fsm.getState()) << "  (event was: " << eventName(event) << ")\n";
00433         testingEV << tcpMain->getName() << ": " << stateName(oldState) << " --> " << stateName(fsm.getState()) << "  (on " << eventName(event) << ")\n";
00434 
00435         // cancel timers, etc.
00436         stateEntered(fsm.getState());
00437     }
00438     else
00439     {
00440         tcpEV << "Staying in state: " << stateName(fsm.getState()) << " (event was: " << eventName(event) << ")\n";
00441     }
00442 
00443     return fsm.getState()!=TCP_S_CLOSED;
00444 }

void TCPConnection::stateEntered ( int  state  )  [protected, virtual]

Perform cleanup necessary when entering a new state, e.g. cancelling timers

Referenced by performStateTransition().

00447 {
00448     // cancel timers
00449     switch (state)
00450     {
00451         case TCP_S_INIT:
00452             // we'll never get back to INIT
00453             break;
00454         case TCP_S_LISTEN:
00455             // we may get back to LISTEN from SYN_RCVD
00456             ASSERT(connEstabTimer && synRexmitTimer);
00457             cancelEvent(connEstabTimer);
00458             cancelEvent(synRexmitTimer);
00459             break;
00460         case TCP_S_SYN_RCVD:
00461         case TCP_S_SYN_SENT:
00462             break;
00463         case TCP_S_ESTABLISHED:
00464             // we're in ESTABLISHED, these timers are no longer needed
00465             delete cancelEvent(connEstabTimer);
00466             delete cancelEvent(synRexmitTimer);
00467             connEstabTimer = synRexmitTimer = NULL;
00468             // TCP_I_ESTAB notification moved inside event processing
00469             break;
00470         case TCP_S_CLOSE_WAIT:
00471         case TCP_S_LAST_ACK:
00472         case TCP_S_FIN_WAIT_1:
00473         case TCP_S_FIN_WAIT_2:
00474         case TCP_S_CLOSING:
00475         case TCP_S_TIME_WAIT:
00476             // whether connection setup succeeded (ESTABLISHED) or not (others),
00477             // cancel these timers
00478             if (connEstabTimer) cancelEvent(connEstabTimer);
00479             if (synRexmitTimer) cancelEvent(synRexmitTimer);
00480             break;
00481         case TCP_S_CLOSED:
00482             // all timers need to be cancelled
00483             if (the2MSLTimer)   cancelEvent(the2MSLTimer);
00484             if (connEstabTimer) cancelEvent(connEstabTimer);
00485             if (finWait2Timer)  cancelEvent(finWait2Timer);
00486             if (synRexmitTimer) cancelEvent(synRexmitTimer);
00487             tcpAlgorithm->connectionClosed();
00488             break;
00489     }
00490 }

void TCPConnection::process_OPEN_ACTIVE ( TCPEventCode event,
TCPCommand *  tcpCommand,
cMessage *  msg 
) [protected, virtual]

Referenced by processAppCommand().

00034 {
00035     TCPOpenCommand *openCmd = check_and_cast<TCPOpenCommand *>(tcpCommand);
00036     IPvXAddress localAddr, remoteAddr;
00037     short localPort, remotePort;
00038 
00039     switch(fsm.getState())
00040     {
00041         case TCP_S_INIT:
00042             initConnection(openCmd);
00043 
00044             // store local/remote socket
00045             state->active = true;
00046             localAddr = openCmd->getLocalAddr();
00047             remoteAddr = openCmd->getRemoteAddr();
00048             localPort = openCmd->getLocalPort();
00049             remotePort = openCmd->getRemotePort();
00050 
00051             if (remoteAddr.isUnspecified() || remotePort==-1)
00052                 opp_error("Error processing command OPEN_ACTIVE: remote address and port must be specified");
00053 
00054             if (localPort==-1)
00055             {
00056                 localPort = tcpMain->getEphemeralPort();
00057                 tcpEV << "Assigned ephemeral port " << localPort << "\n";
00058             }
00059 
00060             tcpEV << "OPEN: " << localAddr << ":" << localPort << " --> " << remoteAddr << ":" << remotePort << "\n";
00061 
00062             tcpMain->addSockPair(this, localAddr, remoteAddr, localPort, remotePort);
00063 
00064             // send initial SYN
00065             selectInitialSeqNum();
00066             sendSyn();
00067             startSynRexmitTimer();
00068             scheduleTimeout(connEstabTimer, TCP_TIMEOUT_CONN_ESTAB);
00069             break;
00070 
00071         default:
00072             opp_error("Error processing command OPEN_ACTIVE: connection already exists");
00073     }
00074 
00075     delete openCmd;
00076     delete msg;
00077 }

void TCPConnection::process_OPEN_PASSIVE ( TCPEventCode event,
TCPCommand *  tcpCommand,
cMessage *  msg 
) [protected, virtual]

Referenced by processAppCommand().

00080 {
00081     TCPOpenCommand *openCmd = check_and_cast<TCPOpenCommand *>(tcpCommand);
00082     IPvXAddress localAddr;
00083     short localPort;
00084 
00085     switch(fsm.getState())
00086     {
00087         case TCP_S_INIT:
00088             initConnection(openCmd);
00089 
00090             // store local/remote socket
00091             state->active = false;
00092             state->fork = openCmd->getFork();
00093             localAddr = openCmd->getLocalAddr();
00094             localPort = openCmd->getLocalPort();
00095 
00096             if (localPort==-1)
00097                 opp_error("Error processing command OPEN_PASSIVE: local port must be specified");
00098 
00099             tcpEV << "Starting to listen on: " << localAddr << ":" << localPort << "\n";
00100 
00101             tcpMain->addSockPair(this, localAddr, IPvXAddress(), localPort, -1);
00102             break;
00103 
00104         default:
00105             opp_error("Error processing command OPEN_PASSIVE: connection already exists");
00106     }
00107 
00108     delete openCmd;
00109     delete msg;
00110 }

void TCPConnection::process_SEND ( TCPEventCode event,
TCPCommand *  tcpCommand,
cMessage *  msg 
) [protected, virtual]

Referenced by processAppCommand().

00113 {
00114     TCPSendCommand *sendCommand = check_and_cast<TCPSendCommand *>(tcpCommand);
00115 
00116     // FIXME how to support PUSH? One option is to treat each SEND as a unit of data,
00117     // and set PSH at SEND boundaries
00118     switch(fsm.getState())
00119     {
00120         case TCP_S_INIT:
00121             opp_error("Error processing command SEND: connection not open");
00122 
00123         case TCP_S_LISTEN:
00124             tcpEV << "SEND command turns passive open into active open, sending initial SYN\n";
00125             state->active = true;
00126             selectInitialSeqNum();
00127             sendSyn();
00128             startSynRexmitTimer();
00129             scheduleTimeout(connEstabTimer, TCP_TIMEOUT_CONN_ESTAB);
00130             sendQueue->enqueueAppData(PK(msg));  // queue up for later
00131             tcpEV << sendQueue->getBytesAvailable(state->snd_una) << " bytes in queue\n";
00132             break;
00133 
00134         case TCP_S_SYN_RCVD:
00135         case TCP_S_SYN_SENT:
00136             tcpEV << "Queueing up data for sending later.\n";
00137             sendQueue->enqueueAppData(PK(msg)); // queue up for later
00138             tcpEV << sendQueue->getBytesAvailable(state->snd_una) << " bytes in queue\n";
00139             break;
00140 
00141         case TCP_S_ESTABLISHED:
00142         case TCP_S_CLOSE_WAIT:
00143             sendQueue->enqueueAppData(PK(msg));
00144             tcpEV << sendQueue->getBytesAvailable(state->snd_una) << " bytes in queue, plus "
00145                   << (state->snd_max-state->snd_una) << " bytes unacknowledged\n";
00146             tcpAlgorithm->sendCommandInvoked();
00147             break;
00148 
00149         case TCP_S_LAST_ACK:
00150         case TCP_S_FIN_WAIT_1:
00151         case TCP_S_FIN_WAIT_2:
00152         case TCP_S_CLOSING:
00153         case TCP_S_TIME_WAIT:
00154             opp_error("Error processing command SEND: connection closing");
00155     }
00156 
00157     delete sendCommand; // msg itself has been taken by the sendQueue
00158 }

void TCPConnection::process_CLOSE ( TCPEventCode event,
TCPCommand *  tcpCommand,
cMessage *  msg 
) [protected, virtual]

Referenced by processAppCommand().

00161 {
00162     delete tcpCommand;
00163     delete msg;
00164 
00165     switch(fsm.getState())
00166     {
00167         case TCP_S_INIT:
00168             opp_error("Error processing command CLOSE: connection not open");
00169 
00170         case TCP_S_LISTEN:
00171             // Nothing to do here
00172             break;
00173 
00174         case TCP_S_SYN_SENT:
00175             // Delete the TCB and return "error:  closing" responses to any
00176             // queued SENDs, or RECEIVEs.
00177             break;
00178 
00179         case TCP_S_SYN_RCVD:
00180         case TCP_S_ESTABLISHED:
00181         case TCP_S_CLOSE_WAIT:
00182             //
00183             // SYN_RCVD processing (ESTABLISHED and CLOSE_WAIT are similar):
00184             //"
00185             // If no SENDs have been issued and there is no pending data to send,
00186             // then form a FIN segment and send it, and enter FIN-WAIT-1 state;
00187             // otherwise queue for processing after entering ESTABLISHED state.
00188             //"
00189             if (state->snd_max==sendQueue->getBufferEndSeq())
00190             {
00191                 tcpEV << "No outstanding SENDs, sending FIN right away, advancing snd_nxt over the FIN\n";
00192                 state->snd_nxt = state->snd_max;
00193                 sendFin();
00194                 state->snd_max = ++state->snd_nxt;
00195                 if (unackedVector) unackedVector->record(state->snd_max - state->snd_una);
00196 
00197                 // state transition will automatically take us to FIN_WAIT_1 (or LAST_ACK)
00198             }
00199             else
00200             {
00201                 tcpEV << "SEND of " << (sendQueue->getBufferEndSeq()-state->snd_max) <<
00202                       " bytes pending, deferring sending of FIN\n";
00203                 event = TCP_E_IGNORE;
00204             }
00205             state->send_fin = true;
00206             state->snd_fin_seq = sendQueue->getBufferEndSeq();
00207             break;
00208 
00209         case TCP_S_FIN_WAIT_1:
00210         case TCP_S_FIN_WAIT_2:
00211         case TCP_S_CLOSING:
00212         case TCP_S_LAST_ACK:
00213         case TCP_S_TIME_WAIT:
00214             // RFC 793 is not entirely clear on how to handle a duplicate close request.
00215             // Here we treat it as an error.
00216             opp_error("Duplicate CLOSE command: connection already closing");
00217     }
00218 }

void TCPConnection::process_ABORT ( TCPEventCode event,
TCPCommand *  tcpCommand,
cMessage *  msg 
) [protected, virtual]

Referenced by processAppCommand().

00221 {
00222     delete tcpCommand;
00223     delete msg;
00224 
00225     //
00226     // The ABORT event will automatically take the connection to the CLOSED
00227     // state, flush queues etc -- no need to do it here. Also, we don't need to
00228     // send notification to the user, they know what's going on.
00229     //
00230     switch(fsm.getState())
00231     {
00232         case TCP_S_INIT:
00233             opp_error("Error processing command ABORT: connection not open");
00234 
00235         case TCP_S_SYN_RCVD:
00236         case TCP_S_ESTABLISHED:
00237         case TCP_S_FIN_WAIT_1:
00238         case TCP_S_FIN_WAIT_2:
00239         case TCP_S_CLOSE_WAIT:
00240             //"
00241             // Send a reset segment:
00242             //
00243             //   <SEQ=SND.NXT><CTL=RST>
00244             //"
00245             sendRst(state->snd_nxt);
00246             break;
00247     }
00248 
00249 }

void TCPConnection::process_STATUS ( TCPEventCode event,
TCPCommand *  tcpCommand,
cMessage *  msg 
) [protected, virtual]

Referenced by processAppCommand().

00252 {
00253     delete tcpCommand; // but reuse msg for reply
00254 
00255     if (fsm.getState()==TCP_S_INIT)
00256         opp_error("Error processing command STATUS: connection not open");
00257 
00258     TCPStatusInfo *statusInfo = new TCPStatusInfo();
00259 
00260     statusInfo->setState(fsm.getState());
00261     statusInfo->setStateName(stateName(fsm.getState()));
00262 
00263     statusInfo->setLocalAddr(localAddr);
00264     statusInfo->setRemoteAddr(remoteAddr);
00265     statusInfo->setLocalPort(localPort);
00266     statusInfo->setRemotePort(remotePort);
00267 
00268     statusInfo->setSnd_mss(state->snd_mss);
00269     statusInfo->setSnd_una(state->snd_una);
00270     statusInfo->setSnd_nxt(state->snd_nxt);
00271     statusInfo->setSnd_max(state->snd_max);
00272     statusInfo->setSnd_wnd(state->snd_wnd);
00273     statusInfo->setSnd_up(state->snd_up);
00274     statusInfo->setSnd_wl1(state->snd_wl1);
00275     statusInfo->setSnd_wl2(state->snd_wl2);
00276     statusInfo->setIss(state->iss);
00277     statusInfo->setRcv_nxt(state->rcv_nxt);
00278     statusInfo->setRcv_wnd(state->rcv_wnd);
00279     statusInfo->setRcv_up(state->rcv_up);
00280     statusInfo->setIrs(state->irs);
00281     statusInfo->setFin_ack_rcvd(state->fin_ack_rcvd);
00282 
00283     msg->setControlInfo(statusInfo);
00284     sendToApp(msg);
00285 }

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.

Referenced by processTCPSegment().

00029 {
00030     // fast route processing not yet implemented
00031     return false;
00032 }

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.

Referenced by processTCPSegment().

00082 {
00083     tcpEV << "Seg arrived: ";
00084     printSegmentBrief(tcpseg);
00085     tcpEV << "TCB: " << state->info() << "\n";
00086 
00087     if (rcvSeqVector) rcvSeqVector->record(tcpseg->getSequenceNo());
00088     if (rcvAckVector) rcvAckVector->record(tcpseg->getAckNo());
00089 
00090     //
00091     // Note: this code is organized exactly as RFC 793, section "3.9 Event
00092     // Processing", subsection "SEGMENT ARRIVES".
00093     //
00094     TCPEventCode event;
00095     if (fsm.getState()==TCP_S_LISTEN)
00096     {
00097         event = processSegmentInListen(tcpseg, src, dest);
00098     }
00099     else if (fsm.getState()==TCP_S_SYN_SENT)
00100     {
00101         event = processSegmentInSynSent(tcpseg, src, dest);
00102     }
00103     else
00104     {
00105         // RFC 793 steps "first check sequence number", "second check the RST bit", etc
00106         event = processSegment1stThru8th(tcpseg);
00107     }
00108     delete tcpseg;
00109     return event;
00110 }

TCPEventCode TCPConnection::processSegmentInListen ( TCPSegment tcpseg,
IPvXAddress  src,
IPvXAddress  dest 
) [protected, virtual]

Referenced by process_RCV_SEGMENT().

00572 {
00573     tcpEV2 << "Processing segment in LISTEN\n";
00574 
00575     //"
00576     // first check for an RST
00577     //   An incoming RST should be ignored.  Return.
00578     //"
00579     if (tcpseg->getRstBit())
00580     {
00581         tcpEV << "RST bit set: dropping segment\n";
00582         return TCP_E_IGNORE;
00583     }
00584 
00585     //"
00586     // second check for an ACK
00587     //    Any acknowledgment is bad if it arrives on a connection still in
00588     //    the LISTEN state.  An acceptable reset segment should be formed
00589     //    for any arriving ACK-bearing segment.  The RST should be
00590     //    formatted as follows:
00591     //
00592     //      <SEQ=SEG.ACK><CTL=RST>
00593     //
00594     //    Return.
00595     //"
00596     if (tcpseg->getAckBit())
00597     {
00598         tcpEV << "ACK bit set: dropping segment and sending RST\n";
00599         sendRst(tcpseg->getAckNo(),destAddr,srcAddr,tcpseg->getDestPort(),tcpseg->getSrcPort());
00600         return TCP_E_IGNORE;
00601     }
00602 
00603     //"
00604     // third check for a SYN
00605     //"
00606     if (tcpseg->getSynBit())
00607     {
00608         if (tcpseg->getFinBit())
00609         {
00610             // Looks like implementations vary on how to react to SYN+FIN.
00611             // Some treat it as plain SYN (and reply with SYN+ACK), some send RST+ACK.
00612             // Let's just do the former here.
00613             tcpEV << "SYN+FIN received: ignoring FIN\n";
00614         }
00615 
00616         tcpEV << "SYN bit set: filling in foreign socket and sending SYN+ACK\n";
00617 
00618         //"
00619         // If the listen was not fully specified (i.e., the foreign socket was not
00620         // fully specified), then the unspecified fields should be filled in now.
00621         //"
00622         //
00623         // Also, we may need to fork, in order to leave another connection
00624         // LISTENing on the port. Note: forking will change our connId.
00625         //
00626         if (state->fork)
00627         {
00628             TCPConnection *conn = cloneListeningConnection(); // this will stay LISTENing
00629             tcpMain->addForkedConnection(this, conn, destAddr, srcAddr, tcpseg->getDestPort(), tcpseg->getSrcPort());
00630             tcpEV << "Connection forked: this connection got new connId=" << connId << ", "
00631                      "spinoff keeps LISTENing with connId=" << conn->connId << "\n";
00632         }
00633         else
00634         {
00635             tcpMain->updateSockPair(this, destAddr, srcAddr, tcpseg->getDestPort(), tcpseg->getSrcPort());
00636         }
00637 
00638         //"
00639         //  Set RCV.NXT to SEG.SEQ+1, IRS is set to SEG.SEQ and any other
00640         //  control or text should be queued for processing later.  ISS
00641         //  should be selected and a SYN segment sent of the form:
00642         //
00643         //    <SEQ=ISS><ACK=RCV.NXT><CTL=SYN,ACK>
00644         //
00645         //  SND.NXT is set to ISS+1 and SND.UNA to ISS.  The connection
00646         //  state should be changed to SYN-RECEIVED.
00647         //"
00648         state->rcv_nxt = tcpseg->getSequenceNo()+1;
00649         state->irs = tcpseg->getSequenceNo();
00650         receiveQueue->init(state->rcv_nxt);   // FIXME may init twice...
00651         selectInitialSeqNum();
00652 
00653         // although not mentioned in RFC 793, seems like we have to pick up
00654         // initial snd_wnd from the segment here.
00655         state->snd_wnd = tcpseg->getWindow();
00656         state->snd_wl1 = tcpseg->getSequenceNo();
00657         state->snd_wl2 = state->iss;
00658         if (sndWndVector) sndWndVector->record(state->snd_wnd);
00659 
00660         sendSynAck();
00661         startSynRexmitTimer();
00662         if (!connEstabTimer->isScheduled())
00663             scheduleTimeout(connEstabTimer, TCP_TIMEOUT_CONN_ESTAB);
00664 
00665         //"
00666         // Note that any other incoming control or data (combined with SYN)
00667         // will be processed in the SYN-RECEIVED state, but processing of SYN
00668         // and ACK should not be repeated.
00669         //"
00670         // We don't send text in SYN or SYN+ACK, but accept it. Otherwise
00671         // there isn't much left to do: RST, SYN, ACK, FIN got processed already,
00672         // so there's only URG and PSH left to handle.
00673         //
00674         if (tcpseg->getPayloadLength()>0)
00675             receiveQueue->insertBytesFromSegment(tcpseg);
00676         if (tcpseg->getUrgBit() || tcpseg->getPshBit())
00677             tcpEV << "Ignoring URG and PSH bits in SYN\n"; // TBD
00678 
00679         return TCP_E_RCV_SYN;  // this will take us to SYN_RCVD
00680     }
00681 
00682     //"
00683     //  fourth other text or control
00684     //   So you are unlikely to get here, but if you do, drop the segment, and return.
00685     //"
00686     tcpEV << "Unexpected segment: dropping it\n";
00687     return TCP_E_IGNORE;
00688 }

TCPEventCode TCPConnection::processSegmentInSynSent ( TCPSegment tcpseg,
IPvXAddress  src,
IPvXAddress  dest 
) [protected, virtual]

Referenced by process_RCV_SEGMENT().

00691 {
00692     tcpEV2 << "Processing segment in SYN_SENT\n";
00693 
00694     //"
00695     // first check the ACK bit
00696     //
00697     //   If the ACK bit is set
00698     //
00699     //     If SEG.ACK =< ISS, or SEG.ACK > SND.NXT, send a reset (unless
00700     //     the RST bit is set, if so drop the segment and return)
00701     //
00702     //       <SEQ=SEG.ACK><CTL=RST>
00703     //
00704     //     and discard the segment.  Return.
00705     //
00706     //     If SND.UNA =< SEG.ACK =< SND.NXT then the ACK is acceptable.
00707     //"
00708     if (tcpseg->getAckBit())
00709     {
00710         if (seqLE(tcpseg->getAckNo(),state->iss) || seqGreater(tcpseg->getAckNo(),state->snd_nxt))
00711         {
00712             tcpEV << "ACK bit set but wrong AckNo, sending RST\n";
00713             sendRst(tcpseg->getAckNo(),destAddr,srcAddr,tcpseg->getDestPort(),tcpseg->getSrcPort());
00714             return TCP_E_IGNORE;
00715         }
00716         tcpEV << "ACK bit set, AckNo acceptable\n";
00717     }
00718 
00719     //"
00720     // second check the RST bit
00721     //
00722     //   If the RST bit is set
00723     //
00724     //     If the ACK was acceptable then signal the user "error:
00725     //     connection reset", drop the segment, enter CLOSED state,
00726     //     delete TCB, and return.  Otherwise (no ACK) drop the segment
00727     //     and return.
00728     //"
00729     if (tcpseg->getRstBit())
00730     {
00731         if (tcpseg->getAckBit())
00732         {
00733             tcpEV << "RST+ACK: performing connection reset\n";
00734             sendIndicationToApp(TCP_I_CONNECTION_RESET);
00735             return TCP_E_RCV_RST;
00736         }
00737         else
00738         {
00739             tcpEV << "RST without ACK: dropping segment\n";
00740             return TCP_E_IGNORE;
00741         }
00742     }
00743 
00744     //"
00745     // third check the security and precedence -- not done
00746     //
00747     // fourth check the SYN bit
00748     //
00749     //   This step should be reached only if the ACK is ok, or there is
00750     //   no ACK, and it the segment did not contain a RST.
00751     //
00752     //   If the SYN bit is on and the security/compartment and precedence
00753     //   are acceptable then,
00754     //"
00755     if (tcpseg->getSynBit())
00756     {
00757         //
00758         //   RCV.NXT is set to SEG.SEQ+1, IRS is set to
00759         //   SEG.SEQ.  SND.UNA should be advanced to equal SEG.ACK (if there
00760         //   is an ACK), and any segments on the retransmission queue which
00761         //   are thereby acknowledged should be removed.
00762         //
00763         state->rcv_nxt = tcpseg->getSequenceNo()+1;
00764         state->irs = tcpseg->getSequenceNo();
00765         receiveQueue->init(state->rcv_nxt);
00766 
00767         if (tcpseg->getAckBit())
00768         {
00769             state->snd_una = tcpseg->getAckNo();
00770             sendQueue->discardUpTo(state->snd_una);
00771 
00772             // although not mentioned in RFC 793, seems like we have to pick up
00773             // initial snd_wnd from the segment here.
00774             state->snd_wnd = tcpseg->getWindow();
00775             state->snd_wl1 = tcpseg->getSequenceNo();
00776             state->snd_wl2 = tcpseg->getAckNo();
00777             if (sndWndVector) sndWndVector->record(state->snd_wnd);
00778         }
00779 
00780         // this also seems to be a good time to learn our local IP address
00781         // (was probably unspecified at connection open)
00782         tcpMain->updateSockPair(this, destAddr, srcAddr, tcpseg->getDestPort(), tcpseg->getSrcPort());
00783 
00784         //"
00785         //   If SND.UNA > ISS (our SYN has been ACKed), change the connection
00786         //   state to ESTABLISHED, form an ACK segment
00787         //
00788         //     <SEQ=SND.NXT><ACK=RCV.NXT><CTL=ACK>
00789         //
00790         //   and send it.  Data or controls which were queued for
00791         //   transmission may be included.  If there are other controls or
00792         //   text in the segment then continue processing at the sixth step
00793         //   below where the URG bit is checked, otherwise return.
00794         //"
00795         if (seqGreater(state->snd_una, state->iss))
00796         {
00797             tcpEV << "SYN+ACK bits set, connection established.\n";
00798 
00799             // RFC says "continue processing at the sixth step below where
00800             // the URG bit is checked". Those steps deal with: URG, segment text
00801             // (and PSH), and FIN.
00802             // Now: URG and PSH we don't support yet; in SYN+FIN we ignore FIN;
00803             // with segment text we just take it easy and put it in the receiveQueue
00804             // -- we'll forward it to the user when more data arrives.
00805             if (tcpseg->getFinBit())
00806                 tcpEV << "SYN+ACK+FIN received: ignoring FIN\n";
00807             if (tcpseg->getPayloadLength()>0)
00808                 state->rcv_nxt = receiveQueue->insertBytesFromSegment(tcpseg); // TBD forward to app, etc.
00809             if (tcpseg->getUrgBit() || tcpseg->getPshBit())
00810                 tcpEV << "Ignoring URG and PSH bits in SYN+ACK\n"; // TBD
00811 
00812             // notify tcpAlgorithm (it has to send ACK of SYN) and app layer
00813             tcpAlgorithm->established(true);
00814             sendEstabIndicationToApp();
00815 
00816             // This will trigger transition to ESTABLISHED. Timers and notifying
00817             // app will be taken care of in stateEntered().
00818             return TCP_E_RCV_SYN_ACK;
00819         }
00820 
00821         //"
00822         //   Otherwise enter SYN-RECEIVED, form a SYN,ACK segment
00823         //
00824         //     <SEQ=ISS><ACK=RCV.NXT><CTL=SYN,ACK>
00825         //
00826         //   and send it.  If there are other controls or text in the
00827         //   segment, queue them for processing after the ESTABLISHED state
00828         //   has been reached, return.
00829         //"
00830         tcpEV << "SYN bit set: sending SYN+ACK\n";
00831         state->snd_max = state->snd_nxt = state->iss;
00832         sendSynAck();
00833         startSynRexmitTimer();
00834 
00835         // Note: code below is similar to processing SYN in LISTEN.
00836 
00837         // For consistency with that code, we ignore SYN+FIN here
00838         if (tcpseg->getFinBit())
00839             tcpEV << "SYN+FIN received: ignoring FIN\n";
00840 
00841         // We don't send text in SYN or SYN+ACK, but accept it. Otherwise
00842         // there isn't much left to do: RST, SYN, ACK, FIN got processed already,
00843         // so there's only URG and PSH left to handle.
00844         if (tcpseg->getPayloadLength()>0)
00845             receiveQueue->insertBytesFromSegment(tcpseg);
00846         if (tcpseg->getUrgBit() || tcpseg->getPshBit())
00847             tcpEV << "Ignoring URG and PSH bits in SYN\n"; // TBD
00848         return TCP_E_RCV_SYN;
00849     }
00850 
00851     //"
00852     // fifth, if neither of the SYN or RST bits is set then drop the
00853     // segment and return.
00854     //"
00855     return TCP_E_IGNORE;
00856 }

TCPEventCode TCPConnection::processSegment1stThru8th ( TCPSegment tcpseg  )  [protected, virtual]

Referenced by process_RCV_SEGMENT().

00113 {
00114     //
00115     // RFC 793: first check sequence number
00116     //
00117     bool acceptable = isSegmentAcceptable(tcpseg);
00118     if (!acceptable)
00119     {
00120         //"
00121         // If an incoming segment is not acceptable, an acknowledgment
00122         // should be sent in reply (unless the RST bit is set, if so drop
00123         // the segment and return):
00124         //
00125         //  <SEQ=SND.NXT><ACK=RCV.NXT><CTL=ACK>
00126         //"
00127         if (tcpseg->getRstBit())
00128         {
00129             tcpEV << "RST with unacceptable seqNum: dropping\n";
00130         }
00131         else
00132         {
00133             tcpEV << "Segment seqNum not acceptable, sending ACK with current receive seq\n";
00134             sendAck();
00135         }
00136         return TCP_E_IGNORE;
00137     }
00138 
00139     //
00140     // RFC 793: second check the RST bit,
00141     //
00142     if (tcpseg->getRstBit())
00143     {
00144         // Note: if we come from LISTEN, processSegmentInListen() has already handled RST.
00145         switch (fsm.getState())
00146         {
00147             case TCP_S_SYN_RCVD:
00148                 //"
00149                 // If this connection was initiated with a passive OPEN (i.e.,
00150                 // came from the LISTEN state), then return this connection to
00151                 // LISTEN state and return.  The user need not be informed.  If
00152                 // this connection was initiated with an active OPEN (i.e., came
00153                 // from SYN-SENT state) then the connection was refused, signal
00154                 // the user "connection refused".  In either case, all segments
00155                 // on the retransmission queue should be removed.  And in the
00156                 // active OPEN case, enter the CLOSED state and delete the TCB,
00157                 // and return.
00158                 //"
00159                 return processRstInSynReceived(tcpseg);
00160 
00161             case TCP_S_ESTABLISHED:
00162             case TCP_S_FIN_WAIT_1:
00163             case TCP_S_FIN_WAIT_2:
00164             case TCP_S_CLOSE_WAIT:
00165                 //"
00166                 // If the RST bit is set then, any outstanding RECEIVEs and SEND
00167                 // should receive "reset" responses.  All segment queues should be
00168                 // flushed.  Users should also receive an unsolicited general
00169                 // "connection reset" signal.
00170                 //
00171                 // Enter the CLOSED state, delete the TCB, and return.
00172                 //"
00173                 tcpEV << "RST: performing connection reset, closing connection\n";
00174                 sendIndicationToApp(TCP_I_CONNECTION_RESET);
00175                 return TCP_E_RCV_RST;  // this will trigger state transition
00176 
00177             case TCP_S_CLOSING:
00178             case TCP_S_LAST_ACK:
00179             case TCP_S_TIME_WAIT:
00180                 //"
00181                 // enter the CLOSED state, delete the TCB, and return.
00182                 //"
00183                 tcpEV << "RST: closing connection\n";
00184                 if (fsm.getState()!=TCP_S_TIME_WAIT)
00185                     sendIndicationToApp(TCP_I_CLOSED); // in TIME_WAIT, we've already sent it
00186                 return TCP_E_RCV_RST; // this will trigger state transition
00187 
00188             default: ASSERT(0);
00189         }
00190     }
00191 
00192     // RFC 793: third check security and precedence
00193     // This step is ignored.
00194 
00195     //
00196     // RFC 793: fourth, check the SYN bit,
00197     //
00198     if (tcpseg->getSynBit())
00199     {
00200         //"
00201         // If the SYN is in the window it is an error, send a reset, any
00202         // outstanding RECEIVEs and SEND should receive "reset" responses,
00203         // all segment queues should be flushed, the user should also
00204         // receive an unsolicited general "connection reset" signal, enter
00205         // the CLOSED state, delete the TCB, and return.
00206         //
00207         // If the SYN is not in the window this step would not be reached
00208         // and an ack would have been sent in the first step (sequence
00209         // number check).
00210         //"
00211 
00212         ASSERT(isSegmentAcceptable(tcpseg));  // assert SYN is in the window
00213         tcpEV << "SYN is in the window: performing connection reset, closing connection\n";
00214         sendIndicationToApp(TCP_I_CONNECTION_RESET);
00215         return TCP_E_RCV_UNEXP_SYN;
00216     }
00217 
00218     //
00219     // RFC 793: fifth check the ACK field,
00220     //
00221     if (!tcpseg->getAckBit())
00222     {
00223         // if the ACK bit is off drop the segment and return
00224         tcpEV << "ACK not set, dropping segment\n";
00225         return TCP_E_IGNORE;
00226     }
00227 
00228     TCPEventCode event = TCP_E_IGNORE;
00229 
00230     if (fsm.getState()==TCP_S_SYN_RCVD)
00231     {
00232         //"
00233         // If SND.UNA =< SEG.ACK =< SND.NXT then enter ESTABLISHED state
00234         // and continue processing.
00235         //
00236         // If the segment acknowledgment is not acceptable, form a
00237         // reset segment,
00238         //
00239         //  <SEQ=SEG.ACK><CTL=RST>
00240         //
00241         // and send it.
00242         //"
00243         if (!seqLE(state->snd_una,tcpseg->getAckNo()) || !seqLE(tcpseg->getAckNo(),state->snd_nxt))
00244         {
00245             sendRst(tcpseg->getAckNo());
00246             return TCP_E_IGNORE;
00247         }
00248 
00249         // notify tcpAlgorithm and app layer
00250         tcpAlgorithm->established(false);
00251         sendEstabIndicationToApp();
00252 
00253         // This will trigger transition to ESTABLISHED. Timers and notifying
00254         // app will be taken care of in stateEntered().
00255         event = TCP_E_RCV_ACK;
00256     }
00257 
00258     uint32 old_snd_nxt = state->snd_nxt; // later we'll need to see if snd_nxt changed
00259     if (fsm.getState()==TCP_S_SYN_RCVD || fsm.getState()==TCP_S_ESTABLISHED ||
00260         fsm.getState()==TCP_S_FIN_WAIT_1 || fsm.getState()==TCP_S_FIN_WAIT_2 ||
00261         fsm.getState()==TCP_S_CLOSE_WAIT || fsm.getState()==TCP_S_CLOSING)
00262     {
00263         //
00264         // ESTABLISHED processing:
00265         //"
00266         //  If SND.UNA < SEG.ACK =< SND.NXT then, set SND.UNA <- SEG.ACK.
00267         //  Any segments on the retransmission queue which are thereby
00268         //  entirely acknowledged are removed.  Users should receive
00269         //  positive acknowledgments for buffers which have been SENT and
00270         //  fully acknowledged (i.e., SEND buffer should be returned with
00271         //  "ok" response).  If the ACK is a duplicate
00272         //  (SEG.ACK < SND.UNA), it can be ignored.  If the ACK acks
00273         //  something not yet sent (SEG.ACK > SND.NXT) then send an ACK,
00274         //  drop the segment, and return.
00275         //
00276         //  If SND.UNA < SEG.ACK =< SND.NXT, the send window should be
00277         //  updated.  If (SND.WL1 < SEG.SEQ or (SND.WL1 = SEG.SEQ and
00278         //  SND.WL2 =< SEG.ACK)), set SND.WND <- SEG.WND, set
00279         //  SND.WL1 <- SEG.SEQ, and set SND.WL2 <- SEG.ACK.
00280         //
00281         //  Note that SND.WND is an offset from SND.UNA, that SND.WL1
00282         //  records the sequence number of the last segment used to update
00283         //  SND.WND, and that SND.WL2 records the acknowledgment number of
00284         //  the last segment used to update SND.WND.  The check here
00285         //  prevents using old segments to update the window.
00286         //"
00287         bool ok = processAckInEstabEtc(tcpseg);
00288         if (!ok)
00289             return TCP_E_IGNORE;  // if acks something not yet sent, drop it
00290     }
00291 
00292     if ((fsm.getState()==TCP_S_FIN_WAIT_1 && state->fin_ack_rcvd))
00293     {
00294         //"
00295         // FIN-WAIT-1 STATE
00296         //   In addition to the processing for the ESTABLISHED state, if
00297         //   our FIN is now acknowledged then enter FIN-WAIT-2 and continue
00298         //   processing in that state.
00299         //"
00300         event = TCP_E_RCV_ACK;  // will trigger transition to FIN-WAIT-2
00301     }
00302 
00303     if (fsm.getState()==TCP_S_FIN_WAIT_2)
00304     {
00305         //"
00306         // FIN-WAIT-2 STATE
00307         //  In addition to the processing for the ESTABLISHED state, if
00308         //  the retransmission queue is empty, the user's CLOSE can be
00309         //  acknowledged ("ok") but do not delete the TCB.
00310         //"
00311         // nothing to do here (in our model, used commands don't need to be
00312         // acknowledged)
00313     }
00314 
00315     if (fsm.getState()==TCP_S_CLOSING)
00316     {
00317         //"
00318         // In addition to the processing for the ESTABLISHED state, if
00319         // the ACK acknowledges our FIN then enter the TIME-WAIT state,
00320         // otherwise ignore the segment.
00321         //"
00322         if (state->fin_ack_rcvd)
00323         {
00324             tcpEV << "Our FIN acked -- can go to TIME_WAIT now\n";
00325             event = TCP_E_RCV_ACK;  // will trigger transition to TIME-WAIT
00326             scheduleTimeout(the2MSLTimer, TCP_TIMEOUT_2MSL);  // start timer
00327 
00328             // we're entering TIME_WAIT, so we can signal CLOSED the user
00329             // (the only thing left to do is wait until the 2MSL timer expires)
00330             sendIndicationToApp(TCP_I_CLOSED);
00331         }
00332     }
00333 
00334     if (fsm.getState()==TCP_S_LAST_ACK)
00335     {
00336         //"
00337         // The only thing that can arrive in this state is an
00338         // acknowledgment of our FIN.  If our FIN is now acknowledged,
00339         // delete the TCB, enter the CLOSED state, and return.
00340         //"
00341         if (state->send_fin && tcpseg->getAckNo()==state->snd_fin_seq+1)
00342         {
00343             tcpEV << "Last ACK arrived\n";
00344             sendIndicationToApp(TCP_I_CLOSED);
00345             return TCP_E_RCV_ACK; // will trigger transition to CLOSED
00346         }
00347     }
00348 
00349     if (fsm.getState()==TCP_S_TIME_WAIT)
00350     {
00351         //"
00352         // The only thing that can arrive in this state is a
00353         // retransmission of the remote FIN.  Acknowledge it, and restart
00354         // the 2 MSL timeout.
00355         //"
00356         // And we are staying in the TIME_WAIT state.
00357         //
00358         sendAck();
00359         cancelEvent(the2MSLTimer);
00360         scheduleTimeout(the2MSLTimer, TCP_TIMEOUT_2MSL);
00361     }
00362 
00363     //
00364     // RFC 793: sixth, check the URG bit,
00365     //
00366     if (tcpseg->getUrgBit() && (fsm.getState()==TCP_S_ESTABLISHED || fsm.getState()==TCP_S_FIN_WAIT_1 ||
00367         fsm.getState()==TCP_S_FIN_WAIT_2))
00368     {
00369         //"
00370         // If the URG bit is set, RCV.UP <- max(RCV.UP,SEG.UP), and signal
00371         // the user that the remote side has urgent data if the urgent
00372         // pointer (RCV.UP) is in advance of the data consumed.  If the
00373         // user has already been signaled (or is still in the "urgent
00374         // mode") for this continuous sequence of urgent data, do not
00375         // signal the user again.
00376         //"
00377 
00378         // TBD: URG currently not supported
00379     }
00380 
00381     //
00382     // RFC 793: seventh, process the segment text,
00383     //
00384     uint32 old_rcv_nxt = state->rcv_nxt; // if rcv_nxt changes, we need to send/schedule an ACK
00385     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)
00386     {
00387         //"
00388         // Once in the ESTABLISHED state, it is possible to deliver segment
00389         // text to user RECEIVE buffers.  Text from segments can be moved
00390         // into buffers until either the buffer is full or the segment is
00391         // empty.  If the segment empties and carries an PUSH flag, then
00392         // the user is informed, when the buffer is returned, that a PUSH
00393         // has been received.
00394         //
00395         // When the TCP takes responsibility for delivering the data to the
00396         // user it must also acknowledge the receipt of the data.
00397         //
00398         // Once the TCP takes responsibility for the data it advances
00399         // RCV.NXT over the data accepted, and adjusts RCV.WND as
00400         // apporopriate to the current buffer availability.  The total of
00401         // RCV.NXT and RCV.WND should not be reduced.
00402         //
00403         // Please note the window management suggestions in section 3.7.
00404         //
00405         // Send an acknowledgment of the form:
00406         //
00407         //   <SEQ=SND.NXT><ACK=RCV.NXT><CTL=ACK>
00408         //
00409         // This acknowledgment should be piggybacked on a segment being
00410         // transmitted if possible without incurring undue delay.
00411         //"
00412         if (tcpseg->getPayloadLength()>0)
00413         {
00414             tcpEV2 << "Processing segment text in a data transfer state\n";
00415 
00416             // insert into receive buffers. If this segment is contiguous with
00417             // previously received ones (seqNo==rcv_nxt), rcv_nxt can be increased;
00418             // otherwise it stays the same but the data must be cached nevertheless
00419             // (to avoid "Failure to retain above-sequence data" problem, RFC 2525
00420             // section 2.5).
00421             uint32 old_rcv_nxt = state->rcv_nxt;
00422             state->rcv_nxt = receiveQueue->insertBytesFromSegment(tcpseg);
00423 
00424             // out-of-order segment?
00425             if (old_rcv_nxt==state->rcv_nxt)
00426             {
00427                 // we'll probably want to send an ACK here
00428                 tcpAlgorithm->receivedOutOfOrderSegment();
00429             }
00430             else
00431             {
00432                 // forward data to app
00433                 //
00434                 // FIXME observe PSH bit
00435                 //
00436                 // FIXME we should implement socket READ command, and pass up only
00437                 // as many bytes as requested. rcv_wnd should be decreased
00438                 // accordingly! (right now we *always* advertise win=16384,
00439                 // that is, there's practically no receiver-imposed flow control!)
00440                 //
00441                 cPacket *msg;
00442                 while ((msg=receiveQueue->extractBytesUpTo(state->rcv_nxt))!=NULL)
00443                 {
00444                     msg->setKind(TCP_I_DATA);  // TBD currently we never send TCP_I_URGENT_DATA
00445                     TCPCommand *cmd = new TCPCommand();
00446                     cmd->setConnId(connId);
00447                     msg->setControlInfo(cmd);
00448                     sendToApp(msg);
00449                 }
00450 
00451                 // if this segment "filled the gap" until the previously arrived segment
00452                 // that carried a FIN (i.e.rcv_nxt==rcv_fin_seq), we have to advance
00453                 // rcv_nxt over the FIN.
00454                 if (state->fin_rcvd && state->rcv_nxt==state->rcv_fin_seq)
00455                 {
00456                     tcpEV << "All segments arrived up to the FIN segment, advancing rcv_nxt over the FIN\n";
00457                     state->rcv_nxt = state->rcv_fin_seq+1;
00458                     sendIndicationToApp(TCP_I_PEER_CLOSED);
00459                 }
00460             }
00461         }
00462     }
00463 
00464     //
00465     // RFC 793: eighth, check the FIN bit,
00466     //
00467     if (tcpseg->getFinBit())
00468     {
00469         //"
00470         // If the FIN bit is set, signal the user "connection closing" and
00471         // return any pending RECEIVEs with same message, advance RCV.NXT
00472         // over the FIN, and send an acknowledgment for the FIN.  Note that
00473         // FIN implies PUSH for any segment text not yet delivered to the
00474         // user.
00475         //"
00476 
00477         // Note: seems like RFC 793 is not entirely correct here: if the
00478         // segment is "above sequence" (ie. RCV.NXT < SEG.SEQ), we cannot
00479         // advance RCV.NXT over the FIN. Instead we remember this sequence
00480         // number and do it later.
00481         uint32 fin_seq = (uint32)tcpseg->getSequenceNo() + (uint32)tcpseg->getPayloadLength();
00482         if (state->rcv_nxt==fin_seq)
00483         {
00484             // advance rcv_nxt over FIN now
00485             tcpEV << "FIN arrived, advancing rcv_nxt over the FIN\n";
00486             state->rcv_nxt++;
00487             sendIndicationToApp(TCP_I_PEER_CLOSED);
00488         }
00489         else
00490         {
00491             // we'll have to do it later (when an arriving segment "fills the gap")
00492             tcpEV << "FIN segment above sequence, storing sequence number of FIN\n";
00493             state->fin_rcvd = true;
00494             state->rcv_fin_seq = fin_seq;
00495         }
00496 
00497         // TBD do PUSH stuff
00498 
00499         // state transitions will be done in the state machine, here we just set
00500         // the proper event code (TCP_E_RCV_FIN or TCP_E_RCV_FIN_ACK)
00501         event = TCP_E_RCV_FIN;
00502         switch (fsm.getState())
00503         {
00504             case TCP_S_FIN_WAIT_1:
00505                 if (state->fin_ack_rcvd)
00506                 {
00507                     event = TCP_E_RCV_FIN_ACK;
00508                     // start the time-wait timer, turn off the other timers
00509                     cancelEvent(finWait2Timer);
00510                     scheduleTimeout(the2MSLTimer, TCP_TIMEOUT_2MSL);
00511 
00512                     // we're entering TIME_WAIT, so we can signal CLOSED the user
00513                     // (the only thing left to do is wait until the 2MSL timer expires)
00514                     sendIndicationToApp(TCP_I_CLOSED);
00515                 }
00516                 break;
00517             case TCP_S_FIN_WAIT_2:
00518                 // Start the time-wait timer, turn off the other timers.
00519                 cancelEvent(finWait2Timer);
00520                 scheduleTimeout(the2MSLTimer, TCP_TIMEOUT_2MSL);
00521 
00522                 // we're entering TIME_WAIT, so we can signal CLOSED the user
00523                 // (the only thing left to do is wait until the 2MSL timer expires)
00524                 sendIndicationToApp(TCP_I_CLOSED);
00525                 break;
00526             case TCP_S_TIME_WAIT:
00527                 // Restart the 2 MSL time-wait timeout.
00528                 cancelEvent(the2MSLTimer);
00529                 scheduleTimeout(the2MSLTimer, TCP_TIMEOUT_2MSL);
00530                 break;
00531         }
00532     }
00533 
00534     if (old_rcv_nxt!=state->rcv_nxt)
00535     {
00536         // if rcv_nxt changed, either because we received segment text or we
00537         // received a FIN that needs to be acked (or both), we need to send or
00538         // schedule an ACK.
00539 
00540         // tcpAlgorithm decides when and how to do ACKs
00541         tcpAlgorithm->receiveSeqChanged();
00542     }
00543 
00544     if ((fsm.getState()==TCP_S_ESTABLISHED || fsm.getState()==TCP_S_SYN_RCVD) &&
00545         state->send_fin && state->snd_nxt==state->snd_fin_seq+1)
00546     {
00547         // if the user issued the CLOSE command a long time ago and we've just
00548         // managed to send off FIN, we simulate a CLOSE command now (we had to
00549         // defer it at that time because we still had data in the send queue.)
00550         // This CLOSE will take us into the FIN_WAIT_1 state.
00551         tcpEV << "Now we can do the CLOSE which was deferred a while ago\n";
00552         event = TCP_E_CLOSE;
00553     }
00554 
00555     if (fsm.getState()==TCP_S_CLOSE_WAIT && state->send_fin &&
00556         state->snd_nxt==state->snd_fin_seq+1 && old_snd_nxt!=state->snd_nxt)
00557     {
00558         // if we're in CLOSE_WAIT and we just got to sent our long-pending FIN,
00559         // we simulate a CLOSE command now (we had to defer it at that time because
00560         // we still had data in the send queue.) This CLOSE will take us into the
00561         // LAST_ACK state.
00562         tcpEV << "Now we can do the CLOSE which was deferred a while ago\n";
00563         event = TCP_E_CLOSE;
00564     }
00565 
00566     return event;
00567 }

TCPEventCode TCPConnection::processRstInSynReceived ( TCPSegment tcpseg  )  [protected, virtual]

Referenced by processSegment1stThru8th().

00859 {
00860     tcpEV2 << "Processing RST in SYN_RCVD\n";
00861 
00862     //"
00863     // If this connection was initiated with a passive OPEN (i.e.,
00864     // came from the LISTEN state), then return this connection to
00865     // LISTEN state and return.  The user need not be informed.  If
00866     // this connection was initiated with an active OPEN (i.e., came
00867     // from SYN-SENT state) then the connection was refused, signal
00868     // the user "connection refused".  In either case, all segments
00869     // on the retransmission queue should be removed.  And in the
00870     // active OPEN case, enter the CLOSED state and delete the TCB,
00871     // and return.
00872     //"
00873 
00874     sendQueue->discardUpTo(sendQueue->getBufferEndSeq()); // flush send queue
00875 
00876     if (state->active)
00877     {
00878         // signal "connection refused"
00879         sendIndicationToApp(TCP_I_CONNECTION_REFUSED);
00880     }
00881 
00882     // on RCV_RST, FSM will go either to LISTEN or to CLOSED, depending on state->active
00883     return TCP_E_RCV_RST;
00884 }

bool TCPConnection::processAckInEstabEtc ( TCPSegment tcpseg  )  [protected, virtual]

Referenced by processSegment1stThru8th().

00887 {
00888     tcpEV2 << "Processing ACK in a data transfer state\n";
00889 
00890     //
00891     //"
00892     //  If SND.UNA < SEG.ACK =< SND.NXT then, set SND.UNA <- SEG.ACK.
00893     //  Any segments on the retransmission queue which are thereby
00894     //  entirely acknowledged are removed.  Users should receive
00895     //  positive acknowledgments for buffers which have been SENT and
00896     //  fully acknowledged (i.e., SEND buffer should be returned with
00897     //  "ok" response).  If the ACK is a duplicate
00898     //  (SEG.ACK < SND.UNA), it can be ignored.  If the ACK acks
00899     //  something not yet sent (SEG.ACK > SND.NXT) then send an ACK,
00900     //  drop the segment, and return.
00901     //
00902     //  If SND.UNA < SEG.ACK =< SND.NXT, the send window should be
00903     //  updated.  If (SND.WL1 < SEG.SEQ or (SND.WL1 = SEG.SEQ and
00904     //  SND.WL2 =< SEG.ACK)), set SND.WND <- SEG.WND, set
00905     //  SND.WL1 <- SEG.SEQ, and set SND.WL2 <- SEG.ACK.
00906     //
00907     //  Note that SND.WND is an offset from SND.UNA, that SND.WL1
00908     //  records the sequence number of the last segment used to update
00909     //  SND.WND, and that SND.WL2 records the acknowledgment number of
00910     //  the last segment used to update SND.WND.  The check here
00911     //  prevents using old segments to update the window.
00912     //"
00913     // Note: should use SND.MAX instead of SND.NXT in above checks
00914     //
00915     if (seqGE(state->snd_una, tcpseg->getAckNo()))
00916     {
00917         //
00918         // duplicate ACK? A received TCP segment is a duplicate ACK if all of
00919         // the following apply:
00920         //    (1) snd_una==ackNo
00921         //    (2) segment contains no data
00922         //    (3) there's unacked data (snd_una!=snd_max)
00923         //
00924         // Note: ssfnet uses additional constraint "window is the same as last
00925         // received (not an update)" -- we don't do that because window updates
00926         // are ignored anyway if neither seqNo nor ackNo has changed.
00927         //
00928         if (state->snd_una==tcpseg->getAckNo() && tcpseg->getPayloadLength()==0 &&
00929             state->snd_una!=state->snd_max)
00930         {
00931             state->dupacks++;
00932             tcpAlgorithm->receivedDuplicateAck();
00933         }
00934         else
00935         {
00936             // if doesn't qualify as duplicate ACK, just ignore it.
00937             if (tcpseg->getPayloadLength()==0)
00938             {
00939                 if (state->snd_una!=tcpseg->getAckNo())
00940                     tcpEV << "Old ACK: ackNo<snd_una\n";
00941                 else if (state->snd_una==state->snd_max)
00942                     tcpEV << "ACK looks duplicate but we have currently no unacked data (snd_una==snd_max)\n";
00943             }
00944 
00945             // reset counter
00946             state->dupacks = 0;
00947         }
00948     }
00949     else if (seqLE(tcpseg->getAckNo(), state->snd_max))
00950     {
00951         // ack in window.
00952         uint32 old_snd_una = state->snd_una;
00953         state->snd_una = tcpseg->getAckNo();
00954         if (unackedVector) unackedVector->record(state->snd_max - state->snd_una);
00955 
00956         // after retransmitting a lost segment, we may get an ack well ahead of snd_nxt
00957         if (seqLess(state->snd_nxt, state->snd_una))
00958             state->snd_nxt = state->snd_una;
00959 
00960         uint32 discardUpToSeq = state->snd_una;
00961 
00962         // our FIN acked?
00963         if (state->send_fin && tcpseg->getAckNo()==state->snd_fin_seq+1)
00964         {
00965             // set flag that our FIN has been acked
00966             tcpEV << "ACK acks our FIN\n";
00967             state->fin_ack_rcvd = true;
00968             discardUpToSeq--; // the FIN sequence number is not real data
00969         }
00970 
00971         // acked data no longer needed in send queue
00972         sendQueue->discardUpTo(discardUpToSeq);
00973 
00974         if (seqLess(state->snd_wl1, tcpseg->getSequenceNo()) ||
00975             (state->snd_wl1==tcpseg->getSequenceNo() && seqLE(state->snd_wl2, tcpseg->getAckNo())))
00976         {
00977             // send window should be updated
00978             tcpEV << "Updating send window from segment: new wnd=" << tcpseg->getWindow() << "\n";
00979             state->snd_wnd = tcpseg->getWindow();
00980             state->snd_wl1 = tcpseg->getSequenceNo();
00981             state->snd_wl2 = tcpseg->getAckNo();
00982             if (sndWndVector) sndWndVector->record(state->snd_wnd);
00983         }
00984 
00985         // notify
00986         tcpAlgorithm->receivedDataAck(old_snd_una);
00987 
00988         // in the receivedDataAck we need the old value
00989         state->dupacks = 0;
00990     }
00991     else
00992     {
00993         ASSERT(seqGreater(tcpseg->getAckNo(), state->snd_max)); // from if-ladder
00994 
00995         // send an ACK, drop the segment, and return.
00996         tcpAlgorithm->receivedAckForDataNotYetSent(tcpseg->getAckNo());
00997         state->dupacks = 0;
00998         return false;  // means "drop"
00999     }
01000     return true;
01001 }

void TCPConnection::process_TIMEOUT_2MSL (  )  [protected, virtual]

Referenced by processTimer().

01026 {
01027     //"
01028     // If the time-wait timeout expires on a connection delete the TCB,
01029     // enter the CLOSED state and return.
01030     //"
01031     switch(fsm.getState())
01032     {
01033         case TCP_S_TIME_WAIT:
01034             // Nothing to do here. The TIMEOUT_2MSL event will automatically take
01035             // the connection to CLOSED. We already notified the user
01036             // (TCP_I_CLOSED) when we entered the TIME_WAIT state from CLOSING,
01037             // FIN_WAIT_1 or FIN_WAIT_2.
01038             break;
01039         default:
01040             // We should not receive this timeout in this state.
01041             opp_error("Internal error: received time-wait (2MSL) timeout in state %s", stateName(fsm.getState()));
01042     }
01043 
01044 }

void TCPConnection::process_TIMEOUT_CONN_ESTAB (  )  [protected, virtual]

Referenced by processTimer().

01006 {
01007     switch(fsm.getState())
01008     {
01009         case TCP_S_SYN_RCVD:
01010         case TCP_S_SYN_SENT:
01011             // Nothing to do here. TIMEOUT_CONN_ESTAB event will automatically
01012             // take the connection to LISTEN or CLOSED, and cancel SYN-REXMIT timer.
01013             if (state->active)
01014             {
01015                 // notify user if we're on the active side
01016                 sendIndicationToApp(TCP_I_TIMED_OUT);
01017             }
01018             break;
01019         default:
01020             // We should not receive this timeout in this state.
01021             opp_error("Internal error: received CONN_ESTAB timeout in state %s", stateName(fsm.getState()));
01022     }
01023 }

void TCPConnection::process_TIMEOUT_FIN_WAIT_2 (  )  [protected, virtual]

Referenced by processTimer().

01047 {
01048     switch(fsm.getState())
01049     {
01050         case TCP_S_FIN_WAIT_2:
01051             // Nothing to do here. The TIMEOUT_FIN_WAIT_2 event will automatically take
01052             // the connection to CLOSED.
01053             sendIndicationToApp(TCP_I_CLOSED);
01054             break;
01055         default:
01056             // We should not receive this timeout in this state.
01057             opp_error("Internal error: received FIN_WAIT_2 timeout in state %s", stateName(fsm.getState()));
01058     }
01059 }

void TCPConnection::process_TIMEOUT_SYN_REXMIT ( TCPEventCode event  )  [protected, virtual]

Referenced by processTimer().

01072 {
01073     if (++state->syn_rexmit_count>MAX_SYN_REXMIT_COUNT)
01074     {
01075         tcpEV << "Retransmission count during connection setup exceeds " << MAX_SYN_REXMIT_COUNT << ", giving up\n";
01076         // Note ABORT will take the connection to closed, and cancel CONN-ESTAB timer as well
01077         event = TCP_E_ABORT;
01078         return;
01079     }
01080 
01081     tcpEV << "Performing retransmission #" << state->syn_rexmit_count << "\n";
01082 
01083     // resend what's needed
01084     switch(fsm.getState())
01085     {
01086         case TCP_S_SYN_SENT: sendSyn(); break;
01087         case TCP_S_SYN_RCVD: sendSynAck(); break;
01088         default:  opp_error("Internal error: SYN-REXMIT timer expired while in state %s", stateName(fsm.getState()));
01089     }
01090 
01091     // reschedule timer
01092     state->syn_rexmit_timeout *= 2;
01093 
01094     if (state->syn_rexmit_timeout > TCP_TIMEOUT_SYN_REXMIT_MAX)
01095         state->syn_rexmit_timeout = TCP_TIMEOUT_SYN_REXMIT_MAX;
01096     scheduleTimeout(synRexmitTimer, state->syn_rexmit_timeout);
01097 }

TCPConnection * TCPConnection::cloneListeningConnection (  )  [protected, virtual]

Utility: clone a listening connection. Used for forking.

Referenced by processSegmentInListen().

00138 {
00139     TCPConnection *conn = new TCPConnection(tcpMain,appGateIndex,connId);
00140 
00141     // following code to be kept consistent with initConnection()
00142     const char *sendQueueClass = sendQueue->getClassName();
00143     conn->sendQueue = check_and_cast<TCPSendQueue *>(createOne(sendQueueClass));
00144     conn->sendQueue->setConnection(conn);
00145 
00146     const char *receiveQueueClass = receiveQueue->getClassName();
00147     conn->receiveQueue = check_and_cast<TCPReceiveQueue *>(createOne(receiveQueueClass));
00148     conn->receiveQueue->setConnection(conn);
00149 
00150     const char *tcpAlgorithmClass = tcpAlgorithm->getClassName();
00151     conn->tcpAlgorithm = check_and_cast<TCPAlgorithm *>(createOne(tcpAlgorithmClass));
00152     conn->tcpAlgorithm->setConnection(conn);
00153 
00154     conn->state = conn->tcpAlgorithm->getStateVariables();
00155     configureStateVariables();
00156     conn->tcpAlgorithm->initialize();
00157 
00158     // put it into LISTEN, with our localAddr/localPort
00159     conn->state->active = false;
00160     conn->state->fork = true;
00161     conn->localAddr = localAddr;
00162     conn->localPort = localPort;
00163     FSM_Goto(conn->fsm, TCP_S_LISTEN);
00164 
00165     return conn;
00166 }

void TCPConnection::initConnection ( TCPOpenCommand *  openCmd  )  [protected, virtual]

Utility: creates send/receive queues and tcpAlgorithm

Referenced by process_OPEN_ACTIVE(), and process_OPEN_PASSIVE().

00283 {
00284     // create send/receive queues
00285     const char *sendQueueClass = openCmd->getSendQueueClass();
00286     if (!sendQueueClass || !sendQueueClass[0])
00287         sendQueueClass = tcpMain->par("sendQueueClass");
00288     sendQueue = check_and_cast<TCPSendQueue *>(createOne(sendQueueClass));
00289     sendQueue->setConnection(this);
00290 
00291     const char *receiveQueueClass = openCmd->getReceiveQueueClass();
00292     if (!receiveQueueClass || !receiveQueueClass[0])
00293         receiveQueueClass = tcpMain->par("receiveQueueClass");
00294     receiveQueue = check_and_cast<TCPReceiveQueue *>(createOne(receiveQueueClass));
00295     receiveQueue->setConnection(this);
00296 
00297     // create algorithm
00298     const char *tcpAlgorithmClass = openCmd->getTcpAlgorithmClass();
00299     if (!tcpAlgorithmClass || !tcpAlgorithmClass[0])
00300         tcpAlgorithmClass = tcpMain->par("tcpAlgorithmClass");
00301     tcpAlgorithm = check_and_cast<TCPAlgorithm *>(createOne(tcpAlgorithmClass));
00302     tcpAlgorithm->setConnection(this);
00303 
00304     // create state block
00305     state = tcpAlgorithm->getStateVariables();
00306     configureStateVariables();
00307     tcpAlgorithm->initialize();
00308 }

void TCPConnection::configureStateVariables (  )  [protected, virtual]

Utility: set snd_mss and rcv_wnd in newly created state variables block

Referenced by cloneListeningConnection(), and initConnection().

00311 {
00312     state->snd_mss = tcpMain->par("mss").longValue(); // TODO: mss=-1 should mean autodetect
00313     state->rcv_wnd = tcpMain->par("advertisedWindow").longValue();
00314 }

void TCPConnection::selectInitialSeqNum (  )  [protected, virtual]

Utility: generates ISS and initializes corresponding state variables

Referenced by process_OPEN_ACTIVE(), process_SEND(), and processSegmentInListen().

00317 {
00318     // set the initial send sequence number
00319     state->iss = (unsigned long)fmod(SIMTIME_DBL(simTime())*250000.0, 1.0+(double)(unsigned)0xffffffffUL) & 0xffffffffUL;
00320 
00321     state->snd_una = state->snd_nxt = state->snd_max = state->iss;
00322 
00323     sendQueue->init(state->iss+1); // +1 is for SYN
00324 }

bool TCPConnection::isSegmentAcceptable ( TCPSegment tcpseg  )  [protected, virtual]

Utility: check if segment is acceptable (all bytes are in receive window)

Referenced by processSegment1stThru8th().

00327 {
00328     // check that segment entirely falls in receive window
00329     //FIXME probably not this simple, see old code segAccept() below...
00330     return seqGE(tcpseg->getSequenceNo(),state->rcv_nxt) &&
00331            seqLE(tcpseg->getSequenceNo()+tcpseg->getPayloadLength(),state->rcv_nxt+state->rcv_wnd);
00332 }

void TCPConnection::sendSyn (  )  [protected, virtual]

Utility: send SYN

Referenced by process_OPEN_ACTIVE(), process_SEND(), and process_TIMEOUT_SYN_REXMIT().

00335 {
00336     if (remoteAddr.isUnspecified() || remotePort==-1)
00337         opp_error("Error processing command OPEN_ACTIVE: foreign socket unspecified");
00338     if (localPort==-1)
00339         opp_error("Error processing command OPEN_ACTIVE: local port unspecified");
00340 
00341     // create segment
00342     TCPSegment *tcpseg = createTCPSegment("SYN");
00343     tcpseg->setSequenceNo(state->iss);
00344     tcpseg->setSynBit(true);
00345     tcpseg->setWindow(state->rcv_wnd);
00346 
00347     state->snd_max = state->snd_nxt = state->iss+1;
00348 
00349     // send it
00350     sendToIP(tcpseg);
00351 }

void TCPConnection::sendSynAck (  )  [protected, virtual]

Utility: send SYN+ACK

Referenced by process_TIMEOUT_SYN_REXMIT(), processSegmentInListen(), and processSegmentInSynSent().

00354 {
00355     // create segment
00356     TCPSegment *tcpseg = createTCPSegment("SYN+ACK");
00357     tcpseg->setSequenceNo(state->iss);
00358     tcpseg->setAckNo(state->rcv_nxt);
00359     tcpseg->setSynBit(true);
00360     tcpseg->setAckBit(true);
00361     tcpseg->setWindow(state->rcv_wnd);
00362 
00363     state->snd_max = state->snd_nxt = state->iss+1;
00364 
00365     // send it
00366     sendToIP(tcpseg);
00367 }

void TCPConnection::sendAck (  )  [virtual]

Utility: send ACK

Referenced by TCPBaseAlg::established(), DumbTCP::established(), TCPBaseAlg::processDelayedAckTimer(), processSegment1stThru8th(), TCPBaseAlg::receivedAckForDataNotYetSent(), DumbTCP::receivedAckForDataNotYetSent(), TCPBaseAlg::receivedOutOfOrderSegment(), DumbTCP::receivedOutOfOrderSegment(), TCPBaseAlg::receiveSeqChanged(), and DumbTCP::receiveSeqChanged().

00405 {
00406     TCPSegment *tcpseg = createTCPSegment("ACK");
00407 
00408     tcpseg->setAckBit(true);
00409     tcpseg->setSequenceNo(state->snd_nxt);
00410     tcpseg->setAckNo(state->rcv_nxt);
00411     tcpseg->setWindow(state->rcv_wnd);
00412 
00413     // send it
00414     sendToIP(tcpseg);
00415 
00416     // notify
00417     tcpAlgorithm->ackSent();
00418 }

bool TCPConnection::sendData ( bool  fullSegmentsOnly,
int  congestionWindow = -1 
) [virtual]

Utility: Send data from sendQueue, at most congestionWindow (-1 means no limit). FIXME adjust comment!!! If fullSegmentsOnly is set, don't send segments smaller than MSS (needed for Nagle). Returns true if some data was actually sent.

Referenced by DumbTCP::established(), DumbTCP::receivedDataAck(), DumbTCP::sendCommandInvoked(), and TCPBaseAlg::sendData().

00463 {
00464     // we'll start sending from snd_max
00465     state->snd_nxt = state->snd_max;
00466 
00467     // check how many bytes we have
00468     ulong buffered = sendQueue->getBytesAvailable(state->snd_nxt);
00469     if (buffered==0)
00470         return false;
00471 
00472     // maxWindow is smaller of (snd_wnd, congestionWindow)
00473     long maxWindow = state->snd_wnd;
00474     if (congestionWindow>=0 && maxWindow > congestionWindow)
00475         maxWindow = congestionWindow;
00476 
00477     // effectiveWindow: number of bytes we're allowed to send now
00478     long effectiveWin = maxWindow - (state->snd_nxt - state->snd_una);
00479     if (effectiveWin <= 0)
00480     {
00481         tcpEV << "Effective window is zero (advertised window " << state->snd_wnd <<
00482                  ", congestion window " << congestionWindow << "), cannot send.\n";
00483         return false;
00484     }
00485 
00486     ulong bytesToSend = effectiveWin;
00487 
00488     if (bytesToSend > buffered)
00489         bytesToSend = buffered;
00490 
00491     if (fullSegmentsOnly && bytesToSend < state->snd_mss)
00492     {
00493         tcpEV << "Cannot send, not enough data for a full segment (MSS=" << state->snd_mss
00494               << ", in buffer " << buffered << ")\n";
00495         return false;
00496     }
00497 
00498     // start sending 'bytesToSend' bytes
00499     tcpEV << "Will send " << bytesToSend << " bytes (effectiveWindow " << effectiveWin
00500           << ", in buffer " << buffered << " bytes)\n";
00501 
00502     uint32 old_snd_nxt = state->snd_nxt;
00503     ASSERT(bytesToSend>0);
00504 
00505 #ifdef TCP_SENDFRAGMENTS  /* normally undefined */
00506     // make agressive use of the window until the last byte
00507     while (bytesToSend>0)
00508     {
00509         ulong bytes = std::min(bytesToSend, state->snd_mss);
00510         sendSegment(bytes);
00511         bytesToSend -= bytes;
00512     }
00513 #else
00514     // send <MSS segments only if it's the only segment we can send now
00515     // FIXME this should probably obey Nagle's alg -- to be checked
00516     if (bytesToSend <= state->snd_mss)
00517     {
00518         sendSegment(bytesToSend);
00519     }
00520     else
00521     {
00522         // send whole segments only
00523         while (bytesToSend>=state->snd_mss)
00524         {
00525             sendSegment(state->snd_mss);
00526             bytesToSend -= state->snd_mss;
00527         }
00528         if (bytesToSend>0)
00529            tcpEV << bytesToSend << " bytes of space left in effectiveWindow\n";
00530     }
00531 #endif
00532 
00533     // remember highest seq sent (snd_nxt may be set back on retransmission,
00534     // but we'll need snd_max to check validity of ACKs -- they must ack
00535     // something we really sent)
00536     state->snd_max = state->snd_nxt;
00537     if (unackedVector) unackedVector->record(state->snd_max - state->snd_una);
00538 
00539     // notify (once is enough)
00540     tcpAlgorithm->ackSent();
00541     tcpAlgorithm->dataSent(old_snd_nxt);
00542 
00543     return true;
00544 }

bool TCPConnection::sendProbe (  )  [virtual]

Utility: sends 1 bytes as "probe", called by the "persist" mechanism

Referenced by TCPBaseAlg::processPersistTimer().

00547 {
00548     // we'll start sending from snd_max
00549     state->snd_nxt = state->snd_max;
00550 
00551     // check we have 1 byte to send
00552     if (sendQueue->getBytesAvailable(state->snd_nxt)==0)
00553     {
00554         tcpEV << "Cannot send probe because send buffer is empty\n";
00555         return false;
00556     }
00557 
00558     uint32 old_snd_nxt = state->snd_nxt;
00559 
00560     tcpEV << "Sending 1 byte as probe, with seq=" << state->snd_nxt << "\n";
00561     sendSegment(1);
00562 
00563     // remember highest seq sent (snd_nxt may be set back on retransmission,
00564     // but we'll need snd_max to check validity of ACKs -- they must ack
00565     // something we really sent)
00566     state->snd_max = state->snd_nxt;
00567     if (unackedVector) unackedVector->record(state->snd_max - state->snd_una);
00568 
00569     // notify
00570     tcpAlgorithm->ackSent();
00571     tcpAlgorithm->dataSent(old_snd_nxt);
00572 
00573     return true;
00574 }

void TCPConnection::retransmitOneSegment (  )  [virtual]

Utility: retransmit one segment from snd_una

Referenced by TCPTahoe::processRexmitTimer(), TCPNoCongestionControl::processRexmitTimer(), TCPTahoe::receivedDuplicateAck(), and TCPReno::receivedDuplicateAck().

00577 {
00578     // retransmit one segment at snd_una, and set snd_nxt accordingly
00579     state->snd_nxt = state->snd_una;
00580 
00581     ulong bytes = std::min(state->snd_mss, state->snd_max - state->snd_nxt);
00582     ASSERT(bytes!=0);
00583 
00584     sendSegment(bytes);
00585 
00586     // notify
00587     tcpAlgorithm->ackSent();
00588 }

void TCPConnection::retransmitData (  )  [virtual]

Utility: retransmit all from snd_una to snd_max

Referenced by TCPReno::processRexmitTimer(), and DumbTCP::processTimer().

00591 {
00592     // retransmit everything from snd_una
00593     state->snd_nxt = state->snd_una;
00594 
00595     ulong bytesToSend = state->snd_max - state->snd_nxt;
00596     ASSERT(bytesToSend!=0);
00597 
00598     while (bytesToSend>0)
00599     {
00600         ulong bytes = std::min(bytesToSend, (ulong)state->snd_mss);
00601         bytes = std::min(bytes, sendQueue->getBytesAvailable(state->snd_nxt));
00602         sendSegment(bytes);
00603         // Do not send packets after the FIN.
00604         // fixes bug that occurs in examples/inet/bulktransfer at event #64043  T=13.861159213744
00605         if (state->send_fin && state->snd_nxt==state->snd_fin_seq+1)
00606             break;
00607         bytesToSend -= bytes;
00608     }
00609 }

void TCPConnection::sendRst ( uint32  seqNo  )  [virtual]

void TCPConnection::sendRst ( uint32  seq,
IPvXAddress  src,
IPvXAddress  dest,
int  srcPort,
int  destPort 
) [virtual]

Utility: sends RST; does not use connection state

00375 {
00376     TCPSegment *tcpseg = createTCPSegment("RST");
00377 
00378     tcpseg->setSrcPort(srcPort);
00379     tcpseg->setDestPort(destPort);
00380 
00381     tcpseg->setRstBit(true);
00382     tcpseg->setSequenceNo(seq);
00383 
00384     // send it
00385     sendToIP(tcpseg, src, dest);
00386 }

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

Referenced by segmentArrivalWhileClosed().

00389 {
00390     TCPSegment *tcpseg = createTCPSegment("RST+ACK");
00391 
00392     tcpseg->setSrcPort(srcPort);
00393     tcpseg->setDestPort(destPort);
00394 
00395     tcpseg->setRstBit(true);
00396     tcpseg->setAckBit(true);
00397     tcpseg->setSequenceNo(seq);
00398     tcpseg->setAckNo(ack);
00399 
00400     // send it
00401     sendToIP(tcpseg, src, dest);
00402 }

void TCPConnection::sendFin (  )  [virtual]

Utility: sends FIN

Referenced by process_CLOSE().

00421 {
00422     TCPSegment *tcpseg = createTCPSegment("FIN");
00423 
00424     // Note: ACK bit *must* be set for both FIN and FIN+ACK. What makes
00425     // the difference for FIN+ACK is that its ackNo acks the remote TCP's FIN.
00426     tcpseg->setFinBit(true);
00427     tcpseg->setAckBit(true);
00428     tcpseg->setAckNo(state->rcv_nxt);
00429     tcpseg->setSequenceNo(state->snd_nxt);
00430     tcpseg->setWindow(state->rcv_wnd);
00431 
00432     // send it
00433     sendToIP(tcpseg);
00434 
00435     // notify
00436     tcpAlgorithm->ackSent();
00437 }

void TCPConnection::sendSegment ( int  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.

Referenced by retransmitData(), retransmitOneSegment(), sendData(), and sendProbe().

00440 {
00441     // send one segment of 'bytes' bytes from snd_nxt, and advance snd_nxt
00442     TCPSegment *tcpseg = sendQueue->createSegmentWithBytes(state->snd_nxt, bytes);
00443     tcpseg->setAckNo(state->rcv_nxt);
00444     tcpseg->setAckBit(true);
00445     tcpseg->setWindow(state->rcv_wnd);
00446     // TBD when to set PSH bit?
00447     // TBD set URG bit if needed
00448     ASSERT(bytes==tcpseg->getPayloadLength());
00449 
00450     state->snd_nxt += bytes;
00451 
00452     if (state->send_fin && state->snd_nxt==state->snd_fin_seq)
00453     {
00454         tcpEV << "Setting FIN on segment\n";
00455         tcpseg->setFinBit(true);
00456         state->snd_nxt = state->snd_fin_seq+1;
00457     }
00458 
00459     sendToIP(tcpseg);
00460 }

void TCPConnection::sendToIP ( TCPSegment tcpseg  )  [virtual]

Utility: adds control info to segment and sends it to IP

Referenced by sendAck(), sendFin(), sendRst(), sendRstAck(), sendSegment(), sendSyn(), and sendSynAck().

00169 {
00170     // record seq (only if we do send data) and ackno
00171     if (sndNxtVector && tcpseg->getPayloadLength()!=0)
00172         sndNxtVector->record(tcpseg->getSequenceNo());
00173     if (sndAckVector) sndAckVector->record(tcpseg->getAckNo());
00174 
00175     // final touches on the segment before sending
00176     tcpseg->setSrcPort(localPort);
00177     tcpseg->setDestPort(remotePort);
00178     tcpseg->setByteLength(TCP_HEADER_OCTETS+tcpseg->getPayloadLength());
00179     // TBD account for Options (once they get implemented)
00180 
00181     tcpEV << "Sending: ";
00182     printSegmentBrief(tcpseg);
00183 
00184     // TBD reuse next function for sending
00185 
00186     if (!remoteAddr.isIPv6())
00187     {
00188         // send over IPv4
00189         IPControlInfo *controlInfo = new IPControlInfo();
00190         controlInfo->setProtocol(IP_PROT_TCP);
00191         controlInfo->setSrcAddr(localAddr.get4());
00192         controlInfo->setDestAddr(remoteAddr.get4());
00193         tcpseg->setControlInfo(controlInfo);
00194 
00195         tcpMain->send(tcpseg,"ipOut");
00196     }
00197     else
00198     {
00199         // send over IPv6
00200         IPv6ControlInfo *controlInfo = new IPv6ControlInfo();
00201         controlInfo->setProtocol(IP_PROT_TCP);
00202         controlInfo->setSrcAddr(localAddr.get6());
00203         controlInfo->setDestAddr(remoteAddr.get6());
00204         tcpseg->setControlInfo(controlInfo);
00205 
00206         tcpMain->send(tcpseg,"ipv6Out");
00207     }
00208 }

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.

Referenced by TCPVirtualDataSendQueue::createSegmentWithBytes(), TCPMsgBasedSendQueue::createSegmentWithBytes(), sendAck(), sendFin(), sendRst(), sendRstAck(), sendSyn(), and sendSynAck().

00240 {
00241     return new TCPSegment(name);
00242 }

void TCPConnection::startSynRexmitTimer (  )  [virtual]

void TCPConnection::signalConnectionTimeout (  )  [virtual]

Utility: signal to user that connection timed out

Referenced by TCPBaseAlg::processRexmitTimer().

00245 {
00246     sendIndicationToApp(TCP_I_TIMED_OUT);
00247 }

void TCPConnection::scheduleTimeout ( cMessage *  msg,
simtime_t  timeout 
) [inline]

cMessage* TCPConnection::cancelEvent ( cMessage *  msg  )  [inline, protected]

Utility: cancel a timer

Referenced by processSegment1stThru8th(), startSynRexmitTimer(), stateEntered(), and ~TCPConnection().

00417 {return tcpMain->cancelEvent(msg);}

void TCPConnection::sendToIP ( TCPSegment tcpseg,
IPvXAddress  src,
IPvXAddress  dest 
) [static, protected]

Utility: send IP packet

00211 {
00212     tcpEV << "Sending: ";
00213     printSegmentBrief(tcpseg);
00214 
00215     if (!dest.isIPv6())
00216     {
00217         // send over IPv4
00218         IPControlInfo *controlInfo = new IPControlInfo();
00219         controlInfo->setProtocol(IP_PROT_TCP);
00220         controlInfo->setSrcAddr(src.get4());
00221         controlInfo->setDestAddr(dest.get4());
00222         tcpseg->setControlInfo(controlInfo);
00223 
00224         check_and_cast<TCP *>(simulation.getContextModule())->send(tcpseg,"ipOut");
00225     }
00226     else
00227     {
00228         // send over IPv6
00229         IPv6ControlInfo *controlInfo = new IPv6ControlInfo();
00230         controlInfo->setProtocol(IP_PROT_TCP);
00231         controlInfo->setSrcAddr(src.get6());
00232         controlInfo->setDestAddr(dest.get6());
00233         tcpseg->setControlInfo(controlInfo);
00234 
00235         check_and_cast<TCP *>(simulation.getContextModule())->send(tcpseg,"ipv6Out");
00236     }
00237 }

void TCPConnection::sendToApp ( cMessage *  msg  )  [protected, virtual]

Utility: sends packet to application

Referenced by process_STATUS(), and processSegment1stThru8th().

00278 {
00279     tcpMain->send(msg, "appOut", appGateIndex);
00280 }

void TCPConnection::sendIndicationToApp ( int  code  )  [protected, virtual]

Utility: sends status indication (TCP_I_xxx) to application

Referenced by process_TIMEOUT_CONN_ESTAB(), process_TIMEOUT_FIN_WAIT_2(), processRstInSynReceived(), processSegment1stThru8th(), processSegmentInSynSent(), and signalConnectionTimeout().

00250 {
00251     tcpEV << "Notifying app: " << indicationName(code) << "\n";
00252     cMessage *msg = new cMessage(indicationName(code));
00253     msg->setKind(code);
00254     TCPCommand *ind = new TCPCommand();
00255     ind->setConnId(connId);
00256     msg->setControlInfo(ind);
00257     tcpMain->send(msg, "appOut", appGateIndex);
00258 }

void TCPConnection::sendEstabIndicationToApp (  )  [protected, virtual]

Utility: sends TCP_I_ESTABLISHED indication with TCPConnectInfo to application

Referenced by processSegment1stThru8th(), and processSegmentInSynSent().

00261 {
00262     tcpEV << "Notifying app: " << indicationName(TCP_I_ESTABLISHED) << "\n";
00263     cMessage *msg = new cMessage(indicationName(TCP_I_ESTABLISHED));
00264     msg->setKind(TCP_I_ESTABLISHED);
00265 
00266     TCPConnectInfo *ind = new TCPConnectInfo();
00267     ind->setConnId(connId);
00268     ind->setLocalAddr(localAddr);
00269     ind->setRemoteAddr(remoteAddr);
00270     ind->setLocalPort(localPort);
00271     ind->setRemotePort(remotePort);
00272 
00273     msg->setControlInfo(ind);
00274     tcpMain->send(msg, "appOut", appGateIndex);
00275 }

void TCPConnection::printConnBrief (  )  [virtual]

Utility: prints local/remote addr/port and app gate index/connId

Referenced by processAppCommand(), processTCPSegment(), and processTimer().

00109 {
00110     tcpEV << "Connection ";
00111     tcpEV << localAddr << ":" << localPort << " to " << remoteAddr << ":" << remotePort;
00112     tcpEV << "  on app[" << appGateIndex << "],connId=" << connId;
00113     tcpEV << "  in " << stateName(fsm.getState());
00114     tcpEV << "  (ptr=0x" << this << ")\n";
00115 }

void TCPConnection::printSegmentBrief ( TCPSegment tcpseg  )  [static]

Utility: prints important header fields

Referenced by process_RCV_SEGMENT(), segmentArrivalWhileClosed(), and sendToIP().

00118 {
00119     tcpEV << "." << tcpseg->getSrcPort() << " > ";
00120     tcpEV << "." << tcpseg->getDestPort() << ": ";
00121 
00122     if (tcpseg->getSynBit())  tcpEV << (tcpseg->getAckBit() ? "SYN+ACK " : "SYN ");
00123     if (tcpseg->getFinBit())  tcpEV << "FIN(+ACK) ";
00124     if (tcpseg->getRstBit())  tcpEV << (tcpseg->getAckBit() ? "RST+ACK " : "RST ");
00125     if (tcpseg->getPshBit())  tcpEV << "PSH ";
00126 
00127     if (tcpseg->getPayloadLength()>0 || tcpseg->getSynBit())
00128     {
00129         tcpEV << tcpseg->getSequenceNo() << ":" << tcpseg->getSequenceNo()+tcpseg->getPayloadLength();
00130         tcpEV << "(" << tcpseg->getPayloadLength() << ") ";
00131     }
00132     if (tcpseg->getAckBit())  tcpEV << "ack " << tcpseg->getAckNo() << " ";
00133     tcpEV << "win " << tcpseg->getWindow() << "\n";
00134     if (tcpseg->getUrgBit())  tcpEV << "urg " << tcpseg->getUrgentPointer() << " ";
00135 }

const char * TCPConnection::stateName ( int  state  )  [static]

Utility: returns name of TCP_S_xxx constants

Referenced by performStateTransition(), printConnBrief(), process_STATUS(), process_TIMEOUT_2MSL(), process_TIMEOUT_CONN_ESTAB(), process_TIMEOUT_FIN_WAIT_2(), and process_TIMEOUT_SYN_REXMIT().

00037 {
00038 #define CASE(x) case x: s=#x+6; break
00039     const char *s = "unknown";
00040     switch (state)
00041     {
00042         CASE(TCP_S_INIT);
00043         CASE(TCP_S_CLOSED);
00044         CASE(TCP_S_LISTEN);
00045         CASE(TCP_S_SYN_SENT);
00046         CASE(TCP_S_SYN_RCVD);
00047         CASE(TCP_S_ESTABLISHED);
00048         CASE(TCP_S_CLOSE_WAIT);
00049         CASE(TCP_S_LAST_ACK);
00050         CASE(TCP_S_FIN_WAIT_1);
00051         CASE(TCP_S_FIN_WAIT_2);
00052         CASE(TCP_S_CLOSING);
00053         CASE(TCP_S_TIME_WAIT);
00054     }
00055     return s;
00056 #undef CASE
00057 }

const char * TCPConnection::eventName ( int  event  )  [static]

Utility: returns name of TCP_E_xxx constants

Referenced by performStateTransition(), and processAppCommand().

00060 {
00061 #define CASE(x) case x: s=#x+6; break
00062     const char *s = "unknown";
00063     switch (event)
00064     {
00065         CASE(TCP_E_IGNORE);
00066         CASE(TCP_E_OPEN_ACTIVE);
00067         CASE(TCP_E_OPEN_PASSIVE);
00068         CASE(TCP_E_SEND);
00069         CASE(TCP_E_CLOSE);
00070         CASE(TCP_E_ABORT);
00071         CASE(TCP_E_STATUS);
00072         CASE(TCP_E_RCV_DATA);
00073         CASE(TCP_E_RCV_ACK);
00074         CASE(TCP_E_RCV_SYN);
00075         CASE(TCP_E_RCV_SYN_ACK);
00076         CASE(TCP_E_RCV_FIN);
00077         CASE(TCP_E_RCV_FIN_ACK);
00078         CASE(TCP_E_RCV_RST);
00079         CASE(TCP_E_RCV_UNEXP_SYN);
00080         CASE(TCP_E_TIMEOUT_2MSL);
00081         CASE(TCP_E_TIMEOUT_CONN_ESTAB);
00082         CASE(TCP_E_TIMEOUT_FIN_WAIT_2);
00083     }
00084     return s;
00085 #undef CASE
00086 }

const char * TCPConnection::indicationName ( int  code  )  [static]

Utility: returns name of TCP_I_xxx constants

Referenced by sendEstabIndicationToApp(), and sendIndicationToApp().

00089 {
00090 #define CASE(x) case x: s=#x+6; break
00091     const char *s = "unknown";
00092     switch (code)
00093     {
00094         CASE(TCP_I_DATA);
00095         CASE(TCP_I_URGENT_DATA);
00096         CASE(TCP_I_ESTABLISHED);
00097         CASE(TCP_I_PEER_CLOSED);
00098         CASE(TCP_I_CLOSED);
00099         CASE(TCP_I_CONNECTION_REFUSED);
00100         CASE(TCP_I_CONNECTION_RESET);
00101         CASE(TCP_I_TIMED_OUT);
00102         CASE(TCP_I_STATUS);
00103     }
00104     return s;
00105 #undef CASE
00106 }

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.

Referenced by TCP::segmentArrivalWhileClosed().

00036 {
00037     tcpEV << "Seg arrived: ";
00038     printSegmentBrief(tcpseg);
00039 
00040     // This segment doesn't belong to any connection, so this object
00041     // must be a temp object created solely for the purpose of calling us
00042     ASSERT(state==NULL);
00043     tcpEV << "Segment doesn't belong to any existing connection\n";
00044 
00045     // RFC 793:
00046     //"
00047     // all data in the incoming segment is discarded.  An incoming
00048     // segment containing a RST is discarded.  An incoming segment not
00049     // containing a RST causes a RST to be sent in response.  The
00050     // acknowledgment and sequence field values are selected to make the
00051     // reset sequence acceptable to the TCP that sent the offending
00052     // segment.
00053     //
00054     // If the ACK bit is off, sequence number zero is used,
00055     //
00056     //    <SEQ=0><ACK=SEG.SEQ+SEG.LEN><CTL=RST,ACK>
00057     //
00058     // If the ACK bit is on,
00059     //
00060     //    <SEQ=SEG.ACK><CTL=RST>
00061     //"
00062     if (tcpseg->getRstBit())
00063     {
00064         tcpEV << "RST bit set: dropping segment\n";
00065         return;
00066     }
00067 
00068     if (!tcpseg->getAckBit())
00069     {
00070         tcpEV << "ACK bit not set: sending RST+ACK\n";
00071         uint32 ackNo = tcpseg->getSequenceNo() + (uint32)tcpseg->getPayloadLength();
00072         sendRstAck(0,ackNo,destAddr,srcAddr,tcpseg->getDestPort(),tcpseg->getSrcPort());
00073     }
00074     else
00075     {
00076         tcpEV << "ACK bit set: sending RST\n";
00077         sendRst(tcpseg->getAckNo(),destAddr,srcAddr,tcpseg->getDestPort(),tcpseg->getSrcPort());
00078     }
00079 }

int TCPConnection::getFsmState (  )  const [inline]

00470 {return fsm.getState();}

TCPStateVariables* TCPConnection::getState (  )  [inline]

00471 {return state;}

TCPSendQueue* TCPConnection::getSendQueue (  )  [inline]

00472 {return sendQueue;}

TCPReceiveQueue* TCPConnection::getReceiveQueue (  )  [inline]

00473 {return receiveQueue;}

TCPAlgorithm* TCPConnection::getTcpAlgorithm (  )  [inline]

00474 {return tcpAlgorithm;}

TCP* TCPConnection::getTcpMain (  )  [inline]

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).

Referenced by TCP::handleMessage().

00182 {
00183     printConnBrief();
00184     tcpEV << msg->getName() << " timer expired\n";
00185 
00186     // first do actions
00187     TCPEventCode event;
00188     if (msg==the2MSLTimer)
00189     {
00190         event = TCP_E_TIMEOUT_2MSL;
00191         process_TIMEOUT_2MSL();
00192     }
00193     else if (msg==connEstabTimer)
00194     {
00195         event = TCP_E_TIMEOUT_CONN_ESTAB;
00196         process_TIMEOUT_CONN_ESTAB();
00197     }
00198     else if (msg==finWait2Timer)
00199     {
00200         event = TCP_E_TIMEOUT_FIN_WAIT_2;
00201         process_TIMEOUT_FIN_WAIT_2();
00202     }
00203     else if (msg==synRexmitTimer)
00204     {
00205         event = TCP_E_IGNORE;
00206         process_TIMEOUT_SYN_REXMIT(event);
00207     }
00208     else
00209     {
00210         event = TCP_E_IGNORE;
00211         tcpAlgorithm->processTimer(msg, event);
00212     }
00213 
00214     // then state transitions
00215     return performStateTransition(event);
00216 }

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).

Referenced by TCP::handleMessage().

00219 {
00220     printConnBrief();
00221     if (!localAddr.isUnspecified())
00222     {
00223         ASSERT(localAddr==segDestAddr);
00224         ASSERT(localPort==tcpseg->getDestPort());
00225     }
00226     if (!remoteAddr.isUnspecified())
00227     {
00228         ASSERT(remoteAddr==segSrcAddr);
00229         ASSERT(remotePort==tcpseg->getSrcPort());
00230     }
00231 
00232     if (tryFastRoute(tcpseg))
00233         return true;
00234 
00235     // first do actions
00236     TCPEventCode event = process_RCV_SEGMENT(tcpseg, segSrcAddr, segDestAddr);
00237 
00238     // then state transitions
00239     return performStateTransition(event);
00240 }

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).

Referenced by TCP::handleMessage().

00243 {
00244     printConnBrief();
00245 
00246     // first do actions
00247     TCPCommand *tcpCommand = (TCPCommand *)(msg->removeControlInfo());
00248     TCPEventCode event = preanalyseAppCommandEvent(msg->getKind());
00249     tcpEV << "App command: " << eventName(event) << "\n";
00250     switch (event)
00251     {
00252         case TCP_E_OPEN_ACTIVE: process_OPEN_ACTIVE(event, tcpCommand, msg); break;
00253         case TCP_E_OPEN_PASSIVE: process_OPEN_PASSIVE(event, tcpCommand, msg); break;
00254         case TCP_E_SEND: process_SEND(event, tcpCommand, msg); break;
00255         case TCP_E_CLOSE: process_CLOSE(event, tcpCommand, msg); break;
00256         case TCP_E_ABORT: process_ABORT(event, tcpCommand, msg); break;
00257         case TCP_E_STATUS: process_STATUS(event, tcpCommand, msg); break;
00258         default: opp_error("wrong event code");
00259     }
00260 
00261     // then state transitions
00262     return performStateTransition(event);
00263 }


Member Data Documentation

cFSM TCPConnection::fsm [protected]

cMessage* TCPConnection::the2MSLTimer [protected]

cMessage* TCPConnection::connEstabTimer [protected]

cMessage* TCPConnection::finWait2Timer [protected]

cMessage* TCPConnection::synRexmitTimer [protected]

cOutVector* TCPConnection::sndWndVector [protected]

cOutVector* TCPConnection::sndNxtVector [protected]

cOutVector* TCPConnection::sndAckVector [protected]

cOutVector* TCPConnection::rcvSeqVector [protected]

cOutVector* TCPConnection::rcvAckVector [protected]

cOutVector* TCPConnection::unackedVector [protected]


The documentation for this class was generated from the following files:

Generated on Fri Mar 20 18:51:22 2009 for INET Framework for OMNeT++/OMNEST by  doxygen 1.5.5