#include <TCP.h>
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 TCPConnection * | createConnection (int appGateIndex, int connId) |
virtual TCPConnection * | findConnForSegment (TCPSegment *tcpseg, IPvXAddress srcAddr, IPvXAddress destAddr) |
virtual TCPConnection * | findConnForApp (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 |
typedef std::map<AppConnKey,TCPConnection*> TCP::TcpAppConnMap [protected] |
typedef std::map<SockPair,TCPConnection*> TCP::TcpConnMap [protected] |
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 }
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 }
TcpAppConnMap TCP::tcpAppConnMap [protected] |
Referenced by addForkedConnection(), findConnForApp(), handleMessage(), initialize(), removeConnection(), updateDisplayString(), and ~TCP().
TcpConnMap TCP::tcpConnMap [protected] |
Referenced by addSockPair(), findConnForSegment(), finish(), initialize(), removeConnection(), and updateSockPair().
short TCP::lastEphemeralPort [protected] |
Referenced by getEphemeralPort(), and initialize().
std::multiset<short> TCP::usedEphemeralPorts [protected] |
Referenced by addSockPair(), getEphemeralPort(), and removeConnection().
bool TCP::testing [static] |
Referenced by initialize().
bool TCP::logverbose [static] |
Referenced by initialize().
Referenced by TCPBaseAlg::initialize(), and initialize().