|
INET Framework for OMNeT++/OMNEST
|
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 "MACRelayUnitBase.h" 00019 #include "MACAddress.h" 00020 #include "EtherFrame_m.h" 00021 #include "Ethernet.h" 00022 00023 00024 #define MAX_LINE 100 00025 00026 00027 /* unused for now 00028 static std::ostream& operator<< (std::ostream& os, cMessage *msg) 00029 { 00030 os << "(" << msg->getClassName() << ")" << msg->getFullName(); 00031 return os; 00032 } 00033 */ 00034 00035 static std::ostream& operator<< (std::ostream& os, const MACRelayUnitBase::AddressEntry& e) 00036 { 00037 os << "port=" << e.portno << " insTime=" << e.insertionTime; 00038 return os; 00039 } 00040 00048 static char *fgetline (FILE *fp) 00049 { 00050 // alloc buffer and read a line 00051 char *line = new char[MAX_LINE]; 00052 if (fgets(line,MAX_LINE,fp)==NULL) 00053 return NULL; 00054 00055 // chop CR/LF 00056 line[MAX_LINE-1] = '\0'; 00057 int len = strlen(line); 00058 while (len>0 && (line[len-1]=='\n' || line[len-1]=='\r')) 00059 line[--len]='\0'; 00060 00061 return line; 00062 } 00063 00064 void MACRelayUnitBase::initialize() 00065 { 00066 // number of ports 00067 numPorts = gate("lowerLayerOut",0)->size(); 00068 if (gate("lowerLayerIn",0)->size()!=numPorts) 00069 error("the sizes of the lowerLayerIn[] and lowerLayerOut[] gate vectors must be the same"); 00070 00071 // other parameters 00072 addressTableSize = par("addressTableSize"); 00073 addressTableSize = addressTableSize >= 0 ? addressTableSize : 0; 00074 00075 agingTime = par("agingTime"); 00076 agingTime = agingTime > 0 ? agingTime : 10; 00077 00078 // Option to pre-read in Address Table. To turn ot off, set addressTableFile to empty string 00079 const char *addressTableFile = par("addressTableFile"); 00080 if (addressTableFile && *addressTableFile) 00081 readAddressTable(addressTableFile); 00082 00083 seqNum = 0; 00084 00085 WATCH_MAP(addresstable); 00086 } 00087 00088 void MACRelayUnitBase::handleAndDispatchFrame(EtherFrame *frame, int inputport) 00089 { 00090 // update address table 00091 updateTableWithAddress(frame->getSrc(), inputport); 00092 00093 // handle broadcast frames first 00094 if (frame->getDest().isBroadcast()) 00095 { 00096 EV << "Broadcasting broadcast frame " << frame << endl; 00097 broadcastFrame(frame, inputport); 00098 return; 00099 } 00100 00101 // Finds output port of destination address and sends to output port 00102 // if not found then broadcasts to all other ports instead 00103 int outputport = getPortForAddress(frame->getDest()); 00104 if (inputport==outputport) 00105 { 00106 EV << "Output port is same as input port, " << frame->getFullName() << 00107 " dest " << frame->getDest() << ", discarding frame\n"; 00108 delete frame; 00109 return; 00110 } 00111 if (outputport>=0) 00112 { 00113 EV << "Sending frame " << frame << " with dest address " << frame->getDest() << " to port " << outputport << endl; 00114 send(frame, "lowerLayerOut", outputport); 00115 } 00116 else 00117 { 00118 EV << "Dest address " << frame->getDest() << " unknown, broadcasting frame " << frame << endl; 00119 broadcastFrame(frame, inputport); 00120 } 00121 } 00122 00123 void MACRelayUnitBase::broadcastFrame(EtherFrame *frame, int inputport) 00124 { 00125 for (int i=0; i<numPorts; ++i) 00126 if (i!=inputport) 00127 send((EtherFrame*)frame->dup(), "lowerLayerOut", i); 00128 delete frame; 00129 } 00130 00131 void MACRelayUnitBase::printAddressTable() 00132 { 00133 AddressTable::iterator iter; 00134 EV << "Address Table (" << addresstable.size() << " entries):\n"; 00135 for (iter = addresstable.begin(); iter!=addresstable.end(); ++iter) 00136 { 00137 EV << " " << iter->first << " --> port" << iter->second.portno << 00138 (iter->second.insertionTime+agingTime <= simTime() ? " (aged)" : "") << endl; 00139 } 00140 } 00141 00142 void MACRelayUnitBase::removeAgedEntriesFromTable() 00143 { 00144 for (AddressTable::iterator iter = addresstable.begin(); iter != addresstable.end();) 00145 { 00146 AddressTable::iterator cur = iter++; // iter will get invalidated after erase() 00147 AddressEntry& entry = cur->second; 00148 if (entry.insertionTime + agingTime <= simTime()) 00149 { 00150 EV << "Removing aged entry from Address Table: " << 00151 cur->first << " --> port" << cur->second.portno << "\n"; 00152 addresstable.erase(cur); 00153 } 00154 } 00155 } 00156 00157 void MACRelayUnitBase::removeOldestTableEntry() 00158 { 00159 AddressTable::iterator oldest = addresstable.end(); 00160 simtime_t oldestInsertTime = simTime()+1; 00161 for (AddressTable::iterator iter = addresstable.begin(); iter != addresstable.end(); iter++) 00162 { 00163 if (iter->second.insertionTime < oldestInsertTime) 00164 { 00165 oldest = iter; 00166 oldestInsertTime = iter->second.insertionTime; 00167 } 00168 } 00169 if (oldest != addresstable.end()) 00170 { 00171 EV << "Table full, removing oldest entry: " << 00172 oldest->first << " --> port" << oldest->second.portno << "\n"; 00173 addresstable.erase(oldest); 00174 } 00175 } 00176 00177 void MACRelayUnitBase::updateTableWithAddress(MACAddress& address, int portno) 00178 { 00179 AddressTable::iterator iter; 00180 00181 iter = addresstable.find(address); 00182 if (iter == addresstable.end()) 00183 { 00184 // Observe finite table size 00185 if (addressTableSize!=0 && addresstable.size() == (unsigned int)addressTableSize) 00186 { 00187 // lazy removal of aged entries: only if table gets full (this step is not strictly needed) 00188 EV << "Making room in Address Table by throwing out aged entries.\n"; 00189 removeAgedEntriesFromTable(); 00190 00191 if (addresstable.size() == (unsigned int)addressTableSize) 00192 removeOldestTableEntry(); 00193 } 00194 00195 // Add entry to table 00196 EV << "Adding entry to Address Table: "<< address << " --> port" << portno << "\n"; 00197 AddressEntry entry; 00198 entry.portno = portno; 00199 entry.insertionTime = simTime(); 00200 addresstable[address] = entry; 00201 } 00202 else 00203 { 00204 // Update existing entry 00205 EV << "Updating entry in Address Table: "<< address << " --> port" << portno << "\n"; 00206 AddressEntry& entry = iter->second; 00207 entry.insertionTime = simTime(); 00208 entry.portno = portno; 00209 } 00210 } 00211 00212 int MACRelayUnitBase::getPortForAddress(MACAddress& address) 00213 { 00214 AddressTable::iterator iter = addresstable.find(address); 00215 if (iter == addresstable.end()) 00216 { 00217 // not found 00218 return -1; 00219 } 00220 if (iter->second.insertionTime + agingTime <= simTime()) 00221 { 00222 // don't use (and throw out) aged entries 00223 EV << "Ignoring and deleting aged entry: "<< iter->first << " --> port" << iter->second.portno << "\n"; 00224 addresstable.erase(iter); 00225 return -1; 00226 } 00227 return iter->second.portno; 00228 } 00229 00230 00231 void MACRelayUnitBase::readAddressTable(const char* fileName) 00232 { 00233 FILE *fp = fopen(fileName, "r"); 00234 if (fp == NULL) 00235 error("cannot open address table file `%s'", fileName); 00236 00237 // Syntax of the file goes as: 00238 // Address in hexadecimal representation, Portno 00239 // ffffffff 1 00240 // ffffeed1 2 00241 // aabcdeff 3 00242 // 00243 // etc... 00244 // 00245 // Each iteration of the loop reads in an entire line i.e. up to '\n' or EOF characters 00246 // and uses strtok to extract tokens from the resulting string 00247 char *line; 00248 int lineno = 0; 00249 while ((line = fgetline(fp)) != NULL) 00250 { 00251 lineno++; 00252 00253 // lines beginning with '#' are treated as comments 00254 if (line[0]=='#') 00255 continue; 00256 00257 // scan in hexaddress 00258 char *hexaddress = strtok(line, " \t"); 00259 // scan in port number 00260 char *portno = strtok(NULL, " \t"); 00261 00262 // empty line? 00263 if (!hexaddress) 00264 continue; 00265 00266 // broken line? 00267 if (!portno) 00268 error("line %d invalid in address table file `%s'", lineno, fileName); 00269 00270 // Create an entry with address and portno and insert into table 00271 AddressEntry entry; 00272 entry.insertionTime = 0; 00273 entry.portno = atoi(portno); 00274 addresstable[MACAddress(hexaddress)] = entry; 00275 00276 // Garbage collection before next iteration 00277 delete [] line; 00278 } 00279 fclose(fp); 00280 } 00281 00282 00283 void MACRelayUnitBase::sendPauseFrame(int portno, int pauseUnits) 00284 { 00285 EV << "Creating and sending PAUSE frame on port " << portno << " with duration=" << pauseUnits << " units\n"; 00286 00287 // create Ethernet frame 00288 char framename[40]; 00289 sprintf(framename, "pause-%d-%d", getId(), seqNum++); 00290 EtherPauseFrame *frame = new EtherPauseFrame(framename); 00291 frame->setPauseTime(pauseUnits); 00292 00293 frame->setByteLength(ETHER_MAC_FRAME_BYTES+ETHER_PAUSE_COMMAND_BYTES); 00294 if (frame->getByteLength() < MIN_ETHERNET_FRAME) 00295 frame->setByteLength(MIN_ETHERNET_FRAME); 00296 00297 send(frame, "lowerLayerOut", portno); 00298 } 00299