INET Framework for OMNeT++/OMNEST
AnsaPPP.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 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 General Public License for more details.
00013 //
00014 // You should have received a copy of the GNU General Public License
00015 // along with this program; if not, write to the Free Software
00016 // Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
00017 //
00018 
00019 #include <stdio.h>
00020 #include <string.h>
00021 #include <omnetpp.h>
00022 #include "IInterfaceTable.h"
00023 #include "InterfaceTableAccess.h"
00024 #include "AnsaPPP.h"
00025 #include "IPassiveQueue.h"
00026 #include "NotificationBoard.h"
00027 #include "NotifierConsts.h"
00028 #include "InterfaceStateManager.h"
00029 
00030 
00031 Define_Module(AnsaPPP);
00032 
00033 AnsaPPP::AnsaPPP()
00034 {
00035     endTransmissionEvent = NULL;
00036 }
00037 
00038 AnsaPPP::~AnsaPPP()
00039 {
00040     cancelAndDelete(endTransmissionEvent);
00041 }
00042 
00043 void AnsaPPP::initialize(int stage)
00044 {
00045     // all initialization is done in the first stage
00046     if (stage==0)
00047     {
00048         txQueue.setName("txQueue");
00049         endTransmissionEvent = new cMessage("pppEndTxEvent");
00050 
00051         txQueueLimit = par("txQueueLimit");
00052         
00053         disabled = false;
00054         WATCH(disabled);
00055 
00056         interfaceEntry = NULL;
00057 
00058         numSent = numRcvdOK = numBitErr = numDroppedIfaceDown = 0;
00059         WATCH(numSent);
00060         WATCH(numRcvdOK);
00061         WATCH(numBitErr);
00062         WATCH(numDroppedIfaceDown);
00063 
00064         // find queueModule
00065         queueModule = NULL;
00066         if (par("queueModule").stringValue()[0])
00067         {
00068             cModule *mod = getParentModule()->getSubmodule(par("queueModule").stringValue());
00069             queueModule = check_and_cast<IPassiveQueue *>(mod);
00070         }
00071 
00072         // remember the output gate now, to speed up send()
00073         physOutGate = gate("phys$o");
00074 
00075         // we're connected if other end of connection path is an input gate
00076         bool connected = physOutGate->getPathEndGate()->getType()==cGate::INPUT;
00077 
00078         // if we're connected, get the gate with transmission rate
00079         datarateChannel = connected ? physOutGate->getTransmissionChannel() : NULL;
00080         double datarate = connected ? datarateChannel->par("datarate").doubleValue() : 0;
00081 
00082         // register our interface entry in IInterfaceTable
00083         interfaceEntry = registerInterface(datarate);
00084 
00085         // prepare to fire notifications
00086         nb = NotificationBoardAccess().get();
00087         nb->subscribe(this, NF_INTERFACE_STATE_CHANGED);
00088         notifDetails.setInterfaceEntry(interfaceEntry);
00089         nb->subscribe(this, NF_SUBSCRIBERLIST_CHANGED);
00090         updateHasSubcribers();
00091 
00092         // display string stuff
00093         if (ev.isGUI())
00094         {
00095             if (connected) {
00096                 oldConnColor = datarateChannel->getDisplayString().getTagArg("o",0);
00097             }
00098             else {
00099                 // we are not connected: gray out our icon
00100                 getDisplayString().setTagArg("i",1,"#707070");
00101                 getDisplayString().setTagArg("i",2,"100");
00102             }
00103         }
00104 
00105         // request first frame to send
00106         if (queueModule)
00107         {
00108             EV << "Requesting first frame from queue module\n";
00109             queueModule->requestPacket();
00110         }
00111     }
00112 
00113     // update display string when addresses have been autoconfigured etc.
00114     if (stage==3)
00115     {
00116         updateDisplayString();
00117     }
00118 }
00119 
00120 InterfaceEntry *AnsaPPP::registerInterface(double datarate)
00121 {
00122     InterfaceEntry *e = new InterfaceEntry();
00123 
00124     // interface name: our module name without special characters ([])
00125     char *interfaceName = new char[strlen(getParentModule()->getFullName())+1];
00126     char *d=interfaceName;
00127     for (const char *s=getParentModule()->getFullName(); *s; s++)
00128         if (isalnum(*s))
00129             *d++ = *s;
00130     *d = '\0';
00131 
00132     e->setName(interfaceName);
00133     delete [] interfaceName;
00134 
00135     // data rate
00136     e->setDatarate(datarate);
00137 
00138     // generate a link-layer address to be used as interface token for IPv6
00139     InterfaceToken token(0, simulation.getUniqueNumber(), 64);
00140     e->setInterfaceToken(token);
00141 
00142     // MTU: typical values are 576 (Internet de facto), 1500 (Ethernet-friendly),
00143     // 4000 (on some point-to-point links), 4470 (Cisco routers default, FDDI compatible)
00144     e->setMtu(4470);
00145 
00146     // capabilities
00147     e->setMulticast(true);
00148     e->setPointToPoint(true);
00149 
00150     // add
00151     IInterfaceTable *ift = InterfaceTableAccess().get();
00152     ift->addInterface(e, this);
00153 
00154     return e;
00155 }
00156 
00157 
00158 void AnsaPPP::startTransmitting(cPacket *msg)
00159 {
00160     // if there's any control info, remove it; then encapsulate the packet
00161     delete msg->removeControlInfo();
00162     PPPFrame *pppFrame = encapsulate(msg);
00163     if (ev.isGUI()) displayBusy();
00164 
00165     if (hasSubscribers)
00166     {
00167         // fire notification
00168         notifDetails.setPacket(pppFrame);
00169         nb->fireChangeNotification(NF_PP_TX_BEGIN, &notifDetails);
00170     }
00171 
00172     // send
00173     EV << "Starting transmission of " << pppFrame << endl;
00174     send(pppFrame, physOutGate);
00175 
00176     // schedule an event for the time when last bit will leave the gate.
00177     simtime_t endTransmissionTime = datarateChannel->getTransmissionFinishTime();
00178     scheduleAt(endTransmissionTime, endTransmissionEvent);
00179 }
00180 
00181 void AnsaPPP::handleMessage(cMessage *msg)
00182 {
00183     if (datarateChannel==NULL)
00184     {
00185         EV << "Interface is not connected, dropping packet " << msg << endl;
00186         delete msg;
00187         numDroppedIfaceDown++;
00188     }
00189     else if (disabled == true)
00190     {
00191         EV << "Interface is disabled, dropping packet " << msg << endl;
00192         delete msg;
00193         numDroppedIfaceDown++;
00194     }
00195     else if (msg==endTransmissionEvent)
00196     {
00197         // Transmission finished, we can start next one.
00198         EV << "Transmission finished.\n";
00199         if (ev.isGUI()) displayIdle();
00200 
00201         if (hasSubscribers)
00202         {
00203             // fire notification
00204             notifDetails.setPacket(NULL);
00205             nb->fireChangeNotification(NF_PP_TX_END, &notifDetails);
00206         }
00207 
00208         if (!txQueue.empty())
00209         {
00210             cPacket *pk = (cPacket *) txQueue.pop();
00211             startTransmitting(pk);
00212             numSent++;
00213         }
00214         else if (queueModule)
00215         {
00216             // tell queue module that we've become idle
00217             queueModule->requestPacket();
00218         }
00219     }
00220     else if (msg->arrivedOn("phys$i"))
00221     {
00222         if (hasSubscribers)
00223         {
00224             // fire notification
00225             notifDetails.setPacket(PK(msg));
00226             nb->fireChangeNotification(NF_PP_RX_END, &notifDetails);
00227         }
00228 
00229         // check for bit errors
00230         if (PK(msg)->hasBitError())
00231         {
00232             EV << "Bit error in " << msg << endl;
00233             numBitErr++;
00234             delete msg;
00235         }
00236         else
00237         {
00238             // pass up payload
00239             cPacket *payload = decapsulate(check_and_cast<PPPFrame *>(msg));
00240             numRcvdOK++;
00241             send(payload,"netwOut");
00242         }
00243     }
00244     else // arrived on gate "netwIn"
00245     {
00246         if (endTransmissionEvent->isScheduled())
00247         {
00248             // We are currently busy, so just queue up the packet.
00249             EV << "Received " << msg << " for transmission but transmitter busy, queueing.\n";
00250             if (ev.isGUI() && txQueue.length()>=3) getDisplayString().setTagArg("i",1,"red");
00251 
00252             if (txQueueLimit && txQueue.length()>txQueueLimit)
00253                 error("txQueue length exceeds %d -- this is probably due to "
00254                       "a bogus app model generating excessive traffic "
00255                       "(or if this is normal, increase txQueueLimit!)",
00256                       txQueueLimit);
00257 
00258             txQueue.insert(msg);
00259         }
00260         else
00261         {
00262             // We are idle, so we can start transmitting right away.
00263             EV << "Received " << msg << " for transmission\n";
00264             startTransmitting(PK(msg));
00265             numSent++;
00266         }
00267     }
00268 
00269     if (ev.isGUI())
00270         updateDisplayString();
00271 
00272 }
00273 
00274 void AnsaPPP::displayBusy()
00275 {
00276     getDisplayString().setTagArg("i",1, txQueue.length()>=3 ? "red" : "yellow");
00277     datarateChannel->getDisplayString().setTagArg("o",0,"yellow");
00278     datarateChannel->getDisplayString().setTagArg("o",1,"3");
00279 }
00280 
00281 void AnsaPPP::displayIdle()
00282 {
00283     getDisplayString().setTagArg("i",1,"");
00284     datarateChannel->getDisplayString().setTagArg("o",0,oldConnColor.c_str());
00285     datarateChannel->getDisplayString().setTagArg("o",1,"1");
00286 }
00287 
00288 void AnsaPPP::updateDisplayString()
00289 {
00290     if (ev.isDisabled())
00291     {
00292         // speed up things
00293         getDisplayString().setTagArg("t",0,"");
00294     }
00295     else if (datarateChannel!=NULL)
00296     {
00297         double datarate = datarateChannel->par("datarate").doubleValue();
00298         char datarateText[40];
00299         if (datarate>=1e9) sprintf(datarateText,"%gG", datarate/1e9);
00300         else if (datarate>=1e6) sprintf(datarateText,"%gM", datarate/1e6);
00301         else if (datarate>=1e3) sprintf(datarateText,"%gK", datarate/1e3);
00302         else sprintf(datarateText,"%gbps", datarate);
00303 
00304 /* TBD find solution for displaying IP address without dependence on IPv6 or IPv6
00305         IPAddress addr = interfaceEntry->ipv4Data()->getIPAddress();
00306         sprintf(buf, "%s / %s\nrcv:%ld snt:%ld", addr.isUnspecified()?"-":addr.str().c_str(), datarateText, numRcvdOK, numSent);
00307 */
00308 
00309         char buf[80];
00310         sprintf(buf, "%s\nrcv:%ld snt:%ld", datarateText, numRcvdOK, numSent);
00311 
00312         if (numBitErr>0)
00313             sprintf(buf+strlen(buf), "\nerr:%ld", numBitErr);
00314 
00315         getDisplayString().setTagArg("t",0,buf);
00316     }
00317     else
00318     {
00319         char buf[80];
00320         sprintf(buf, "not connected\ndropped:%ld", numDroppedIfaceDown);
00321         getDisplayString().setTagArg("t",0,buf);
00322     }
00323 }
00324 
00325 void AnsaPPP::updateHasSubcribers()
00326 {
00327     hasSubscribers = nb->hasSubscribers(NF_PP_TX_BEGIN) ||
00328                      nb->hasSubscribers(NF_PP_TX_END) ||
00329                      nb->hasSubscribers(NF_PP_RX_END);
00330 }
00331 
00332 void AnsaPPP::receiveChangeNotification(int category, const cPolymorphic *details)
00333 {
00334     if (category==NF_SUBSCRIBERLIST_CHANGED)
00335         updateHasSubcribers();
00336 
00337     else if (category==NF_INTERFACE_STATE_CHANGED) // change state of notified interface
00338     {
00339         InterfaceEntry *entry = check_and_cast<InterfaceEntry*>(details);
00340 
00341         if(interfaceEntry == entry && entry->isDown() != isDisabled())
00342         {
00343           setDisabled(entry->isDown());
00344 
00345           if(datarateChannel!=NULL)
00346           {
00347            AnsaPPP *neighInt = dynamic_cast<AnsaPPP*>(physOutGate->getPathEndGate()->getOwnerModule());
00348 
00349            if((neighInt != NULL) && (neighInt->isDisabled() != isDisabled()))
00350            {
00351               InterfaceStateManager *stMng = check_and_cast<InterfaceStateManager*>(neighInt->getParentModule()->getParentModule()->getSubmodule("interfaceStateManager"));
00352 
00353               if(stMng != NULL)
00354               {
00355                 EV << "Changing state of the interface on the other site of link" << endl;
00356                 stMng->changeInterfaceState(neighInt->getInterfaceEntry(), isDisabled());
00357               }
00358            }
00359           }
00360         }
00361     }
00362 }
00363 
00364 PPPFrame *AnsaPPP::encapsulate(cPacket *msg)
00365 {
00366     PPPFrame *pppFrame = new PPPFrame(msg->getName());
00367     pppFrame->setByteLength(PPP_OVERHEAD_BYTES);
00368     pppFrame->encapsulate(msg);
00369     return pppFrame;
00370 }
00371 
00372 cPacket *AnsaPPP::decapsulate(PPPFrame *pppFrame)
00373 {
00374     cPacket *msg = pppFrame->decapsulate();
00375     delete pppFrame;
00376     return msg;
00377 }
00378 
00379