old/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_old.h"
00020 #include "TCPConnection_old.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 using namespace tcp_old;
00029 
00030 Define_Module(TCP);
00031 
00032 bool TCP::testing;
00033 bool TCP::logverbose;
00034 
00035 #define EPHEMERAL_PORTRANGE_START 1024
00036 #define EPHEMERAL_PORTRANGE_END   5000
00037 
00038 static std::ostream& operator<<(std::ostream& os, const TCP::SockPair& sp)
00039 {
00040     os << "loc=" << IPvXAddress(sp.localAddr) << ":" << sp.localPort << " "
00041        << "rem=" << IPvXAddress(sp.remoteAddr) << ":" << sp.remotePort;
00042     return os;
00043 }
00044 
00045 static std::ostream& operator<<(std::ostream& os, const TCP::AppConnKey& app)
00046 {
00047     os << "connId=" << app.connId << " appGateIndex=" << app.appGateIndex;
00048     return os;
00049 }
00050 
00051 static std::ostream& operator<<(std::ostream& os, const TCPConnection& conn)
00052 {
00053     os << "connId=" << conn.connId << " " << TCPConnection::stateName(conn.getFsmState())
00054        << " state={" << const_cast<TCPConnection&>(conn).getState()->info() << "}";
00055     return os;
00056 }
00057 
00058 
00059 void TCP::initialize()
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.getSystemModule();
00070     testing = netw->hasPar("testing") && netw->par("testing").boolValue();
00071     logverbose = !testing && netw->hasPar("logverbose") && netw->par("logverbose").boolValue();
00072 }
00073 
00074 TCP::~TCP()
00075 {
00076     while (!tcpAppConnMap.empty())
00077     {
00078         TcpAppConnMap::iterator i = tcpAppConnMap.begin();
00079         delete (*i).second;
00080         tcpAppConnMap.erase(i);
00081     }
00082 }
00083 
00084 void TCP::handleMessage(cMessage *msg)
00085 {
00086     if (msg->isSelfMessage())
00087     {
00088         TCPConnection *conn = (TCPConnection *) msg->getContextPointer();
00089         bool ret = conn->processTimer(msg);
00090         if (!ret)
00091             removeConnection(conn);
00092     }
00093     else if (msg->arrivedOn("ipIn") || msg->arrivedOn("ipv6In"))
00094     {
00095         if (dynamic_cast<ICMPMessage *>(msg) || dynamic_cast<ICMPv6Message *>(msg))
00096         {
00097             tcpEV << "ICMP error received -- discarding\n"; // FIXME can ICMP packets really make it up to TCP???
00098             delete msg;
00099         }
00100         else
00101         {
00102             // must be a TCPSegment
00103             TCPSegment *tcpseg = check_and_cast<TCPSegment *>(msg);
00104 
00105             // get src/dest addresses
00106             IPvXAddress srcAddr, destAddr;
00107             if (dynamic_cast<IPControlInfo *>(tcpseg->getControlInfo())!=NULL)
00108             {
00109                 IPControlInfo *controlInfo = (IPControlInfo *)tcpseg->removeControlInfo();
00110                 srcAddr = controlInfo->getSrcAddr();
00111                 destAddr = controlInfo->getDestAddr();
00112                 delete controlInfo;
00113             }
00114             else if (dynamic_cast<IPv6ControlInfo *>(tcpseg->getControlInfo())!=NULL)
00115             {
00116                 IPv6ControlInfo *controlInfo = (IPv6ControlInfo *)tcpseg->removeControlInfo();
00117                 srcAddr = controlInfo->getSrcAddr();
00118                 destAddr = controlInfo->getDestAddr();
00119                 delete controlInfo;
00120             }
00121             else
00122             {
00123                 error("(%s)%s arrived without control info", tcpseg->getClassName(), tcpseg->getName());
00124             }
00125 
00126             // process segment
00127             TCPConnection *conn = findConnForSegment(tcpseg, srcAddr, destAddr);
00128             if (conn)
00129             {
00130                 bool ret = conn->processTCPSegment(tcpseg, srcAddr, destAddr);
00131                 if (!ret)
00132                     removeConnection(conn);
00133             }
00134             else
00135             {
00136                 segmentArrivalWhileClosed(tcpseg, srcAddr, destAddr);
00137             }
00138         }
00139     }
00140     else // must be from app
00141     {
00142         TCPCommand *controlInfo = check_and_cast<TCPCommand *>(msg->getControlInfo());
00143         int appGateIndex = msg->getArrivalGate()->getIndex();
00144         int connId = controlInfo->getConnId();
00145 
00146         TCPConnection *conn = findConnForApp(appGateIndex, connId);
00147 
00148         if (!conn)
00149         {
00150             conn = createConnection(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 }
00169 
00170 TCPConnection *TCP::createConnection(int appGateIndex, int connId)
00171 {
00172     return new TCPConnection(this, appGateIndex, connId);
00173 }
00174 
00175 void TCP::segmentArrivalWhileClosed(TCPSegment *tcpseg, IPvXAddress srcAddr, IPvXAddress destAddr)
00176 {
00177     TCPConnection *tmp = new TCPConnection();
00178     tmp->segmentArrivalWhileClosed(tcpseg, srcAddr, destAddr);
00179     delete tmp;
00180     delete tcpseg;
00181 }
00182 
00183 void TCP::updateDisplayString()
00184 {
00185     if (ev.isDisabled())
00186     {
00187         // in express mode, we don't bother to update the display
00188         // (std::map's iteration is not very fast if map is large)
00189         getDisplayString().setTagArg("t",0,"");
00190         return;
00191     }
00192 
00193     //char buf[40];
00194     //sprintf(buf,"%d conns", tcpAppConnMap.size());
00195     //getDisplayString().setTagArg("t",0,buf);
00196 
00197     int numINIT=0, numCLOSED=0, numLISTEN=0, numSYN_SENT=0, numSYN_RCVD=0,
00198         numESTABLISHED=0, numCLOSE_WAIT=0, numLAST_ACK=0, numFIN_WAIT_1=0,
00199         numFIN_WAIT_2=0, numCLOSING=0, numTIME_WAIT=0;
00200 
00201     for (TcpAppConnMap::iterator i=tcpAppConnMap.begin(); i!=tcpAppConnMap.end(); ++i)
00202     {
00203         int state = (*i).second->getFsmState();
00204         switch(state)
00205         {
00206            case TCP_S_INIT:        numINIT++; break;
00207            case TCP_S_CLOSED:      numCLOSED++; break;
00208            case TCP_S_LISTEN:      numLISTEN++; break;
00209            case TCP_S_SYN_SENT:    numSYN_SENT++; break;
00210            case TCP_S_SYN_RCVD:    numSYN_RCVD++; break;
00211            case TCP_S_ESTABLISHED: numESTABLISHED++; break;
00212            case TCP_S_CLOSE_WAIT:  numCLOSE_WAIT++; break;
00213            case TCP_S_LAST_ACK:    numLAST_ACK++; break;
00214            case TCP_S_FIN_WAIT_1:  numFIN_WAIT_1++; break;
00215            case TCP_S_FIN_WAIT_2:  numFIN_WAIT_2++; break;
00216            case TCP_S_CLOSING:     numCLOSING++; break;
00217            case TCP_S_TIME_WAIT:   numTIME_WAIT++; break;
00218         }
00219     }
00220     char buf2[200];
00221     buf2[0] = '\0';
00222     if (numINIT>0)       sprintf(buf2+strlen(buf2), "init:%d ", numINIT);
00223     if (numCLOSED>0)     sprintf(buf2+strlen(buf2), "closed:%d ", numCLOSED);
00224     if (numLISTEN>0)     sprintf(buf2+strlen(buf2), "listen:%d ", numLISTEN);
00225     if (numSYN_SENT>0)   sprintf(buf2+strlen(buf2), "syn_sent:%d ", numSYN_SENT);
00226     if (numSYN_RCVD>0)   sprintf(buf2+strlen(buf2), "syn_rcvd:%d ", numSYN_RCVD);
00227     if (numESTABLISHED>0) sprintf(buf2+strlen(buf2),"estab:%d ", numESTABLISHED);
00228     if (numCLOSE_WAIT>0) sprintf(buf2+strlen(buf2), "close_wait:%d ", numCLOSE_WAIT);
00229     if (numLAST_ACK>0)   sprintf(buf2+strlen(buf2), "last_ack:%d ", numLAST_ACK);
00230     if (numFIN_WAIT_1>0) sprintf(buf2+strlen(buf2), "fin_wait_1:%d ", numFIN_WAIT_1);
00231     if (numFIN_WAIT_2>0) sprintf(buf2+strlen(buf2), "fin_wait_2:%d ", numFIN_WAIT_2);
00232     if (numCLOSING>0)    sprintf(buf2+strlen(buf2), "closing:%d ", numCLOSING);
00233     if (numTIME_WAIT>0)  sprintf(buf2+strlen(buf2), "time_wait:%d ", numTIME_WAIT);
00234     getDisplayString().setTagArg("t",0,buf2);
00235 }
00236 
00237 TCPConnection *TCP::findConnForSegment(TCPSegment *tcpseg, IPvXAddress srcAddr, IPvXAddress destAddr)
00238 {
00239     SockPair key;
00240     key.localAddr = destAddr;
00241     key.remoteAddr = srcAddr;
00242     key.localPort = tcpseg->getDestPort();
00243     key.remotePort = tcpseg->getSrcPort();
00244     SockPair save = key;
00245 
00246     // try with fully qualified SockPair
00247     TcpConnMap::iterator i;
00248     i = tcpConnMap.find(key);
00249     if (i!=tcpConnMap.end())
00250         return i->second;
00251 
00252     // try with localAddr missing (only localPort specified in passive/active open)
00253     key.localAddr = IPvXAddress();
00254     i = tcpConnMap.find(key);
00255     if (i!=tcpConnMap.end())
00256         return i->second;
00257 
00258     // try fully qualified local socket + blank remote socket (for incoming SYN)
00259     key = save;
00260     key.remoteAddr = IPvXAddress();
00261     key.remotePort = -1;
00262     i = tcpConnMap.find(key);
00263     if (i!=tcpConnMap.end())
00264         return i->second;
00265 
00266     // try with blank remote socket, and localAddr missing (for incoming SYN)
00267     key.localAddr = IPvXAddress();
00268     i = tcpConnMap.find(key);
00269     if (i!=tcpConnMap.end())
00270         return i->second;
00271 
00272     // given up
00273     return NULL;
00274 }
00275 
00276 TCPConnection *TCP::findConnForApp(int appGateIndex, int connId)
00277 {
00278     AppConnKey key;
00279     key.appGateIndex = appGateIndex;
00280     key.connId = connId;
00281 
00282     TcpAppConnMap::iterator i = tcpAppConnMap.find(key);
00283     return i==tcpAppConnMap.end() ? NULL : i->second;
00284 }
00285 
00286 ushort TCP::getEphemeralPort()
00287 {
00288     // start at the last allocated port number + 1, and search for an unused one
00289     ushort searchUntil = lastEphemeralPort++;
00290     if (lastEphemeralPort == EPHEMERAL_PORTRANGE_END) // wrap
00291         lastEphemeralPort = EPHEMERAL_PORTRANGE_START;
00292 
00293     while (usedEphemeralPorts.find(lastEphemeralPort)!=usedEphemeralPorts.end())
00294     {
00295         if (lastEphemeralPort == searchUntil) // got back to starting point?
00296             error("Ephemeral port range %d..%d exhausted, all ports occupied", EPHEMERAL_PORTRANGE_START, EPHEMERAL_PORTRANGE_END);
00297         lastEphemeralPort++;
00298         if (lastEphemeralPort == EPHEMERAL_PORTRANGE_END) // wrap
00299             lastEphemeralPort = EPHEMERAL_PORTRANGE_START;
00300     }
00301 
00302     // found a free one, return it
00303     return lastEphemeralPort;
00304 }
00305 
00306 void TCP::addSockPair(TCPConnection *conn, IPvXAddress localAddr, IPvXAddress remoteAddr, int localPort, int remotePort)
00307 {
00308     // update addresses/ports in TCPConnection
00309     SockPair key;
00310     key.localAddr = conn->localAddr = localAddr;
00311     key.remoteAddr = conn->remoteAddr = remoteAddr;
00312     key.localPort = conn->localPort = localPort;
00313     key.remotePort = conn->remotePort = remotePort;
00314 
00315     // make sure connection is unique
00316     TcpConnMap::iterator it = tcpConnMap.find(key);
00317     if (it!=tcpConnMap.end())
00318     {
00319         // throw "address already in use" error
00320         if (remoteAddr.isUnspecified() && remotePort==-1)
00321             error("Address already in use: there is already a connection listening on %s:%d",
00322                   localAddr.str().c_str(), localPort);
00323         else
00324             error("Address already in use: there is already a connection %s:%d to %s:%d",
00325                   localAddr.str().c_str(), localPort, remoteAddr.str().c_str(), remotePort);
00326     }
00327 
00328     // then insert it into tcpConnMap
00329     tcpConnMap[key] = conn;
00330 
00331     // mark port as used
00332     if (localPort>=EPHEMERAL_PORTRANGE_START && localPort<EPHEMERAL_PORTRANGE_END)
00333         usedEphemeralPorts.insert(localPort);
00334 }
00335 
00336 void TCP::updateSockPair(TCPConnection *conn, IPvXAddress localAddr, IPvXAddress remoteAddr, int localPort, int remotePort)
00337 {
00338     // find with existing address/port pair...
00339     SockPair key;
00340     key.localAddr = conn->localAddr;
00341     key.remoteAddr = conn->remoteAddr;
00342     key.localPort = conn->localPort;
00343     key.remotePort = conn->remotePort;
00344     TcpConnMap::iterator it = tcpConnMap.find(key);
00345     ASSERT(it!=tcpConnMap.end() && it->second==conn);
00346 
00347     // ...and remove from the old place in tcpConnMap
00348     tcpConnMap.erase(it);
00349 
00350     // then update addresses/ports, and re-insert it with new key into tcpConnMap
00351     key.localAddr = conn->localAddr = localAddr;
00352     key.remoteAddr = conn->remoteAddr = remoteAddr;
00353     ASSERT(conn->localPort == localPort);
00354     key.remotePort = conn->remotePort = remotePort;
00355     tcpConnMap[key] = conn;
00356 
00357     // localPort doesn't change (see ASSERT above), so there's no need to update usedEphemeralPorts[].
00358 }
00359 
00360 void TCP::addForkedConnection(TCPConnection *conn, TCPConnection *newConn, IPvXAddress localAddr, IPvXAddress remoteAddr, int localPort, int remotePort)
00361 {
00362     // update conn's socket pair, and register newConn (which'll keep LISTENing)
00363     updateSockPair(conn, localAddr, remoteAddr, localPort, remotePort);
00364     addSockPair(newConn, newConn->localAddr, newConn->remoteAddr, newConn->localPort, newConn->remotePort);
00365 
00366     // conn will get a new connId...
00367     AppConnKey key;
00368     key.appGateIndex = conn->appGateIndex;
00369     key.connId = conn->connId;
00370     tcpAppConnMap.erase(key);
00371     key.connId = conn->connId = ev.getUniqueNumber();
00372     tcpAppConnMap[key] = conn;
00373 
00374     // ...and newConn will live on with the old connId
00375     key.appGateIndex = newConn->appGateIndex;
00376     key.connId = newConn->connId;
00377     tcpAppConnMap[key] = newConn;
00378 }
00379 
00380 void TCP::removeConnection(TCPConnection *conn)
00381 {
00382     tcpEV << "Deleting TCP connection\n";
00383 
00384     AppConnKey key;
00385     key.appGateIndex = conn->appGateIndex;
00386     key.connId = conn->connId;
00387     tcpAppConnMap.erase(key);
00388 
00389     SockPair key2;
00390     key2.localAddr = conn->localAddr;
00391     key2.remoteAddr = conn->remoteAddr;
00392     key2.localPort = conn->localPort;
00393     key2.remotePort = conn->remotePort;
00394     tcpConnMap.erase(key2);
00395 
00396     // IMPORTANT: usedEphemeralPorts.erase(conn->localPort) is NOT GOOD because it
00397     // deletes ALL occurrences of the port from the multiset.
00398     std::multiset<ushort>::iterator it = usedEphemeralPorts.find(conn->localPort);
00399     if (it!=usedEphemeralPorts.end())
00400         usedEphemeralPorts.erase(it);
00401 
00402     delete conn;
00403 }
00404 
00405 void TCP::finish()
00406 {
00407     tcpEV << getFullPath() << ": finishing with " << tcpConnMap.size() << " connections open.\n";
00408 }