TCP Class Reference

#include <TCP.h>

List of all members.


Detailed Description

Implements the TCP protocol. This section describes the internal architecture of the TCP model.

Usage and compliance with various RFCs are discussed in the corresponding NED documentation for TCP. Also, you may want to check the TCPSocket class which makes it easier to use TCP from applications.

The TCP protocol implementation is composed of several classes (discussion follows below):

TCP subclassed from cSimpleModule. It manages socketpair-to-connection mapping, and dispatches segments and user commands to the appropriate TCPConnection object.

TCPConnection manages the connection, with the help of other objects. TCPConnection itself implements the basic TCP "machinery": takes care of the state machine, stores the state variables (TCB), sends/receives SYN, FIN, RST, ACKs, etc.

TCPConnection internally relies on 3 objects. The first two are subclassed from TCPSendQueue and TCPReceiveQueue. They manage the actual data stream, so TCPConnection itself only works with sequence number variables. This makes it possible to easily accomodate need for various types of simulated data transfer: real byte stream, "virtual" bytes (byte counts only), and sequence of cMessage objects (where every message object is mapped to a TCP sequence number range).

Currently implemented send queue and receive queue classes are TCPVirtualDataSendQueue and TCPVirtualDataRcvQueue which implement queues with "virtual" bytes (byte counts only).

The third object is subclassed from TCPAlgorithm. Control over retransmissions, congestion control and ACK sending are "outsourced" from TCPConnection into TCPAlgorithm: delayed acks, slow start, fast rexmit, etc. are all implemented in TCPAlgorithm subclasses. This simplifies the design of TCPConnection and makes it a lot easier to implement new TCP variations such as NewReno, Vegas or LinuxTCP as TCPAlgorithm subclasses.

Currently implemented TCPAlgorithm classes are DumbTCP, TCPTahoe, TCPReno, etc.

The concrete TCPAlgorithm class to use can be chosen per connection (in OPEN) or in a module parameter.

Public Member Functions

 TCP ()
virtual ~TCP ()
virtual void addSockPair (TCPConnection *conn, IPvXAddress localAddr, IPvXAddress remoteAddr, int localPort, int remotePort)
virtual void updateSockPair (TCPConnection *conn, IPvXAddress localAddr, IPvXAddress remoteAddr, int localPort, int remotePort)
virtual void addForkedConnection (TCPConnection *conn, TCPConnection *newConn, IPvXAddress localAddr, IPvXAddress remoteAddr, int localPort, int remotePort)
virtual short getEphemeralPort ()

Public Attributes

bool recordStatistics

Static Public Attributes

static bool testing
static bool logverbose

Protected Types

typedef std::map< AppConnKey,
TCPConnection * > 
TcpAppConnMap
typedef std::map< SockPair,
TCPConnection * > 
TcpConnMap

Protected Member Functions

virtual TCPConnectioncreateConnection (int appGateIndex, int connId)
virtual TCPConnectionfindConnForSegment (TCPSegment *tcpseg, IPvXAddress srcAddr, IPvXAddress destAddr)
virtual TCPConnectionfindConnForApp (int appGateIndex, int connId)
virtual void segmentArrivalWhileClosed (TCPSegment *tcpseg, IPvXAddress src, IPvXAddress dest)
virtual void removeConnection (TCPConnection *conn)
virtual void updateDisplayString ()
virtual void initialize ()
virtual void handleMessage (cMessage *msg)
virtual void finish ()

Protected Attributes

TcpAppConnMap tcpAppConnMap
TcpConnMap tcpConnMap
short lastEphemeralPort
std::multiset< short > usedEphemeralPorts

Classes

struct  AppConnKey
struct  SockPair


Member Typedef Documentation

typedef std::map<AppConnKey,TCPConnection*> TCP::TcpAppConnMap [protected]

typedef std::map<SockPair,TCPConnection*> TCP::TcpConnMap [protected]


Constructor & Destructor Documentation

TCP::TCP (  )  [inline]

00160 {}

TCP::~TCP (  )  [virtual]

00074 {
00075     while (!tcpAppConnMap.empty())
00076     {
00077         TcpAppConnMap::iterator i = tcpAppConnMap.begin();
00078         delete (*i).second;
00079         tcpAppConnMap.erase(i);
00080     }
00081 }


Member Function Documentation

TCPConnection * TCP::createConnection ( int  appGateIndex,
int  connId 
) [protected, virtual]

Factory method; may be overriden for customizing TCP

