INET Framework for OMNeT++/OMNEST
stpi.cc
Go to the documentation of this file.
00001 /*
00002  * stpInstance.cpp
00003  *
00004  *  Created on: 16.5.2011
00005  *      Author: aranel
00006  */
00007 
00008 #include "stpi.h"
00009 #include "STPMACCompare.h"
00010 
00011 stpi::stpi(unsigned int _vlan, unsigned int _portCount, MACAddress _bridgeAddress, MACTable * _addrTable) {
00012 
00013         portCount = _portCount;
00014         bridgeAddress = _bridgeAddress; // apply the bridge address
00015         vlan = _vlan;
00016         addrTable = _addrTable;
00017         initPortTable();
00018 
00019         helloTime = 2;
00020         maxAge = 20;
00021         fwdDelay = 15;
00022         bridgePriority = 32768;
00023         ubridgePriority = bridgePriority;
00024 
00025         isRoot = true;
00026         topologyChange = 0;
00027         topologyChangeNotification = false;
00028         topologyChangeRecvd = false;
00029 
00030         rootPriority = bridgePriority;
00031         rootID = bridgeAddress;
00032         rootPathCost = 0;
00033         rootPort = 0;
00034         cHelloTime = helloTime;
00035         cMaxAge = maxAge;
00036         cFwdDelay = fwdDelay;
00037 
00038 
00039         helloTimer = 0;
00040 
00041 
00042         allDesignated();
00043 
00044 }
00045 
00046 stpi::~stpi() {
00047 
00048 }
00049 
00050 void stpi::send(cMessage * _msg, unsigned int _port) {
00051         tMsg tmp;
00052         tmp.port = _port;
00053         tmp.msg = _msg;
00054         msgList.push_back(tmp);
00055 }
00056 
00057 
00058 
00059 /* dummy records to portTable to be learned */
00060 void stpi::initPortTable() {
00061         tPortFlags flag;
00062         flag.Forwarding = false;
00063         flag.Learning = false;
00064 
00065         tPort port;
00066 
00067         port.enabled = true;
00068         port.state = SDISCARDING;
00069         port.role = RUNKNOWN;
00070         port.priority = 128;
00071         port.flags = flag;
00072         port.rootPathCost = INT16_MAX;
00073         port.rootPriority = 65536;
00074         port.rootID = MACAddress("FF-FF-FF-FF-FF-FF");
00075         port.bridgePriority = 65536;
00076         port.bridgeID = MACAddress("FF-FF-FF-FF-FF-FF");
00077         port.portPriority = 256;
00078         port.portID = 256;
00079         port.age = 0;
00080         port.fdWhile = 0;
00081         port.maxAge = 20;
00082         port.fwdDelay = 15;
00083         port.helloTime = 2;
00084         port.linkCost = 19;
00085 
00086         defaultPort = port;
00087 
00088         portTable.insert(portTable.begin(), portCount, port);
00089 
00090 }
00091 
00092 void stpi::handleMessage(cMessage *msg)
00093 {
00094         cMessage * tmp = msg;
00095 
00096         if (!msg->isSelfMessage()) {
00097                 if (dynamic_cast<STPBPDU *>(tmp)){ // Configuration BPDU
00098                         STPBPDU * bpdu = (STPBPDU *) tmp;
00099                         handleBPDU(bpdu);
00100                 } else if (dynamic_cast<STPTCN *>(tmp)){ // Topology Change Notification
00101                         STPTCN * tcn = (STPTCN *) tmp;
00102                         handleTCN(tcn);
00103                 } else { // Rubbish
00104                         delete msg;
00105                 }
00106         } else { // self rubbish
00107                 delete msg;
00108         }
00109 }
00110 
00111 bool stpi::learning(unsigned int port) {
00112         return portTable.at(port).flags.Learning;
00113 }
00114 
00115 bool stpi::forwarding(unsigned int port) {
00116         return portTable.at(port).flags.Forwarding;
00117 }
00118 
00119 void stpi::handleBPDU(STPBPDU * bpdu) {
00120 
00121         /* get inferior BPDU, reply with superior */
00122         if (superiorBPDU(bpdu->getArrivalGate()->getIndex(), bpdu) == false) {
00123                 /* ONLY on designated port, because of flapping old root bridge information
00124                  * between bridges, when only parameters changed
00125                  */
00126                 if (portTable.at(bpdu->getArrivalGate()->getIndex()).role == RDESIGNATED) {
00127                         generateHelloBPDU(bpdu->getArrivalGate()->getIndex());
00128                 }
00129         }
00130         /* relay BPDU from root */
00131         else if (portTable.at(bpdu->getArrivalGate()->getIndex()).role == RROOT) {
00132 
00133                 if (bpdu->getTca()) {
00134                         topologyChangeNotification = false;
00135                 }
00136                 if (bpdu->getTc()) {
00137                         topologyChange++;
00138                     addrTable->enableFasterAging();
00139                 } else {
00140                     addrTable->resetAging();
00141                 }
00142 
00143                 for (unsigned int i = 0; i < desPorts.size(); i++) {
00144                         generateHelloBPDU(desPorts.at(i));
00145                 }
00146                 if (topologyChangeRecvd) { // TCA with BPDUs
00147                         topologyChangeRecvd = false;
00148                 }
00149                 if (topologyChange > 0) {
00150                         topologyChange--;
00151                 }
00152         }
00153 
00154 
00155         /* --- BEGIN RUBBISH --- */
00156 
00157         tryRoot();
00158 
00159         /* --- END OF RUBBISH --- */
00160 
00161         delete bpdu;
00162 }
00163 
00164 void stpi::handleTCN(STPTCN * tcn) {
00165         topologyChangeNotification = true;
00166         topologyChangeRecvd = true;
00167         //delete tcn;
00168 }
00169 
00170 
00171 
00172 void stpi::generateHelloBPDU(int port) {
00173         STPBPDU * bpdu = new STPBPDU("BPDU", vlan);
00174         bpdu->setVlan(vlan);
00175         bpdu->setPortRole(stpi::RDESIGNATED);
00176         bpdu->setBridgeID(bridgeAddress);
00177         bpdu->setBridgePriority(bridgePriority);
00178         bpdu->setRootPathCost(rootPathCost);
00179         bpdu->setRootID(rootID);
00180         bpdu->setRootPriority(rootPriority);
00181         bpdu->setPortID(port);
00182         bpdu->setPortPriority(portTable.at(port).priority);
00183         bpdu->setMsgAge(0);
00184         bpdu->setMaxAge(cMaxAge);
00185         bpdu->setHelloTime(cHelloTime);
00186         bpdu->setFwdDelay(cFwdDelay);
00187 
00188 
00189 
00190         if (topologyChangeRecvd) {
00191                 bpdu->setTca(true);
00192         } else {
00193                 bpdu->setTca(false);
00194         }
00195         if (topologyChange > 0) {
00196                  /* lowering value is in reply to root bpdu,
00197                   * for root in generator
00198                   */
00199                 bpdu->setTc(true);
00200         } else {
00201                 bpdu->setTc(false);
00202         }
00203 
00204         send(bpdu, port);
00205 }
00206 
00207 void stpi::generateTCN() {
00208         if (topologyChangeNotification) { // is something to notify
00209                 if (portTable.at(rootPort).role == RROOT) { // exist root port to notifying
00210                         STPTCN * tcn = new STPTCN("TCN", vlan);
00211                         tcn->setVlan(vlan);
00212                         send(tcn, rootPort);
00213                 }
00214         }
00215 
00216 }
00217 
00218 /* check of the received BPDU is superior to port information from porttable */
00219 bool stpi::superiorBPDU(int p, STPBPDU * bpdu) {
00220         tPort port = portTable.at(p);
00221         tPort xbpdu;
00222         int result;
00223 
00224         xbpdu.rootPriority = bpdu->getRootPriority();
00225         xbpdu.rootID = bpdu->getRootID();
00226         xbpdu.rootPathCost = bpdu->getRootPathCost() + port.linkCost;
00227         xbpdu.bridgePriority = bpdu->getBridgePriority();
00228         xbpdu.bridgeID = bpdu->getBridgeID();
00229         xbpdu.portPriority = bpdu->getPortPriority();
00230         xbpdu.portID = bpdu->getPortID();
00231 
00232         result = superiorTPort(port, xbpdu);
00233 
00234         if (result > 0) { // port is superior
00235                 return false;
00236         }
00237         if (result < 0) { // BPDU is superior
00238                 resetFDWhile(p); // renew info
00239                 setPortState(p, SDISCARDING);
00240                 setSuperiorBPDU(p, bpdu); // renew information
00241                 return true;
00242         }
00243 
00244         setSuperiorBPDU(p, bpdu); // renew information
00245         return true;
00246 }
00247 
00248 /* set all new information to the port, or renew old one, eg. reset timer */
00249 void stpi::setSuperiorBPDU(int port, STPBPDU * bpdu) {
00250         if (bpdu->getMsgAge() >= bpdu->getMaxAge()) {
00251                 return;
00252         }
00253 
00254         portTable.at(port).rootPriority = bpdu->getRootPriority();
00255         portTable.at(port).rootID = bpdu->getRootID();
00256         portTable.at(port).rootPathCost = bpdu->getRootPathCost() + portTable.at(port).linkCost;
00257         portTable.at(port).bridgePriority = bpdu->getBridgePriority();
00258         portTable.at(port).bridgeID = bpdu->getBridgeID();
00259         portTable.at(port).portPriority = bpdu->getPortPriority();
00260         portTable.at(port).portID = bpdu->getPortID();
00261 
00262         portTable.at(port).maxAge = bpdu->getMaxAge();
00263         portTable.at(port).fwdDelay = bpdu->getFwdDelay();
00264         portTable.at(port).helloTime = bpdu->getHelloTime();
00265 
00266 
00267         resetAge(port); // renew info
00268 
00269 }
00270 
00271 void stpi::generator() {
00272         if (!isRoot) {
00273                 return;
00274         }
00275         for (unsigned int i = 0; i < portCount; i++) {
00276                 generateHelloBPDU(i);
00277         }
00278         if (topologyChangeRecvd) { // TCA with BPDUs
00279                 topologyChangeRecvd = false;
00280         }
00281         if (topologyChange > 0) {
00282                 addrTable->enableFasterAging();
00283                 topologyChange--;
00284         } else {
00285                 addrTable->resetAging();
00286         }
00287 }
00288 
00289 
00290 void stpi::handleTick() {
00291         /* BRIDGE TIMERS */
00292         if (isRoot) {
00293                 helloTimer++;
00294         } else {
00295                 helloTimer = 0;
00296         }
00297 
00298         for (unsigned int i = 0; i < portCount; i++) { // PORT TIMERS
00299                 // DISABLED ports is not operational
00300                 if (portTable.at(i).enabled == false) {
00301                         continue;
00302                 }
00303 
00304                 // designated port's origins informations to LAN,
00305                 // cannot aged out yourself
00306                 if (portTable.at(i).role != RDESIGNATED) {
00307                         portTable.at(i).age++;
00308                 }
00309                 if (portTable.at(i).role == RROOT || portTable.at(i).role == RDESIGNATED) {
00310                         portTable.at(i).fdWhile++;
00311                 }
00312         }
00313 
00314         checkTimers();
00315         checkParametersChange();
00316 
00317         generateTCN(); // if something to notify
00318 }
00319 
00320 void stpi::checkTimers() {
00321 
00322         /* Hello timer check */
00323         if (helloTimer >= cHelloTime) {
00324                 generator();
00325                 helloTimer = 0;
00326         }
00327 
00328         /* information age check */
00329         for (unsigned int i = 0; i < portCount; i++) {
00330                 if (portTable.at(i).age >= cMaxAge) {
00331                         if (portTable.at(i).role == RROOT) {
00332                                 portTable.at(i) = defaultPort;
00333                                 lostRoot();
00334                         } else {
00335                                 portTable.at(i) = defaultPort;
00336                                 lostAlternate(i);
00337                         }
00338                 }
00339         }
00340 
00341 
00342         /* fdWhile timer */
00343         for (unsigned int i = 0; i < portCount; i++) {
00344                 /* ROOT / DESIGNATED, can transition */
00345                 if (portTable.at(i).role == RROOT || portTable.at(i).role == RDESIGNATED) {
00346                         if (portTable.at(i).fdWhile >= cFwdDelay) {
00347                                 switch (portTable.at(i).state) {
00348                                 case SDISABLED:
00349                                         setPortState(i, SDISCARDING);
00350                                         resetFDWhile(i);
00351                                         break;
00352                                 case SDISCARDING:
00353                                         setPortState(i, SLEARNING);
00354                                         resetFDWhile(i);
00355                                         break;
00356                                 case SLEARNING:
00357                                         setPortState(i, SFORWARDING);
00358                                         resetFDWhile(i);
00359                                         break;
00360                                 default:
00361                                         resetFDWhile(i);
00362                                         break;
00363                                 }
00364 
00365                         }
00366                 } else {
00367                         resetFDWhile(i);
00368                         setPortState(i, SDISCARDING);
00369                 }
00370         }
00371 
00372         /* TOPOLOGY CHANGE HANDLING */
00373         if (topologyChangeNotification == true) {
00374                 if (isRoot == true) {
00375                         topologyChange = 5;
00376                         topologyChangeNotification = false;
00377                 }
00378         }
00379 
00380 }
00381 
00382 void stpi::checkParametersChange() {
00383         if (isRoot) {
00384                 cHelloTime = helloTime;
00385                 cMaxAge = maxAge;
00386                 cFwdDelay = fwdDelay;
00387         }
00388         if (ubridgePriority != bridgePriority) {
00389                 ubridgePriority = bridgePriority;
00390                 reset();
00391         }
00392 }
00393 
00394 
00395 void stpi::resetAge(int p) {
00396         portTable.at(p).age = 0;
00397 }
00398 
00399 void stpi::resetFDWhile(int p) {
00400         portTable.at(p).fdWhile = 0;
00401 }
00402 
00403 bool stpi::checkRootEligibility() {
00404         for (unsigned int i = 0; i < portCount; i++) {
00405                 if (superiorID(portTable.at(i).rootPriority, portTable.at(i).rootID,
00406                                 bridgePriority, bridgeAddress) > 0) {
00407                         /* port information  is superior to bridge ID, is not root eligible */
00408                         return false;
00409                 }
00410         }
00411         return true;
00412 }
00413 
00414 void stpi::tryRoot() {
00415         if (checkRootEligibility() == false) {
00416                 isRoot = false;
00417 //              EV << this->getParentModule()->getFullName() << ": not eligible to be root!" << std::endl;
00418                 selectRootPort();
00419                 selectDesignatedPorts();
00420         } else {
00421 
00422 //              EV << this->getParentModule()->getFullName() << ": Iam root!" << std::endl;
00423                 isRoot = true;
00424                 allDesignated();
00425                 rootPriority = bridgePriority;
00426                 rootID = bridgeAddress;
00427                 rootPathCost = 0;
00428                 cHelloTime = helloTime;
00429                 cMaxAge = maxAge;
00430                 cFwdDelay = fwdDelay;
00431         }
00432 
00433 
00434 }
00435 
00436 int stpi::superiorID(unsigned int APR, MACAddress AID, unsigned int BPR, MACAddress BID) {
00437         if (APR < BPR) {
00438                 return 1; // A is superior
00439         } else if (APR > BPR) {
00440                 return -1;
00441         }
00442 
00443         // APR == BPR
00444         if (AID < BID) {
00445                 return 1; // A is superior
00446         } else if (AID > BID) {
00447                 return -1;
00448         }
00449 
00450         /* A==B
00451          * (can happen if bridge have two port connected to one not bridged lan,
00452          * "cable loopback")
00453          */
00454         return 0;
00455 }
00456 
00457 int stpi::superiorPort(unsigned int APR, unsigned int AID, unsigned int BPR, unsigned int BID) {
00458         if (APR < BPR) {
00459                 return 1; // A is superior
00460         } else if (APR > BPR) {
00461                 return -1;
00462         }
00463 
00464         // APR == BPR
00465         if (AID < BID) {
00466                 return 1; // A is superior
00467         } else if (AID > BID) {
00468                 return -1;
00469         }
00470 
00471         /* A==B */
00472         return 0;
00473 }
00474 
00475 
00476 
00477 int stpi::superiorTPort(tPort A, tPort B) {
00478         int result;
00479         /* ROOT COMPARSION */
00480         result = superiorID(A.rootPriority, A.rootID,
00481                                     B.rootPriority, B.rootID);
00482         if (result != 0){ // not same, so pass result
00483                 return result;
00484         }
00485 
00486         /* PATH COST */
00487         if (A.rootPathCost < B.rootPathCost) {
00488                 return 1;
00489         }
00490         if (A.rootPathCost > B.rootPathCost) {
00491                 return -1;
00492         }
00493 
00494         /* DESIGNATED BRIDGE */
00495         result = superiorID(A.bridgePriority, A.bridgeID,
00496                                     B.bridgePriority, B.bridgeID);
00497         if (result != 0){ // not same, so pass result
00498                 return result;
00499         }
00500 
00501 
00502         /* DESIGNATED PORT OF DESIGNATED BRIDGE*/
00503         result = superiorPort(A.portPriority, A.portID,
00504                                                  B.portPriority, B.portID);
00505         if (result != 0){ // not same, so pass result
00506                 return result;
00507         }
00508 
00509         return 0; // same
00510 }
00511 
00512 void stpi::selectRootPort() {
00513         unsigned int xRootPort = 0;
00514         int result;
00515         tPort best = defaultPort; // can be superseded by all real tPort structures
00516         tPort tmp;
00517 
00518         for (unsigned int i = 0; i < portCount; i++) {
00519                 tmp = portTable.at(i);
00520                 portTable.at(i).role = RUNKNOWN;
00521                 result = superiorTPort(tmp, best);
00522                 if (result > 0) { // new root port
00523                         xRootPort = i;
00524                         best = tmp;
00525                         continue;
00526                 }
00527                 if (result < 0) { // inferior information
00528                         continue;
00529                 }
00530                 /* same info on tPort */
00531                 if (tmp.priority < best.priority) { // better priority
00532                         xRootPort = i;
00533                         best = tmp;
00534                         continue;
00535                 }
00536                 if (tmp.priority > best.priority) { // inferior priority
00537                         continue;
00538                 }
00539                 /* ALL IS THE SAME, index of port resolving stalemate,
00540                  * because of for cycle is the existing xRootPort lower
00541                  * so it' remains */
00542                 continue;
00543 
00544         }
00545         if (rootPort != xRootPort) {
00546                 topologyChangeNotification = true;
00547         }
00548 
00549         rootPort = xRootPort;
00550         portTable.at(rootPort).role = RROOT;
00551 
00552         rootPathCost = best.rootPathCost;
00553         rootID = best.rootID;
00554         rootPriority = best.rootPriority;
00555 
00556         cMaxAge = best.maxAge;
00557         cFwdDelay = best.fwdDelay;
00558         cHelloTime = best.helloTime;
00559 
00560 }
00561 
00562 /* select designated ports */
00563 void stpi::selectDesignatedPorts() {
00564 /* designated is the best with same Root, OR inferior root */
00565         std::vector<unsigned int> xDesPorts;
00566         tPort tmp;
00567         tPort bridge; // current bridge information
00568         int result;
00569 
00570         bridge.bridgePriority = bridgePriority;
00571         bridge.bridgeID = bridgeAddress;
00572         bridge.rootID = rootID;
00573         bridge.rootPriority = rootPriority;
00574 
00575 
00576 
00577         for (unsigned int i = 0; i < portCount; i++) {
00578                 tmp = portTable.at(i);
00579 
00580                 if (tmp.role == RROOT) {
00581                         continue;
00582                 }
00583                 if (tmp.enabled == false) {
00584                         continue;
00585                 }
00586 
00587                 bridge.portPriority = portTable.at(i).priority;
00588                 bridge.portID = i;
00589 
00590                 bridge.rootPathCost = rootPathCost + portTable.at(i).linkCost;
00591 
00592                 if (superiorID(rootPriority, rootID, tmp.rootPriority, tmp.rootID) < 0) {
00593         //              error("cannot be superior Root on port !");
00594                 }
00595         //      EV << this->getFullName() << "I:" << bridge << " ? " << tmp << std::endl;
00596                 result = superiorTPort(bridge, tmp);
00597                 if (result > 0) {
00598                         xDesPorts.push_back(i);
00599                         portTable.at(i).role = RDESIGNATED;
00600                         continue;
00601                 }
00602                 if (result < 0) {
00603                         portTable.at(i).role = RALTERNATE;
00604                         continue;
00605                 }
00606         //      error("cannot be the same !");
00607 
00608         }
00609 
00610         desPorts.clear();
00611         desPorts = xDesPorts;
00612 
00613 }
00614 
00615 void stpi::allDesignated() {
00616         std::vector<unsigned int> xDesPorts;
00617         for (unsigned int i = 0; i < portCount; i++) {
00618                 if (portTable.at(i).enabled == false) {
00619                         continue;
00620                 }
00621                 xDesPorts.push_back(i);
00622                 portTable.at(i).role = RDESIGNATED;
00623         }
00624         desPorts.clear();
00625         desPorts = xDesPorts;
00626 }
00627 
00628 
00629 /* neighbor lost handling */
00630 void stpi::lostRoot() {
00631         topologyChangeNotification = true; // information for bridge, that topology change is commited
00632 //      EV << this->getParentModule()->getFullName() << ": ROOT IS DOWN!" << std::endl;
00633         tryRoot();
00634 
00635 }
00636 
00637 void stpi::lostAlternate(int port) {
00638         selectDesignatedPorts();
00639         topologyChangeNotification = true;
00640 }
00641 
00642 void stpi::reset() {
00643         isRoot = true;
00644         rootPriority = bridgePriority;
00645         rootID = bridgeAddress;
00646         rootPathCost = 0;
00647         cHelloTime = helloTime;
00648         cMaxAge = maxAge;
00649         cFwdDelay = fwdDelay;
00650         allDesignated();
00651 
00652         for (unsigned int i = 0; i < portCount; i++) {
00653                 portTable.at(i) = defaultPort;
00654         }
00655 }
00656 
00657 void stpi::setPortState(unsigned int i, tPortState state) {
00658         portTable.at(i).state = state;
00659 
00660         switch (state) {
00661         case SDISABLED:
00662         case SDISCARDING:
00663                 portTable.at(i).flags.Learning = false;
00664                 portTable.at(i).flags.Forwarding = false;
00665                 break;
00666         case SLEARNING:
00667                 portTable.at(i).flags.Learning = true;
00668                 portTable.at(i).flags.Forwarding = false;
00669                 break;
00670         case SFORWARDING:
00671                 portTable.at(i).flags.Learning = true;
00672                 portTable.at(i).flags.Forwarding = true;
00673                 break;
00674         default:
00675                 break;
00676         }
00677 
00678 }
00679