TCP.cc

Go to the documentation of this file.
00001 //
00002 // Copyright (C) 2004 Andras Varga
00003 //
00004 // This program is free software; you can redistribute it and/or
00005 // modify it under the terms of the GNU Lesser General Public License
00006 // as published by the Free Software Foundation; either version 2
00007 // of the License, or (at your option) any later version.
00008 //
00009 // This program is distributed in the hope that it will be useful,
00010 // but WITHOUT ANY WARRANTY; without even the implied warranty of
00011 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
00012 // GNU Lesser General Public License for more details.
00013 //
00014 // You should have received a copy of the GNU Lesser General Public License
00015 // along with this program; if not, see <http://www.gnu.org/licenses/>.
00016 //
00017 
00018 
00019 #include "TCP.h"
00020 #include "TCPConnection.h"
00021 #include "TCPSegment.h"
00022 #include "TCPCommand_m.h"
00023 #include "IPControlInfo.h"
00024 #include "IPv6ControlInfo.h"
00025 #include "ICMPMessage_m.h"
00026 #include "ICMPv6Message_m.h"
00027 
00028 Define_Module(TCP);
00029 
00030 
00031 bool TCP::testing;
00032 bool TCP::logverbose;
00033 
00034 #define EPHEMERAL_PORTRANGE_START 1024
00035 #define EPHEMERAL_PORTRANGE_END   5000
00036 
00037 static std::ostream& operator<<(std::ostream& os, const TCP::SockPair& sp)
00038 {
00039     os << "loc=" << IPvXAddress(sp.localAddr) << ":" << sp.localPort << " "
00040        << "rem=" << IPvXAddress(sp.remoteAddr) << ":" << sp.remotePort;
00041     return os;
00042 }
00043 
00044 static std::ostream& operator<<(std::ostream& os, const TCP::AppConnKey& app)
00045 {
00046     os << "connId=" << app.connId << " appGateIndex=" << app.appGateIndex;
00047     return os;
00048 }
00049 
00050 static std::ostream& operator<<(std::ostream& os, const TCPConnection& conn)
00051 {
00052     os << "connId=" << conn.connId << " " << TCPConnection::stateName(conn.getFsmState())
00053        << " state={" << const_cast<TCPConnection&>(conn).getState()->info() << "}";
00054     return os;
00055 }
00056 
00057 
00058 void TCP::initialize()
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 }
00072 
00073 TCP::~TCP()
00074 {
00075     while (!tcpAppConnMap.empty())
00076     {
00077         TcpAppConnMap::iterator i = tcpAppConnMap.begin();
00078         delete (*i).second;
00079         tcpAppConnMap.erase(i);
00080     }
00081 }
00082 
00083 void TCP::handleMessage(cMessage *msg)
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 }
00168 
00169 TCPConnection *TCP::createConnection(int appGateIndex, int connId)
00170 {
00171     return new TCPConnection(this, appGateIndex, connId);
00172 }
00173 
00174 void TCP::segmentArrivalWhileClosed(TCPSegment *tcpseg, IPvXAddress srcAddr, IPvXAddress destAddr)
00175 {
00176     TCPConnection *tmp = new TCPConnection();
00177     tmp->segmentArrivalWhileClosed(tcpseg, srcAddr, destAddr);
00178     delete tmp;
00179     delete tcpseg;
00180 }
00181 
00182 void TCP::updateDisplayString()
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 }
00235 
00236 TCPConnection *TCP::findConnForSegment(TCPSegment *tcpseg, IPvXAddress srcAddr, IPvXAddress destAddr)
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 }
00274 
00275 TCPConnection *TCP::findConnForApp(int appGateIndex, int connId)
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 }
00284 
00285 ushort TCP::getEphemeralPort()
00286 {
00287     // start at the last allocated port number + 1, and search for an unused one
00288     ushort 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 }
00304 
00305 void TCP::addSockPair(TCPConnection *conn, IPvXAddress localAddr, IPvXAddress remoteAddr, int localPort, int remotePort)
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 }
00334 
00335 void TCP::updateSockPair(TCPConnection *conn, IPvXAddress localAddr, IPvXAddress remoteAddr, int localPort, int remotePort)
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 }
00358 
00359 void TCP::addForkedConnection(TCPConnection *conn, TCPConnection *newConn, IPvXAddress localAddr, IPvXAddress remoteAddr, int localPort, int remotePort)
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 }
00378 
00379 void TCP::removeConnection(TCPConnection *conn)
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<ushort>::iterator it = usedEphemeralPorts.find(conn->localPort);
00398     if (it!=usedEphemeralPorts.end())
00399         usedEphemeralPorts.erase(it);
00400 
00401     delete conn;
00402 }
00403 
00404 void TCP::finish()
00405 {
00406     tcpEV << getFullPath() << ": finishing with " << tcpConnMap.size() << " connections open.\n";
00407 }