Referenced by handleMessage().

00170 {
00171     return new TCPConnection(this, appGateIndex, connId);
00172 }

TCPConnection * TCP::findConnForSegment ( TCPSegment tcpseg,
IPvXAddress  srcAddr,
IPvXAddress  destAddr 
) [protected, virtual]

Referenced by handleMessage().

00237 {
00238     SockPair key;
00239     key.localAddr = destAddr;
00240     key.remoteAddr = srcAddr;
00241     key.localPort = tcpseg->getDestPort();
00242     key.remotePort = tcpseg->getSrcPort();
00243     SockPair save = key;
00244 
00245     // try with fully qualified SockPair
00246     TcpConnMap::iterator i;
00247     i = tcpConnMap.find(key);
00248     if (i!=tcpConnMap.end())
00249         return i->second;
00250 
00251     // try with localAddr missing (only localPort specified in passive/active open)
00252     key.localAddr = IPvXAddress();
00253     i = tcpConnMap.find(key);
00254     if (i!=tcpConnMap.end())
00255         return i->second;
00256 
00257     // try fully qualified local socket + blank remote socket (for incoming SYN)
00258     key = save;
00259     key.remoteAddr = IPvXAddress();
00260     key.remotePort = -1;
00261     i = tcpConnMap.find(key);
00262     if (i!=tcpConnMap.end())
00263         return i->second;
00264 
00265     // try with blank remote socket, and localAddr missing (for incoming SYN)
00266     key.localAddr = IPvXAddress();
00267     i = tcpConnMap.find(key);
00268     if (i!=tcpConnMap.end())
00269         return i->second;
00270 
00271     // given up
00272     return NULL;
00273 }

TCPConnection * TCP::findConnForApp ( int  appGateIndex,
int  connId 
) [protected, virtual]

Referenced by handleMessage().

00276 {
00277     AppConnKey key;
00278     key.appGateIndex = appGateIndex;
00279     key.connId = connId;
00280 
00281     TcpAppConnMap::iterator i = tcpAppConnMap.find(key);
00282     return i==tcpAppConnMap.end() ? NULL : i->second;
00283 }

void TCP::segmentArrivalWhileClosed ( TCPSegment tcpseg,
IPvXAddress  src,
IPvXAddress  dest 
) [protected, virtual]

Referenced by handleMessage().

00175 {
00176     TCPConnection *tmp = new TCPConnection();
00177     tmp->segmentArrivalWhileClosed(tcpseg, srcAddr, destAddr);
00178     delete tmp;
00179     delete tcpseg;
00180 }

void TCP::removeConnection ( TCPConnection conn  )  [protected, virtual]

Referenced by handleMessage().

00380 {
00381     tcpEV << "Deleting TCP connection\n";
00382 
00383     AppConnKey key;
00384     key.appGateIndex = conn->appGateIndex;
00385     key.connId = conn->connId;
00386     tcpAppConnMap.erase(key);
00387 
00388     SockPair key2;
00389     key2.localAddr = conn->localAddr;
00390     key2.remoteAddr = conn->remoteAddr;
00391     key2.localPort = conn->localPort;
00392     key2.remotePort = conn->remotePort;
00393     tcpConnMap.erase(key2);
00394 
00395     // IMPORTANT: usedEphemeralPorts.erase(conn->localPort) is NOT GOOD because it
00396     // deletes ALL occurrences of the port from the multiset.
00397     std::multiset<short>::iterator it = usedEphemeralPorts.find(conn->localPort);
00398     if (it!=usedEphemeralPorts.end())
00399         usedEphemeralPorts.erase(it);
00400 
00401     delete conn;
00402 }

void TCP::updateDisplayString (  )  [protected, virtual]

Referenced by handleMessage().

