EtherMAC.cc

Go to the documentation of this file.
00001 /*
00002  * Copyright (C) 2003 Andras Varga; CTIE, Monash University, Australia
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 #include <stdio.h>
00019 #include <string.h>
00020 #include <omnetpp.h>
00021 #include "EtherMAC.h"
00022 #include "Ieee802Ctrl_m.h"
00023 #include "IPassiveQueue.h"
00024 
00025 
00026 
00027 static std::ostream& operator<< (std::ostream& out, cMessage *msg)
00028 {
00029     out << "(" << msg->getClassName() << ")" << msg->getFullName();
00030     return out;
00031 }
00032 
00033 
00034 Define_Module( EtherMAC );
00035 
00036 EtherMAC::EtherMAC()
00037 {
00038     frameBeingReceived = NULL;
00039     endJammingMsg = endRxMsg = endBackoffMsg = NULL;
00040 }
00041 
00042 EtherMAC::~EtherMAC()
00043 {
00044     delete frameBeingReceived;
00045     cancelAndDelete(endRxMsg);
00046     cancelAndDelete(endBackoffMsg);
00047     cancelAndDelete(endJammingMsg);
00048 }
00049 
00050 void EtherMAC::initialize()
00051 {
00052     EtherMACBase::initialize();
00053 
00054     endRxMsg = new cMessage("EndReception", ENDRECEPTION);
00055     endBackoffMsg = new cMessage("EndBackoff", ENDBACKOFF);
00056     endJammingMsg = new cMessage("EndJamming", ENDJAMMING);
00057 
00058     // check: datarate is forbidden with EtherMAC -- module's txrate must be used
00059     cGate *g = physOutGate;
00060     while (g)
00061     {
00062         cDatarateChannel *chan = dynamic_cast<cDatarateChannel*>(g->getChannel());
00063         if (chan && chan->par("datarate").doubleValue()>0)
00064             error("connection on gate %s has data rate set: using data rate with EtherMAC "
00065                   "is forbidden, module's txrate parameter must be used instead",
00066                   g->getFullPath().c_str());
00067         g = g->getNextGate();
00068     }
00069 
00070     // launch autoconfig process
00071     bool performAutoconfig = true;
00072     if (!disabled && connected && performAutoconfig)
00073     {
00074         startAutoconfig();
00075     }
00076     else
00077     {
00078         autoconfigInProgress = false;
00079         duplexMode = par("duplexEnabled");
00080         calculateParameters();
00081     }
00082     WATCH(autoconfigInProgress);
00083 
00084     // initialize state info
00085     backoffs = 0;
00086     numConcurrentTransmissions = 0;
00087 
00088     WATCH(backoffs);
00089     WATCH(numConcurrentTransmissions);
00090 
00091     // initialize statistics
00092     totalCollisionTime = 0.0;
00093     totalSuccessfulRxTxTime = 0.0;
00094     numCollisions = numBackoffs = 0;
00095 
00096     WATCH(numCollisions);
00097     WATCH(numBackoffs);
00098 
00099     numCollisionsVector.setName("collisions");
00100     numBackoffsVector.setName("backoffs");
00101 }
00102 
00103 void EtherMAC::initializeTxrate()
00104 {
00105     txrate = par("txrate");
00106 }
00107 
00108 void EtherMAC::startAutoconfig()
00109 {
00110     autoconfigInProgress = true;
00111     lowestTxrateSuggested = 0;  // none suggested
00112     duplexVetoed = false;
00113 
00114     double initialTxrate = par("txrate");
00115     bool duplexEnabled = par("duplexEnabled");
00116     txrate = 0;
00117     duplexMode = duplexEnabled;
00118     if (!duplexEnabled || initialTxrate>0)
00119     {
00120         EV << "Autoconfig: advertising our settings: " << initialTxrate/1000000 << "Mb, "
00121            << (duplexMode ? "duplex" : "half-duplex") << endl;
00122 
00123         EtherAutoconfig *autoconf = new EtherAutoconfig("autoconf");
00124         if (!duplexEnabled)
00125             autoconf->setHalfDuplex(true);
00126         if (initialTxrate>0)
00127             autoconf->setTxrate(initialTxrate);
00128         send(autoconf, physOutGate);
00129     }
00130     scheduleAt(simTime()+AUTOCONFIG_PERIOD, new cMessage("EndAutoconfig",ENDAUTOCONFIG));
00131 }
00132 
00133 void EtherMAC::handleAutoconfigMessage(cMessage *msg)
00134 {
00135     if (!msg->isSelfMessage())
00136     {
00137         if (msg->getArrivalGate() == gate("upperLayerIn"))
00138         {
00139             // from upper layer
00140             EV << "Received frame from upper layer during autoconfig period: " << msg << endl;
00141             processFrameFromUpperLayer(check_and_cast<EtherFrame *>(msg));
00142         }
00143         else
00144         {
00145             // from network: must be autoconfig message
00146             EV << "Message from network during autoconfig period: " << msg << endl;
00147             EtherAutoconfig *autoconf = check_and_cast<EtherAutoconfig *>(msg);
00148             double acTxrate = autoconf->getTxrate();
00149 
00150             EV << "Autoconfig message: ";
00151             if (acTxrate>0)
00152                 EV << acTxrate/1000000 << "Mb ";
00153             if (autoconf->getHalfDuplex())
00154                 EV << "non-duplex";
00155             EV << "\n";
00156 
00157             if (acTxrate>0 && (acTxrate<lowestTxrateSuggested || lowestTxrateSuggested==0))
00158                 lowestTxrateSuggested = acTxrate;
00159             if (!duplexVetoed && autoconf->getHalfDuplex())
00160                 duplexVetoed = true;
00161             delete msg;
00162         }
00163     }
00164     else
00165     {
00166         // self-message signals end of autoconfig period
00167         EV << "Self-message during autoconfig period: " << msg << endl;
00168 
00169         delete msg;
00170         autoconfigInProgress = false;
00171 
00172         double initialTxrate = par("txrate");
00173         bool duplexEnabled = par("duplexEnabled");
00174 
00175         txrate = (initialTxrate==0 && lowestTxrateSuggested==0) ? 100000000 /* 100 Mb */:
00176                  (initialTxrate==0) ? lowestTxrateSuggested :
00177                  (lowestTxrateSuggested==0) ? initialTxrate :
00178                  (lowestTxrateSuggested<initialTxrate) ? lowestTxrateSuggested : initialTxrate;
00179         duplexMode = (duplexEnabled && !duplexVetoed);
00180         calculateParameters();
00181 
00182         EV << "Parameters after autoconfig: txrate=" << txrate/1000000 << "Mb, " << (duplexMode ? "duplex" : "half-duplex") << endl;
00183 
00184         if (ev.isGUI())
00185         {
00186             char modestr[64];
00187             sprintf(modestr, "%dMb\n%s", int(txrate/1000000), (duplexMode ? "full duplex" : "half duplex"));
00188             getDisplayString().setTagArg("t",0,modestr);
00189             //getDisplayString().setTagArg("t",1,"r");
00190             sprintf(modestr, "%s: %dMb %s", getFullName(), int(txrate/1000000), (duplexMode ? "duplex" : "half duplex"));
00191             getParentModule()->bubble(modestr);
00192         }
00193 
00194         if (!txQueue.empty())
00195         {
00196             EV << "Autoconfig period over, starting to send frames\n";
00197             scheduleEndIFGPeriod();
00198         }
00199     }
00200 }
00201 
00202 void EtherMAC::handleMessage (cMessage *msg)
00203 {
00204     if (disabled)
00205     {
00206         EV << "MAC is disabled -- dropping message " << msg << "\n";
00207         delete msg;
00208         return;
00209     }
00210     if (autoconfigInProgress)
00211     {
00212         handleAutoconfigMessage(msg);
00213         return;
00214     }
00215 
00216     printState();
00217     // some consistency check
00218     if (!duplexMode && transmitState==TRANSMITTING_STATE && receiveState!=RX_IDLE_STATE)
00219         error("Inconsistent state -- transmitting and receiving at the same time");
00220 
00221     if (!msg->isSelfMessage())
00222     {
00223         // either frame from upper layer, or frame/jam signal from the network
00224         if (msg->getArrivalGate() == gate("upperLayerIn"))
00225             processFrameFromUpperLayer(check_and_cast<EtherFrame *>(msg));
00226         else
00227             processMsgFromNetwork(PK(msg));
00228     }
00229     else
00230     {
00231         // Process different self-messages (timer signals)
00232         EV << "Self-message " << msg << " received\n";
00233         switch (msg->getKind())
00234         {
00235             case ENDIFG:
00236                 handleEndIFGPeriod();
00237                 break;
00238 
00239             case ENDTRANSMISSION:
00240                 handleEndTxPeriod();
00241                 break;
00242 
00243             case ENDRECEPTION:
00244                 handleEndRxPeriod();
00245                 break;
00246 
00247             case ENDBACKOFF:
00248                 handleEndBackoffPeriod();
00249                 break;
00250 
00251             case ENDJAMMING:
00252                 handleEndJammingPeriod();
00253                 break;
00254 
00255             case ENDPAUSE:
00256                 handleEndPausePeriod();
00257                 break;
00258 
00259             default:
00260                 error("self-message with unexpected message kind %d", msg->getKind());
00261         }
00262     }
00263     printState();
00264 
00265     if (ev.isGUI())
00266         updateDisplayString();
00267 }
00268 
00269 
00270 void EtherMAC::processFrameFromUpperLayer(EtherFrame *frame)
00271 {
00272     EtherMACBase::processFrameFromUpperLayer(frame);
00273 
00274     if (!autoconfigInProgress && (duplexMode || receiveState==RX_IDLE_STATE) && transmitState==TX_IDLE_STATE)
00275     {
00276         EV << "No incoming carrier signals detected, frame clear to send, wait IFG first\n";
00277         scheduleEndIFGPeriod();
00278     }
00279 }
00280 
00281 
00282 void EtherMAC::processMsgFromNetwork(cPacket *msg)
00283 {
00284     EtherMACBase::processMsgFromNetwork(msg);
00285 
00286     simtime_t endRxTime = simTime() + msg->getBitLength()*bitTime;
00287 
00288     if (!duplexMode && transmitState==TRANSMITTING_STATE)
00289     {
00290         // since we're halfduplex, receiveState must be RX_IDLE_STATE (asserted at top of handleMessage)
00291         if (dynamic_cast<EtherJam*>(msg) != NULL)
00292             error("Stray jam signal arrived while transmitting (usual cause is cable length exceeding allowed maximum)");
00293 
00294         EV << "Transmission interrupted by incoming frame, handling collision\n";
00295         cancelEvent(endTxMsg);
00296 
00297         EV << "Transmitting jam signal\n";
00298         sendJamSignal(); // backoff will be executed when jamming finished
00299 
00300         // set receive state and schedule end of reception
00301         receiveState = RX_COLLISION_STATE;
00302         numConcurrentTransmissions++;
00303         simtime_t endJamTime = simTime()+jamDuration;
00304         scheduleAt(endRxTime<endJamTime ? endJamTime : endRxTime, endRxMsg);
00305         delete msg;
00306 
00307         numCollisions++;
00308         numCollisionsVector.record(numCollisions);
00309     }
00310     else if (receiveState==RX_IDLE_STATE)
00311     {
00312         if (dynamic_cast<EtherJam*>(msg) != NULL)
00313             error("Stray jam signal arrived (usual cause is cable length exceeding allowed maximum)");
00314 
00315         EV << "Start reception of frame\n";
00316         numConcurrentTransmissions++;
00317         if (frameBeingReceived)
00318             error("frameBeingReceived!=0 in RX_IDLE_STATE");
00319         frameBeingReceived = (EtherFrame *)msg;
00320         scheduleEndRxPeriod(msg);
00321         channelBusySince = simTime();
00322     }
00323     else if (receiveState==RECEIVING_STATE && dynamic_cast<EtherJam*>(msg)==NULL && endRxMsg->getArrivalTime()-simTime()<bitTime)
00324     {
00325         // With the above condition we filter out "false" collisions that may occur with
00326         // back-to-back frames. That is: when "beginning of frame" message (this one) occurs
00327         // BEFORE "end of previous frame" event (endRxMsg) -- same simulation time,
00328         // only wrong order.
00329 
00330         EV << "Back-to-back frames: completing reception of current frame, starting reception of next one\n";
00331 
00332         // complete reception of previous frame
00333         cancelEvent(endRxMsg);
00334         EtherFrame *frame = frameBeingReceived;
00335         frameBeingReceived = NULL;
00336         frameReceptionComplete(frame);
00337 
00338         // start receiving next frame
00339         frameBeingReceived = (EtherFrame *)msg;
00340         scheduleEndRxPeriod(msg);
00341     }
00342     else // (receiveState==RECEIVING_STATE || receiveState==RX_COLLISION_STATE)
00343     {
00344         // handle overlapping receptions
00345         if (dynamic_cast<EtherJam*>(msg) != NULL)
00346         {
00347             if (numConcurrentTransmissions<=0)
00348                 error("numConcurrentTransmissions=%d on jam arrival (stray jam?)",numConcurrentTransmissions);
00349 
00350             numConcurrentTransmissions--;
00351             EV << "Jam signal received, this marks end of one transmission\n";
00352 
00353             // by the time jamming ends, all transmissions will have been aborted
00354             if (numConcurrentTransmissions==0)
00355             {
00356                 EV << "Last jam signal received, collision will ends when jam ends\n";
00357                 cancelEvent(endRxMsg);
00358                 scheduleAt(endRxTime, endRxMsg);
00359             }
00360         }
00361         else // EtherFrame or EtherPauseFrame
00362         {
00363             numConcurrentTransmissions++;
00364             if (endRxMsg->getArrivalTime() < endRxTime)
00365             {
00366                 // otherwise just wait until the end of the longest transmission
00367                 EV << "Overlapping receptions -- setting collision state and extending collision period\n";
00368                 cancelEvent(endRxMsg);
00369                 scheduleAt(endRxTime, endRxMsg);
00370             }
00371             else
00372             {
00373                 EV << "Overlapping receptions -- setting collision state\n";
00374             }
00375         }
00376 
00377         // delete collided frames: arrived frame as well as the one we're currently receiving
00378         delete msg;
00379         if (receiveState==RECEIVING_STATE)
00380         {
00381             delete frameBeingReceived;
00382             frameBeingReceived = NULL;
00383 
00384             numCollisions++;
00385             numCollisionsVector.record(numCollisions);
00386         }
00387 
00388         // go to collision state
00389         receiveState = RX_COLLISION_STATE;
00390     }
00391 }
00392 
00393 
00394 void EtherMAC::handleEndIFGPeriod()
00395 {
00396     EtherMACBase::handleEndIFGPeriod();
00397 
00398     // End of IFG period, okay to transmit, if Rx idle OR duplexMode
00399     cPacket *frame = (cPacket *)txQueue.front();
00400 
00401     // Perform carrier extension if in Gigabit Ethernet
00402     if (carrierExtension && frame->getByteLength() < GIGABIT_MIN_FRAME_WITH_EXT)
00403     {
00404         EV << "Performing carrier extension of small frame\n";
00405         frame->setByteLength(GIGABIT_MIN_FRAME_WITH_EXT);
00406     }
00407 
00408     // send frame to network
00409     startFrameTransmission();
00410 }
00411 
00412 void EtherMAC::startFrameTransmission()
00413 {
00414     cPacket *origFrame = (cPacket *)txQueue.front();
00415     EV << "Transmitting a copy of frame " << origFrame << endl;
00416     cPacket *frame = origFrame->dup();
00417 
00418     // add preamble and SFD (Starting Frame Delimiter), then send out
00419     frame->addByteLength(PREAMBLE_BYTES+SFD_BYTES);
00420     if (ev.isGUI())  updateConnectionColor(TRANSMITTING_STATE);
00421     send(frame, physOutGate);
00422 
00423     // update burst variables
00424     if (frameBursting)
00425     {
00426         bytesSentInBurst = frame->getByteLength();
00427         framesSentInBurst++;
00428     }
00429 
00430     // check for collisions (there might be an ongoing reception which we don't know about, see below)
00431     if (!duplexMode && receiveState!=RX_IDLE_STATE)
00432     {
00433         // During the IFG period the hardware cannot listen to the channel,
00434         // so it might happen that receptions have begun during the IFG,
00435         // and even collisions might be in progress.
00436         //
00437         // But we don't know of any ongoing transmission so we blindly
00438         // start transmitting, immediately collide and send a jam signal.
00439         //
00440         sendJamSignal();
00441         // numConcurrentTransmissions stays the same: +1 transmission, -1 jam
00442 
00443         if (receiveState==RECEIVING_STATE)
00444         {
00445             delete frameBeingReceived;
00446             frameBeingReceived = NULL;
00447 
00448             numCollisions++;
00449             numCollisionsVector.record(numCollisions);
00450         }
00451         // go to collision state
00452         receiveState = RX_COLLISION_STATE;
00453     }
00454     else
00455     {
00456         // no collision
00457         scheduleEndTxPeriod(frame);
00458 
00459         // only count transmissions in totalSuccessfulRxTxTime if channel is half-duplex
00460         if (!duplexMode)
00461             channelBusySince = simTime();
00462     }
00463 }
00464 
00465 void EtherMAC::handleEndTxPeriod()
00466 {
00467     EtherMACBase::handleEndTxPeriod();
00468 
00469     // only count transmissions in totalSuccessfulRxTxTime if channel is half-duplex
00470     if (!duplexMode)
00471     {
00472         simtime_t dt = simTime()-channelBusySince;
00473         totalSuccessfulRxTxTime += dt;
00474     }
00475 
00476     backoffs = 0;
00477 
00478     // check for and obey received PAUSE frames after each transmission
00479     if (checkAndScheduleEndPausePeriod())
00480         return;
00481 
00482     // Gigabit Ethernet: now decide if we transmit next frame right away (burst) or wait IFG
00483     // FIXME! this is not entirely correct, there must be IFG between burst frames too
00484     bool burstFrame=false;
00485     if (frameBursting && !txQueue.empty())
00486     {
00487         // check if max bytes for burst not exceeded
00488         if (bytesSentInBurst<GIGABIT_MAX_BURST_BYTES)
00489         {
00490              burstFrame=true;
00491              EV << "Transmitting next frame in current burst\n";
00492         }
00493         else
00494         {
00495              EV << "Next frame does not fit in current burst\n";
00496         }
00497     }
00498 
00499     if (burstFrame)
00500         startFrameTransmission();
00501     else
00502         beginSendFrames();
00503 }
00504 
00505 void EtherMAC::handleEndRxPeriod()
00506 {
00507     EV << "Frame reception complete\n";
00508     simtime_t dt = simTime()-channelBusySince;
00509     if (receiveState==RECEIVING_STATE) // i.e. not RX_COLLISION_STATE
00510     {
00511         EtherFrame *frame = frameBeingReceived;
00512         frameBeingReceived = NULL;
00513         frameReceptionComplete(frame);
00514         totalSuccessfulRxTxTime += dt;
00515     }
00516     else
00517     {
00518         totalCollisionTime += dt;
00519     }
00520 
00521     receiveState = RX_IDLE_STATE;
00522     numConcurrentTransmissions = 0;
00523 
00524     if (transmitState==TX_IDLE_STATE && !txQueue.empty())
00525     {
00526         EV << "Receiver now idle, can transmit frames in output buffer after IFG period\n";
00527         scheduleEndIFGPeriod();
00528     }
00529 }
00530 
00531 void EtherMAC::handleEndBackoffPeriod()
00532 {
00533     if (transmitState != BACKOFF_STATE)
00534         error("At end of BACKOFF not in BACKOFF_STATE!");
00535     if (txQueue.empty())
00536         error("At end of BACKOFF and buffer empty!");
00537 
00538     if (receiveState==RX_IDLE_STATE)
00539     {
00540         EV << "Backoff period ended, wait IFG\n";
00541         scheduleEndIFGPeriod();
00542     }
00543     else
00544     {
00545         EV << "Backoff period ended but channel not free, idling\n";
00546         transmitState = TX_IDLE_STATE;
00547     }
00548 }
00549 
00550 void EtherMAC::handleEndJammingPeriod()
00551 {
00552     if (transmitState != JAMMING_STATE)
00553         error("At end of JAMMING not in JAMMING_STATE!");
00554     EV << "Jamming finished, executing backoff\n";
00555     handleRetransmission();
00556 }
00557 
00558 void EtherMAC::sendJamSignal()
00559 {
00560     cPacket *jam = new EtherJam("JAM_SIGNAL");
00561     jam->setByteLength(JAM_SIGNAL_BYTES);
00562     if (ev.isGUI())  updateConnectionColor(JAMMING_STATE);
00563     send(jam, physOutGate);
00564 
00565     scheduleAt(simTime()+jamDuration, endJammingMsg);
00566     transmitState = JAMMING_STATE;
00567 }
00568 
00569 void EtherMAC::scheduleEndRxPeriod(cPacket *frame)
00570 {
00571     scheduleAt(simTime()+frame->getBitLength()*bitTime, endRxMsg);
00572     receiveState = RECEIVING_STATE;
00573 }
00574 
00575 void EtherMAC::handleRetransmission()
00576 {
00577     if (++backoffs > MAX_ATTEMPTS)
00578     {
00579         EV << "Number of retransmit attempts of frame exceeds maximum, cancelling transmission of frame\n";
00580         delete txQueue.pop();
00581 
00582         transmitState = TX_IDLE_STATE;
00583         backoffs = 0;
00584         // no beginSendFrames(), because end of jam signal sending will trigger it automatically
00585         return;
00586     }
00587 
00588     EV << "Executing backoff procedure\n";
00589     int backoffrange = (backoffs>=BACKOFF_RANGE_LIMIT) ? 1024 : (1 << backoffs);
00590     int slotNumber = intuniform(0,backoffrange-1);
00591     simtime_t backofftime = slotNumber*slotTime;
00592 
00593     scheduleAt(simTime()+backofftime, endBackoffMsg);
00594     transmitState = BACKOFF_STATE;
00595 
00596     numBackoffs++;
00597     numBackoffsVector.record(numBackoffs);
00598 }
00599 
00600 void EtherMAC::printState()
00601 {
00602 #define CASE(x) case x: EV << #x; break
00603     EV << "transmitState: ";
00604     switch (transmitState) {
00605         CASE(TX_IDLE_STATE);
00606         CASE(WAIT_IFG_STATE);
00607         CASE(TRANSMITTING_STATE);
00608         CASE(JAMMING_STATE);
00609         CASE(BACKOFF_STATE);
00610         CASE(PAUSE_STATE);
00611     }
00612     EV << ",  receiveState: ";
00613     switch (receiveState) {
00614         CASE(RX_IDLE_STATE);
00615         CASE(RECEIVING_STATE);
00616         CASE(RX_COLLISION_STATE);
00617     }
00618     EV << ",  backoffs: " << backoffs;
00619     EV << ",  numConcurrentTransmissions: " << numConcurrentTransmissions;
00620     EV << ",  queueLength: " << txQueue.length() << endl;
00621 #undef CASE
00622 }
00623 
00624 void EtherMAC::finish()
00625 {
00626     EtherMACBase::finish();
00627 
00628     simtime_t t = simTime();
00629     simtime_t totalChannelIdleTime = t - totalSuccessfulRxTxTime - totalCollisionTime;
00630     recordScalar("rx channel idle (%)", 100*(totalChannelIdleTime/t));
00631     recordScalar("rx channel utilization (%)", 100*(totalSuccessfulRxTxTime/t));
00632     recordScalar("rx channel collision (%)", 100*(totalCollisionTime/t));
00633     recordScalar("collisions",     numCollisions);
00634     recordScalar("backoffs",       numBackoffs);
00635 }
00636 
00637 void EtherMAC::updateHasSubcribers()
00638 {
00639     hasSubscribers = false;  // currently we don't fire any notifications
00640 }
00641