#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 () |
void | addSockPair (TCPConnection *conn, IPvXAddress localAddr, IPvXAddress remoteAddr, int localPort, int remotePort) |
void | updateSockPair (TCPConnection *conn, IPvXAddress localAddr, IPvXAddress remoteAddr, int localPort, int remotePort) |
void | addForkedConnection (TCPConnection *conn, TCPConnection *newConn, IPvXAddress localAddr, IPvXAddress remoteAddr, int localPort, int remotePort) |
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 | |
TCPConnection * | findConnForSegment (TCPSegment *tcpseg, IPvXAddress srcAddr, IPvXAddress destAddr) |
TCPConnection * | findConnForApp (int appGateIndex, int connId) |
void | removeConnection (TCPConnection *conn) |
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 |
|
|
|
|
|
00159 {}
|
|
00075 { 00076 while (!tcpAppConnMap.empty()) 00077 { 00078 TcpAppConnMap::iterator i = tcpAppConnMap.begin(); 00079 delete (*i).second; 00080 tcpAppConnMap.erase(i); 00081 } 00082 }
|
|
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). 00348 { 00349 // update conn's socket pair, and register newConn (which'll keep LISTENing) 00350 updateSockPair(conn, localAddr, remoteAddr, localPort, remotePort); 00351 addSockPair(newConn, newConn->localAddr, newConn->remoteAddr, newConn->localPort, newConn->remotePort); 00352 00353 // conn will get a new connId... 00354 AppConnKey key; 00355 key.appGateIndex = conn->appGateIndex; 00356 key.connId = conn->connId; 00357 tcpAppConnMap.erase(key); 00358 key.connId = conn->connId = ev.getUniqueNumber(); 00359 tcpAppConnMap[key] = conn; 00360 00361 // ...and newConn will live on with the old connId 00362 key.appGateIndex = newConn->appGateIndex; 00363 key.connId = newConn->connId; 00364 tcpAppConnMap[key] = newConn; 00365 }
|
|
To be called from TCPConnection when a new connection gets created, during processing of OPEN_ACTIVE or OPEN_PASSIVE. 00294 { 00295 // update addresses/ports in TCPConnection 00296 SockPair key; 00297 key.localAddr = conn->localAddr = localAddr; 00298 key.remoteAddr = conn->remoteAddr = remoteAddr; 00299 key.localPort = conn->localPort = localPort; 00300 key.remotePort = conn->remotePort = remotePort; 00301 00302 // make sure connection is unique 00303 TcpConnMap::iterator it = tcpConnMap.find(key); 00304 if (it!=tcpConnMap.end()) 00305 { 00306 // throw "address already in use" error 00307 if (remoteAddr.isUnspecified() && remotePort==-1) 00308 error("Address already in use: there is already a connection listening on %s:%d", 00309 localAddr.str().c_str(), localPort); 00310 else 00311 error("Address already in use: there is already a connection %s:%d to %s:%d", 00312 localAddr.str().c_str(), localPort, remoteAddr.str().c_str(), remotePort); 00313 } 00314 00315 // then insert it into tcpConnMap 00316 tcpConnMap[key] = conn; 00317 00318 // mark port as used 00319 if (localPort>=EPHEMERAL_PORTRANGE_START && localPort<EPHEMERAL_PORTRANGE_END) 00320 usedEphemeralPorts.insert(localPort); 00321 }
|
|
00264 { 00265 AppConnKey key; 00266 key.appGateIndex = appGateIndex; 00267 key.connId = connId; 00268 00269 TcpAppConnMap::iterator i = tcpAppConnMap.find(key); 00270 return i==tcpAppConnMap.end() ? NULL : i->second; 00271 }
|
|
00225 { 00226 SockPair key; 00227 key.localAddr = destAddr; 00228 key.remoteAddr = srcAddr; 00229 key.localPort = tcpseg->destPort(); 00230 key.remotePort = tcpseg->srcPort(); 00231 SockPair save = key; 00232 00233 // try with fully qualified SockPair 00234 TcpConnMap::iterator i; 00235 i = tcpConnMap.find(key); 00236 if (i!=tcpConnMap.end()) 00237 return i->second; 00238 00239 // try with localAddr missing (only localPort specified in passive/active open) 00240 key.localAddr = IPvXAddress(); 00241 i = tcpConnMap.find(key); 00242 if (i!=tcpConnMap.end()) 00243 return i->second; 00244 00245 // try fully qualified local socket + blank remote socket (for incoming SYN) 00246 key = save; 00247 key.remoteAddr = IPvXAddress(); 00248 key.remotePort = -1; 00249 i = tcpConnMap.find(key); 00250 if (i!=tcpConnMap.end()) 00251 return i->second; 00252 00253 // try with blank remote socket, and localAddr missing (for incoming SYN) 00254 key.localAddr = IPvXAddress(); 00255 i = tcpConnMap.find(key); 00256 if (i!=tcpConnMap.end()) 00257 return i->second; 00258 00259 // given up 00260 return NULL; 00261 }
|
|
00393 { 00394 tcpEV << fullPath() << ": finishing with " << tcpConnMap.size() << " connections open.\n"; 00395 }
|
|
To be called from TCPConnection: reserves an ephemeral port for the connection. 00274 { 00275 // start at the last allocated port number + 1, and search for an unused one 00276 short searchUntil = lastEphemeralPort++; 00277 if (lastEphemeralPort == EPHEMERAL_PORTRANGE_END) // wrap 00278 lastEphemeralPort = EPHEMERAL_PORTRANGE_START; 00279 00280 while (usedEphemeralPorts.find(lastEphemeralPort)!=usedEphemeralPorts.end()) 00281 { 00282 if (lastEphemeralPort == searchUntil) // got back to starting point? 00283 error("Ephemeral port range %d..%d exhausted, all ports occupied", EPHEMERAL_PORTRANGE_START, EPHEMERAL_PORTRANGE_END); 00284 lastEphemeralPort++; 00285 if (lastEphemeralPort == EPHEMERAL_PORTRANGE_END) // wrap 00286 lastEphemeralPort = EPHEMERAL_PORTRANGE_START; 00287 } 00288 00289 // found a free one, return it 00290 return lastEphemeralPort; 00291 }
|
|
00086 { 00087 if (msg->isSelfMessage()) 00088 { 00089 TCPConnection *conn = (TCPConnection *) msg->contextPointer(); 00090 bool ret = conn->processTimer(msg); 00091 if (!ret) 00092 removeConnection(conn); 00093 } 00094 else if (msg->arrivedOn("from_ip") || msg->arrivedOn("from_ipv6")) 00095 { 00096 if (dynamic_cast<ICMPMessage *>(msg) || dynamic_cast<ICMPv6Message *>(msg)) 00097 { 00098 tcpEV << "ICMP error received -- discarding\n"; // TODO implement processsing ICMP errors 00099 delete msg; 00100 } 00101 else 00102 { 00103 // must be a TCPSegment 00104 TCPSegment *tcpseg = check_and_cast<TCPSegment *>(msg); 00105 00106 // get src/dest addresses 00107 IPvXAddress srcAddr, destAddr; 00108 if (dynamic_cast<IPControlInfo *>(tcpseg->controlInfo())!=NULL) 00109 { 00110 IPControlInfo *controlInfo = (IPControlInfo *)tcpseg->removeControlInfo(); 00111 srcAddr = controlInfo->srcAddr(); 00112 destAddr = controlInfo->destAddr(); 00113 delete controlInfo; 00114 } 00115 else if (dynamic_cast<IPv6ControlInfo *>(tcpseg->controlInfo())!=NULL) 00116 { 00117 IPv6ControlInfo *controlInfo = (IPv6ControlInfo *)tcpseg->removeControlInfo(); 00118 srcAddr = controlInfo->srcAddr(); 00119 destAddr = controlInfo->destAddr(); 00120 delete controlInfo; 00121 } 00122 else 00123 { 00124 error("(%s)%s arrived without control info", tcpseg->className(), tcpseg->name()); 00125 } 00126 00127 // process segment 00128 TCPConnection *conn = findConnForSegment(tcpseg, srcAddr, destAddr); 00129 if (!conn) 00130 { 00131 TCPConnection::segmentArrivalWhileClosed(tcpseg, srcAddr, destAddr); 00132 delete tcpseg; 00133 return; 00134 } 00135 bool ret = conn->processTCPSegment(tcpseg, srcAddr, destAddr); 00136 if (!ret) 00137 removeConnection(conn); 00138 } 00139 } 00140 else // must be from app 00141 { 00142 TCPCommand *controlInfo = check_and_cast<TCPCommand *>(msg->controlInfo()); 00143 int appGateIndex = msg->arrivalGate()->index(); 00144 int connId = controlInfo->connId(); 00145 00146 TCPConnection *conn = findConnForApp(appGateIndex, connId); 00147 00148 if (!conn) 00149 { 00150 conn = new TCPConnection(this,appGateIndex,connId); 00151 00152 // add into appConnMap here; it'll be added to connMap during processing 00153 // the OPEN command in TCPConnection's processAppCommand(). 00154 AppConnKey key; 00155 key.appGateIndex = appGateIndex; 00156 key.connId = connId; 00157 tcpAppConnMap[key] = conn; 00158 00159 tcpEV << "TCP connection created for " << msg << "\n"; 00160 } 00161 bool ret = conn->processAppCommand(msg); 00162 if (!ret) 00163 removeConnection(conn); 00164 } 00165 00166 if (ev.isGUI()) 00167 updateDisplayString(); 00168 }
|
|
00060 { 00061 lastEphemeralPort = EPHEMERAL_PORTRANGE_START; 00062 WATCH(lastEphemeralPort); 00063 00064 WATCH_PTRMAP(tcpConnMap); 00065 WATCH_PTRMAP(tcpAppConnMap); 00066 00067 recordStatistics = par("recordStats"); 00068 00069 cModule *netw = simulation.systemModule(); 00070 testing = netw->hasPar("testing") && netw->par("testing").boolValue(); 00071 logverbose = !testing && netw->hasPar("logverbose") && netw->par("logverbose").boolValue(); 00072 }
|
|
00368 { 00369 tcpEV << "Deleting TCP connection\n"; 00370 00371 AppConnKey key; 00372 key.appGateIndex = conn->appGateIndex; 00373 key.connId = conn->connId; 00374 tcpAppConnMap.erase(key); 00375 00376 SockPair key2; 00377 key2.localAddr = conn->localAddr; 00378 key2.remoteAddr = conn->remoteAddr; 00379 key2.localPort = conn->localPort; 00380 key2.remotePort = conn->remotePort; 00381 tcpConnMap.erase(key2); 00382 00383 // IMPORTANT: usedEphemeralPorts.erase(conn->localPort) is NOT GOOD because it 00384 // deletes ALL occurrences of the port from the multiset. 00385 std::multiset<short>::iterator it = usedEphemeralPorts.find(conn->localPort); 00386 if (it!=usedEphemeralPorts.end()) 00387 usedEphemeralPorts.erase(it); 00388 00389 delete conn; 00390 }
|
|
00171 { 00172 if (ev.disabled()) 00173 { 00174 // in express mode, we don't bother to update the display 00175 // (std::map's iteration is not very fast if map is large) 00176 displayString().setTagArg("t",0,""); 00177 return; 00178 } 00179 00180 //char buf[40]; 00181 //sprintf(buf,"%d conns", tcpAppConnMap.size()); 00182 //displayString().setTagArg("t",0,buf); 00183 00184 int numINIT=0, numCLOSED=0, numLISTEN=0, numSYN_SENT=0, numSYN_RCVD=0, 00185 numESTABLISHED=0, numCLOSE_WAIT=0, numLAST_ACK=0, numFIN_WAIT_1=0, 00186 numFIN_WAIT_2=0, numCLOSING=0, numTIME_WAIT=0; 00187 00188 for (TcpAppConnMap::iterator i=tcpAppConnMap.begin(); i!=tcpAppConnMap.end(); ++i) 00189 { 00190 int state = (*i).second->getFsmState(); 00191 switch(state) 00192 { 00193 case TCP_S_INIT: numINIT++; break; 00194 case TCP_S_CLOSED: numCLOSED++; break; 00195 case TCP_S_LISTEN: numLISTEN++; break; 00196 case TCP_S_SYN_SENT: numSYN_SENT++; break; 00197 case TCP_S_SYN_RCVD: numSYN_RCVD++; break; 00198 case TCP_S_ESTABLISHED: numESTABLISHED++; break; 00199 case TCP_S_CLOSE_WAIT: numCLOSE_WAIT++; break; 00200 case TCP_S_LAST_ACK: numLAST_ACK++; break; 00201 case TCP_S_FIN_WAIT_1: numFIN_WAIT_1++; break; 00202 case TCP_S_FIN_WAIT_2: numFIN_WAIT_2++; break; 00203 case TCP_S_CLOSING: numCLOSING++; break; 00204 case TCP_S_TIME_WAIT: numTIME_WAIT++; break; 00205 } 00206 } 00207 char buf2[200]; 00208 buf2[0] = '\0'; 00209 if (numINIT>0) sprintf(buf2+strlen(buf2), "init:%d ", numINIT); 00210 if (numCLOSED>0) sprintf(buf2+strlen(buf2), "closed:%d ", numCLOSED); 00211 if (numLISTEN>0) sprintf(buf2+strlen(buf2), "listen:%d ", numLISTEN); 00212 if (numSYN_SENT>0) sprintf(buf2+strlen(buf2), "syn_sent:%d ", numSYN_SENT); 00213 if (numSYN_RCVD>0) sprintf(buf2+strlen(buf2), "syn_rcvd:%d ", numSYN_RCVD); 00214 if (numESTABLISHED>0) sprintf(buf2+strlen(buf2),"estab:%d ", numESTABLISHED); 00215 if (numCLOSE_WAIT>0) sprintf(buf2+strlen(buf2), "close_wait:%d ", numCLOSE_WAIT); 00216 if (numLAST_ACK>0) sprintf(buf2+strlen(buf2), "last_ack:%d ", numLAST_ACK); 00217 if (numFIN_WAIT_1>0) sprintf(buf2+strlen(buf2), "fin_wait_1:%d ", numFIN_WAIT_1); 00218 if (numFIN_WAIT_2>0) sprintf(buf2+strlen(buf2), "fin_wait_2:%d ", numFIN_WAIT_2); 00219 if (numCLOSING>0) sprintf(buf2+strlen(buf2), "closing:%d ", numCLOSING); 00220 if (numTIME_WAIT>0) sprintf(buf2+strlen(buf2), "time_wait:%d ", numTIME_WAIT); 00221 displayString().setTagArg("t",0,buf2); 00222 }
|
|
To be called from TCPConnection when socket pair (key for TcpConnMap) changes (e.g. becomes fully qualified). 00324 { 00325 // find with existing address/port pair... 00326 SockPair key; 00327 key.localAddr = conn->localAddr; 00328 key.remoteAddr = conn->remoteAddr; 00329 key.localPort = conn->localPort; 00330 key.remotePort = conn->remotePort; 00331 TcpConnMap::iterator it = tcpConnMap.find(key); 00332 ASSERT(it!=tcpConnMap.end() && it->second==conn); 00333 00334 // ...and remove from the old place in tcpConnMap 00335 tcpConnMap.erase(it); 00336 00337 // then update addresses/ports, and re-insert it with new key into tcpConnMap 00338 key.localAddr = conn->localAddr = localAddr; 00339 key.remoteAddr = conn->remoteAddr = remoteAddr; 00340 ASSERT(conn->localPort == localPort); 00341 key.remotePort = conn->remotePort = remotePort; 00342 tcpConnMap[key] = conn; 00343 00344 // localPort doesn't change (see ASSERT above), so there's no need to update usedEphemeralPorts[]. 00345 }
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|