00183 {
00184     if (ev.isDisabled())
00185     {
00186         // in express mode, we don't bother to update the display
00187         // (std::map's iteration is not very fast if map is large)
00188         getDisplayString().setTagArg("t",0,"");
00189         return;
00190     }
00191 
00192     //char buf[40];
00193     //sprintf(buf,"%d conns", tcpAppConnMap.size());
00194     //getDisplayString().setTagArg("t",0,buf);
00195 
00196     int numINIT=0, numCLOSED=0, numLISTEN=0, numSYN_SENT=0, numSYN_RCVD=0,
00197         numESTABLISHED=0, numCLOSE_WAIT=0, numLAST_ACK=0, numFIN_WAIT_1=0,
00198         numFIN_WAIT_2=0, numCLOSING=0, numTIME_WAIT=0;
00199 
00200     for (TcpAppConnMap::iterator i=tcpAppConnMap.begin(); i!=tcpAppConnMap.end(); ++i)
00201     {
00202         int state = (*i).second->getFsmState();
00203         switch(state)
00204         {
00205            case TCP_S_INIT:        numINIT++; break;
00206            case TCP_S_CLOSED:      numCLOSED++; break;
00207            case TCP_S_LISTEN:      numLISTEN++; break;
00208            case TCP_S_SYN_SENT:    numSYN_SENT++; break;
00209            case TCP_S_SYN_RCVD:    numSYN_RCVD++; break;
00210            case TCP_S_ESTABLISHED: numESTABLISHED++; break;
00211            case TCP_S_CLOSE_WAIT:  numCLOSE_WAIT++; break;
00212            case TCP_S_LAST_ACK:    numLAST_ACK++; break;
00213            case TCP_S_FIN_WAIT_1:  numFIN_WAIT_1++; break;
00214            case TCP_S_FIN_WAIT_2:  numFIN_WAIT_2++; break;
00215            case TCP_S_CLOSING:     numCLOSING++; break;
00216            case TCP_S_TIME_WAIT:   numTIME_WAIT++; break;
00217         }
00218     }
00219     char buf2[200];
00220     buf2[0] = '\0';
00221     if (numINIT>0)       sprintf(buf2+strlen(buf2), "init:%d ", numINIT);
00222     if (numCLOSED>0)     sprintf(buf2+strlen(buf2), "closed:%d ", numCLOSED);
00223     if (numLISTEN>0)     sprintf(buf2+strlen(buf2), "listen:%d ", numLISTEN);
00224     if (numSYN_SENT>0)   sprintf(buf2+strlen(buf2), "syn_sent:%d ", numSYN_SENT);
00225     if (numSYN_RCVD>0)   sprintf(buf2+strlen(buf2), "syn_rcvd:%d ", numSYN_RCVD);
00226     if (numESTABLISHED>0) sprintf(buf2+strlen(buf2),"estab:%d ", numESTABLISHED);
00227     if (numCLOSE_WAIT>0) sprintf(buf2+strlen(buf2), "close_wait:%d ", numCLOSE_WAIT);
00228     if (numLAST_ACK>0)   sprintf(buf2+strlen(buf2), "last_ack:%d ", numLAST_ACK);
00229     if (numFIN_WAIT_1>0) sprintf(buf2+strlen(buf2), "fin_wait_1:%d ", numFIN_WAIT_1);
00230     if (numFIN_WAIT_2>0) sprintf(buf2+strlen(buf2), "fin_wait_2:%d ", numFIN_WAIT_2);
00231     if (numCLOSING>0)    sprintf(buf2+strlen(buf2), "closing:%d ", numCLOSING);
00232     if (numTIME_WAIT>0)  sprintf(buf2+strlen(buf2), "time_wait:%d ", numTIME_WAIT);
00233     getDisplayString().setTagArg("t",0,buf2);
00234 }

void TCP::initialize (  )  [protected, virtual]

00059 {
00060     lastEphemeralPort = EPHEMERAL_PORTRANGE_START;
00061     WATCH(lastEphemeralPort);
00062 
00063     WATCH_PTRMAP(tcpConnMap);
00064     WATCH_PTRMAP(tcpAppConnMap);
00065 
00066     recordStatistics = par("recordStats");
00067 
00068     cModule *netw = simulation.getSystemModule();
00069     testing = netw->hasPar("testing") && netw->par("testing").boolValue();
00070     logverbose = !testing && netw->hasPar("logverbose") && netw->par("logverbose").boolValue();
00071 }

void TCP::handleMessage ( cMessage *  msg  )  [protected, virtual]

00084 {
00085     if (msg->isSelfMessage())
00086     {
00087         TCPConnection *conn = (TCPConnection *) msg->getContextPointer();
00088         bool ret = conn->processTimer(msg);
00089         if (!ret)
00090             removeConnection(conn);
00091     }
00092     else if (msg->arrivedOn("ipIn") || msg->arrivedOn("ipv6In"))
00093     {
00094         if (dynamic_cast<ICMPMessage *>(msg) || dynamic_cast<ICMPv6Message *>(msg))
00095         {
00096             tcpEV << "ICMP error received -- discarding\n"; // FIXME can ICMP packets really make it up to TCP???
00097             delete msg;
00098         }
00099         else
00100         {
00101             // must be a TCPSegment
00102             TCPSegment *tcpseg = check_and_cast<TCPSegment *>(msg);
00103 
00104             // get src/dest addresses
00105             IPvXAddress srcAddr, destAddr;
00106             if (dynamic_cast<IPControlInfo *>(tcpseg->getControlInfo())!=NULL)
00107             {
00108                 IPControlInfo *controlInfo = (IPControlInfo *)tcpseg->removeControlInfo();
00109                 srcAddr = controlInfo->getSrcAddr();
00110                 destAddr = controlInfo->getDestAddr();
00111                 delete controlInfo;
00112             }
00113             else if (dynamic_cast<IPv6ControlInfo *>(tcpseg->getControlInfo())!=NULL)
00114             {
00115                 IPv6ControlInfo *controlInfo = (IPv6ControlInfo *)tcpseg->removeControlInfo();
00116                 srcAddr = controlInfo->getSrcAddr();
00117                 destAddr = controlInfo->getDestAddr();
00118                 delete controlInfo;
00119             }
00120             else
00121             {
00122                 error("(%s)%s arrived without control info", tcpseg->getClassName(), tcpseg->getName());
00123             }
00124 
00125             // process segment
00126             TCPConnection *conn = findConnForSegment(tcpseg, srcAddr, destAddr);
00127             if (conn)
00128             {
00129                 bool ret = conn->processTCPSegment(tcpseg, srcAddr, destAddr);
00130                 if (!ret)
00131                     removeConnection(conn);
00132             }
00133             else
00134             {
00135                 segmentArrivalWhileClosed(tcpseg, srcAddr, destAddr);
00136             }
00137         }
00138     }
00139     else // must be from app
00140     {
00141         TCPCommand *controlInfo = check_and_cast<TCPCommand *>(msg->getControlInfo());
00142         int appGateIndex = msg->getArrivalGate()->getIndex();
00143         int connId = controlInfo->getConnId();
00144 
00145         TCPConnection *conn = findConnForApp(appGateIndex, connId);
00146 
00147         if (!conn)
00148         {
00149             conn = createConnection(appGateIndex, connId);
00150 
00151             // add into appConnMap here; it'll be added to connMap during processing
00152             // the OPEN command in TCPConnection's processAppCommand().
00153             AppConnKey key;
00154             key.appGateIndex = appGateIndex;
00155             key.connId = connId;
00156             tcpAppConnMap[key] = conn;
00157 
00158             tcpEV << "TCP connection created for " << msg << "\n";
00159         }
00160         bool ret = conn->processAppCommand(msg);
00161         if (!ret)
00162             removeConnection(conn);
00163     }
00164 
00165     if (ev.isGUI())
00166         updateDisplayString();
00167 }

void TCP::finish (  )  [protected, virtual]

00405 {
00406     tcpEV << getFullPath() << ": finishing with " << tcpConnMap.size() << " connections open.\n";
00407 }

void TCP::addSockPair ( TCPConnection conn,
IPvXAddress  localAddr,
IPvXAddress  remoteAddr,
int  localPort,
int  remotePort 
) [virtual]

To be called from TCPConnection when a new connection gets created, during processing of OPEN_ACTIVE or OPEN_PASSIVE.

Referenced by addForkedConnection(), TCPConnection::process_OPEN_ACTIVE(), and TCPConnection::process_OPEN_PASSIVE().

00306 {
00307     // update addresses/ports in TCPConnection
00308     SockPair key;
00309     key.localAddr = conn->localAddr = localAddr;
00310     key.remoteAddr = conn->remoteAddr = remoteAddr;
00311     key.localPort = conn->localPort = localPort;
00312     key.remotePort = conn->remotePort = remotePort;
00313 
00314     // make sure connection is unique
00315     TcpConnMap::iterator it = tcpConnMap.find(key);
00316     if (it!=tcpConnMap.end())
00317     {
00318         // throw "address already in use" error
00319         if (remoteAddr.isUnspecified() && remotePort==-1)
00320             error("Address already in use: there is already a connection listening on %s:%d",
00321                   localAddr.str().c_str(), localPort);
00322         else
00323             error("Address already in use: there is already a connection %s:%d to %s:%d",
00324                   localAddr.str().c_str(), localPort, remoteAddr.str().c_str(), remotePort);
00325     }
00326 
00327     // then insert it into tcpConnMap
00328     tcpConnMap[key] = conn;
00329 
00330     // mark port as used
00331     if (localPort>=EPHEMERAL_PORTRANGE_START && localPort<EPHEMERAL_PORTRANGE_END)
00332         usedEphemeralPorts.insert(localPort);
00333 }

void TCP::updateSockPair ( TCPConnection conn,
IPvXAddress  localAddr,
IPvXAddress  remoteAddr,
int  localPort,
int  remotePort 
) [virtual]

To be called from TCPConnection when socket pair (key for TcpConnMap) changes (e.g. becomes fully qualified).

Referenced by addForkedConnection(), TCPConnection::processSegmentInListen(), and TCPConnection::processSegmentInSynSent().

00336 {
00337     // find with existing address/port pair...
00338     SockPair key;
00339     key.localAddr = conn->localAddr;
00340     key.remoteAddr = conn->remoteAddr;
00341     key.localPort = conn->localPort;
00342     key.remotePort = conn->remotePort;
00343     TcpConnMap::iterator it = tcpConnMap.find(key);
00344     ASSERT(it!=tcpConnMap.end() && it->second==conn);
00345 
00346     // ...and remove from the old place in tcpConnMap
00347     tcpConnMap.erase(it);
00348 
00349     // then update addresses/ports, and re-insert it with new key into tcpConnMap
00350     key.localAddr = conn->localAddr = localAddr;
00351     key.remoteAddr = conn->remoteAddr = remoteAddr;
00352     ASSERT(conn->localPort == localPort);
00353     key.remotePort = conn->remotePort = remotePort;
00354     tcpConnMap[key] = conn;
00355 
00356     // localPort doesn't change (see ASSERT above), so there's no need to update usedEphemeralPorts[].
00357 }

void TCP::addForkedConnection ( TCPConnection conn,
TCPConnection newConn,
IPvXAddress  localAddr,
IPvXAddress  remoteAddr,
int  localPort,
int  remotePort 
) [virtual]

Update conn's socket pair, and register newConn (which'll keep LISTENing). Also, conn will get a new connId (and newConn will live on with its old connId).

Referenced by TCPConnection::processSegmentInListen().

00360 {
00361     // update conn's socket pair, and register newConn (which'll keep LISTENing)
00362     updateSockPair(conn, localAddr, remoteAddr, localPort, remotePort);
00363     addSockPair(newConn, newConn->localAddr, newConn->remoteAddr, newConn->localPort, newConn->remotePort);
00364 
00365     // conn will get a new connId...
00366     AppConnKey key;
00367     key.appGateIndex = conn->appGateIndex;
00368     key.connId = conn->connId;
00369     tcpAppConnMap.erase(key);
00370     key.connId = conn->connId = ev.getUniqueNumber();
00371     tcpAppConnMap[key] = conn;
00372 
00373     // ...and newConn will live on with the old connId
00374     key.appGateIndex = newConn->appGateIndex;
00375     key.connId = newConn->connId;
00376     tcpAppConnMap[key] = newConn;
00377 }

short TCP::getEphemeralPort (  )  [virtual]

To be called from TCPConnection: reserves an ephemeral port for the connection.

Referenced by TCPConnection::process_OPEN_ACTIVE().

00286 {
00287     // start at the last allocated port number + 1, and search for an unused one
00288     short searchUntil = lastEphemeralPort++;
00289     if (lastEphemeralPort == EPHEMERAL_PORTRANGE_END) // wrap
00290         lastEphemeralPort = EPHEMERAL_PORTRANGE_START;
00291 
00292     while (usedEphemeralPorts.find(lastEphemeralPort)!=usedEphemeralPorts.end())
00293     {
00294         if (lastEphemeralPort == searchUntil) // got back to starting point?
00295             error("Ephemeral port range %d..%d exhausted, all ports occupied", EPHEMERAL_PORTRANGE_START, EPHEMERAL_PORTRANGE_END);
00296         lastEphemeralPort++;
00297         if (lastEphemeralPort == EPHEMERAL_PORTRANGE_END) // wrap
00298             lastEphemeralPort = EPHEMERAL_PORTRANGE_START;
00299     }
00300 
00301     // found a free one, return it
00302     return lastEphemeralPort;
00303 }


Member Data Documentation

short TCP::lastEphemeralPort [protected]

Referenced by getEphemeralPort(), and initialize().

std::multiset<short> TCP::usedEphemeralPorts [protected]

bool TCP::testing [static]

Referenced by initialize().

bool TCP::logverbose [static]

Referenced by initialize().


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