ARP.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 "ARP.h"
00020 #include "IPv4InterfaceData.h"
00021 #include "Ieee802Ctrl_m.h"
00022 
00023 
00024 static std::ostream& operator<< (std::ostream& out, cMessage *msg)
00025 {
00026     out << "(" << msg->getClassName() << ")" << msg->getFullName();
00027     return out;
00028 }
00029 
00030 static std::ostream& operator<< (std::ostream& out, const ARP::ARPCacheEntry& e)
00031 {
00032     if (e.pending)
00033         out << "pending (" << e.numRetries << " retries)";
00034     else
00035         out << "MAC:" << e.macAddress << "  age:" << floor(simTime()-e.lastUpdate) << "s";
00036     return out;
00037 }
00038 
00039 
00040 Define_Module (ARP);
00041 
00042 void ARP::initialize()
00043 {
00044     ift = InterfaceTableAccess().get();
00045     rt = RoutingTableAccess().get();
00046 
00047     nicOutBaseGateId = gateSize("nicOut")==0 ? -1 : gate("nicOut",0)->getId();
00048 
00049     retryTimeout = par("retryTimeout");
00050     retryCount = par("retryCount");
00051     cacheTimeout = par("cacheTimeout");
00052     doProxyARP = par("proxyARP");
00053 
00054     pendingQueue.setName("pendingQueue");
00055 
00056     // init statistics
00057     numRequestsSent = numRepliesSent = 0;
00058     numResolutions = numFailedResolutions = 0;
00059     WATCH(numRequestsSent);
00060     WATCH(numRepliesSent);
00061     WATCH(numResolutions);
00062     WATCH(numFailedResolutions);
00063 
00064     WATCH_PTRMAP(arpCache);
00065 }
00066 
00067 void ARP::finish()
00068 {
00069     recordScalar("ARP requests sent", numRequestsSent);
00070     recordScalar("ARP replies sent", numRepliesSent);
00071     recordScalar("ARP resolutions", numResolutions);
00072     recordScalar("failed ARP resolutions", numFailedResolutions);
00073 }
00074 
00075 ARP::~ARP()
00076 {
00077     while (!arpCache.empty())
00078     {
00079         ARPCache::iterator i = arpCache.begin();
00080         delete (*i).second;
00081         arpCache.erase(i);
00082     }
00083 }
00084 
00085 void ARP::handleMessage(cMessage *msg)
00086 {
00087     if (msg->isSelfMessage())
00088     {
00089         requestTimedOut(msg);
00090     }
00091     else if (dynamic_cast<ARPPacket *>(msg))
00092     {
00093         ARPPacket *arp = (ARPPacket *)msg;
00094         processARPPacket(arp);
00095     }
00096     else // not ARP
00097     {
00098         processOutboundPacket(msg);
00099     }
00100     if (ev.isGUI())
00101         updateDisplayString();
00102 }
00103 
00104 void ARP::updateDisplayString()
00105 {
00106     std::stringstream os;
00107 
00108     os << arpCache.size() << " cache entries\nsent req:" << numRequestsSent
00109             << " repl:" << numRepliesSent << " fail:" << numFailedResolutions;
00110     getDisplayString().setTagArg("t", 0, os.str().c_str());
00111 }
00112 
00113 void ARP::processOutboundPacket(cMessage *msg)
00114 {
00115     EV << "Packet " << msg << " arrived from higher layer, ";
00116 
00117     // get next hop address from control info in packet
00118     IPRoutingDecision *controlInfo = check_and_cast<IPRoutingDecision*>(msg->removeControlInfo());
00119     IPAddress nextHopAddr = controlInfo->getNextHopAddr();
00120     InterfaceEntry *ie = ift->getInterfaceById(controlInfo->getInterfaceId());
00121     delete controlInfo;
00122 
00123     // if output interface is not broadcast, don't bother with ARP
00124     if (!ie->isBroadcast())
00125     {
00126         EV << "output interface " << ie->getName() << " is not broadcast, skipping ARP\n";
00127         send(msg, nicOutBaseGateId + ie->getNetworkLayerGateIndex());
00128         return;
00129     }
00130 
00131     // determine what address to look up in ARP cache
00132     if (!nextHopAddr.isUnspecified())
00133     {
00134         EV << "using next-hop address " << nextHopAddr << "\n";
00135     }
00136     else
00137     {
00138         // try proxy ARP
00139         IPDatagram *datagram = check_and_cast<IPDatagram *>(msg);
00140         nextHopAddr = datagram->getDestAddress();
00141         EV << "no next-hop address, using destination address " << nextHopAddr << " (proxy ARP)\n";
00142     }
00143 
00144     //
00145     // Handle multicast IP addresses. RFC 1112, section 6.4 says:
00146     // "An IP host group address is mapped to an Ethernet multicast address
00147     // by placing the low-order 23 bits of the IP address into the low-order
00148     // 23 bits of the Ethernet multicast address 01-00-5E-00-00-00 (hex).
00149     // Because there are 28 significant bits in an IP host group address,
00150     // more than one host group address may map to the same Ethernet multicast
00151     // address."
00152     //
00153     if (nextHopAddr.isMulticast())
00154     {
00155         // FIXME: we do a simpler solution right now: send to the Broadcast MAC address
00156         EV << "destination address is multicast, sending packet to broadcast MAC address\n";
00157         static MACAddress broadcastAddr("FF:FF:FF:FF:FF:FF");
00158         sendPacketToNIC(msg, ie, broadcastAddr);
00159         return;
00160 #if 0
00161         // experimental RFC 1112 code
00162         // TBD needs counterpart to be implemented in EtherMAC processReceivedDataFrame().
00163         unsigned char macBytes[6];
00164         macBytes[0] = 0x01;
00165         macBytes[1] = 0x00;
00166         macBytes[2] = 0x5e;
00167         macBytes[3] = nextHopAddr.getDByte(1) & 0x7f;
00168         macBytes[4] = nextHopAddr.getDByte(2);
00169         macBytes[5] = nextHopAddr.getDByte(3);
00170         MACAddress multicastMacAddr;
00171         multicastMacAddr.setAddressBytes(bytes);
00172         sendPacketToNIC(msg, ie, multicastMacAddr);
00173         return;
00174 #endif
00175     }
00176 
00177     // try look up
00178     ARPCache::iterator it = arpCache.find(nextHopAddr);
00179     //ASSERT(it==arpCache.end() || ie==(*it).second->ie); // verify: if arpCache gets keyed on InterfaceEntry* too, this becomes unnecessary
00180     if (it==arpCache.end())
00181     {
00182         // no cache entry: launch ARP request
00183         ARPCacheEntry *entry = new ARPCacheEntry();
00184         ARPCache::iterator where = arpCache.insert(arpCache.begin(), std::make_pair(nextHopAddr,entry));
00185         entry->myIter = where; // note: "inserting a new element into a map does not invalidate iterators that point to existing elements"
00186         entry->ie = ie;
00187 
00188         EV << "Starting ARP resolution for " << nextHopAddr << "\n";
00189         initiateARPResolution(entry);
00190 
00191         // and queue up packet
00192         entry->pendingPackets.push_back(msg);
00193         pendingQueue.insert(msg);
00194     }
00195     else if ((*it).second->pending)
00196     {
00197         // an ARP request is already pending for this address -- just queue up packet
00198         EV << "ARP resolution for " << nextHopAddr << " is pending, queueing up packet\n";
00199         (*it).second->pendingPackets.push_back(msg);
00200         pendingQueue.insert(msg);
00201     }
00202     else if ((*it).second->lastUpdate+cacheTimeout<simTime())
00203     {
00204         EV << "ARP cache entry for " << nextHopAddr << " expired, starting new ARP resolution\n";
00205 
00206         // cache entry stale, send new ARP request
00207         ARPCacheEntry *entry = (*it).second;
00208         entry->ie = ie; // routing table may have changed
00209         initiateARPResolution(entry);
00210 
00211         // and queue up packet
00212         entry->pendingPackets.push_back(msg);
00213         pendingQueue.insert(msg);
00214     }
00215     else
00216     {
00217         // valid ARP cache entry found, flag msg with MAC address and send it out
00218         EV << "ARP cache hit, MAC address for " << nextHopAddr << " is " << (*it).second->macAddress << ", sending packet down\n";
00219         sendPacketToNIC(msg, ie, (*it).second->macAddress);
00220     }
00221 }
00222 
00223 void ARP::initiateARPResolution(ARPCacheEntry *entry)
00224 {
00225     IPAddress nextHopAddr = entry->myIter->first;
00226     entry->pending = true;
00227     entry->numRetries = 0;
00228     entry->lastUpdate = 0;
00229     sendARPRequest(entry->ie, nextHopAddr);
00230 
00231     // start timer
00232     cMessage *msg = entry->timer = new cMessage("ARP timeout");
00233     msg->setContextPointer(entry);
00234     scheduleAt(simTime()+retryTimeout, msg);
00235 
00236     numResolutions++;
00237 }
00238 
00239 void ARP::sendPacketToNIC(cMessage *msg, InterfaceEntry *ie, const MACAddress& macAddress)
00240 {
00241     // add control info with MAC address
00242     Ieee802Ctrl *controlInfo = new Ieee802Ctrl();
00243     controlInfo->setDest(macAddress);
00244     msg->setControlInfo(controlInfo);
00245 
00246     // send out
00247     send(msg, nicOutBaseGateId + ie->getNetworkLayerGateIndex());
00248 }
00249 
00250 void ARP::sendARPRequest(InterfaceEntry *ie, IPAddress ipAddress)
00251 {
00252     // find our own IP address and MAC address on the given interface
00253     MACAddress myMACAddress = ie->getMacAddress();
00254     IPAddress myIPAddress = ie->ipv4Data()->getIPAddress();
00255 
00256     // both must be set
00257     ASSERT(!myMACAddress.isUnspecified());
00258     ASSERT(!myIPAddress.isUnspecified());
00259 
00260     // fill out everything in ARP Request packet except dest MAC address
00261     ARPPacket *arp = new ARPPacket("arpREQ");
00262     arp->setByteLength(ARP_HEADER_BYTES);
00263     arp->setOpcode(ARP_REQUEST);
00264     arp->setSrcMACAddress(myMACAddress);
00265     arp->setSrcIPAddress(myIPAddress);
00266     arp->setDestIPAddress(ipAddress);
00267 
00268     static MACAddress broadcastAddress("ff:ff:ff:ff:ff:ff");
00269     sendPacketToNIC(arp, ie, broadcastAddress);
00270     numRequestsSent++;
00271 }
00272 
00273 void ARP::requestTimedOut(cMessage *selfmsg)
00274 {
00275     ARPCacheEntry *entry = (ARPCacheEntry *)selfmsg->getContextPointer();
00276     entry->numRetries++;
00277     if (entry->numRetries < retryCount)
00278     {
00279         // retry
00280         IPAddress nextHopAddr = entry->myIter->first;
00281         EV << "ARP request for " << nextHopAddr << " timed out, resending\n";
00282         sendARPRequest(entry->ie, nextHopAddr);
00283         scheduleAt(simTime()+retryTimeout, selfmsg);
00284         return;
00285     }
00286 
00287     // max retry count reached: ARP failure.
00288     // throw out entry from cache, delete pending messages
00289     MsgPtrVector& pendingPackets = entry->pendingPackets;
00290     EV << "ARP timeout, max retry count " << retryCount << " for "
00291        << entry->myIter->first << " reached. Dropping " << pendingPackets.size()
00292        << " waiting packets from the queue\n";
00293     while (!pendingPackets.empty())
00294     {
00295         MsgPtrVector::iterator i = pendingPackets.begin();
00296         cMessage *msg = (*i);
00297         pendingPackets.erase(i);
00298         pendingQueue.remove(msg);
00299         delete msg;
00300     }
00301     delete selfmsg;
00302     arpCache.erase(entry->myIter);
00303     delete entry;
00304     numFailedResolutions++;
00305 }
00306 
00307 
00308 bool ARP::addressRecognized(IPAddress destAddr, InterfaceEntry *ie)
00309 {
00310     if (rt->isLocalAddress(destAddr))
00311         return true;
00312 
00313     // respond to Proxy ARP request: if we can route this packet (and the
00314     // output port is different from this one), say yes
00315     if (!doProxyARP)
00316         return false;
00317     InterfaceEntry *rtie = rt->getInterfaceForDestAddr(destAddr);
00318     return rtie!=NULL && rtie!=ie;
00319 }
00320 
00321 void ARP::dumpARPPacket(ARPPacket *arp)
00322 {
00323     EV << (arp->getOpcode()==ARP_REQUEST ? "ARP_REQ" : arp->getOpcode()==ARP_REPLY ? "ARP_REPLY" : "unknown type")
00324        << "  src=" << arp->getSrcIPAddress() << " / " << arp->getSrcMACAddress()
00325        << "  dest=" << arp->getDestIPAddress() << " / " << arp->getDestMACAddress() << "\n";
00326 }
00327 
00328 
00329 void ARP::processARPPacket(ARPPacket *arp)
00330 {
00331     EV << "ARP packet " << arp << " arrived:\n";
00332     dumpARPPacket(arp);
00333 
00334     // extract input port
00335     IPRoutingDecision *controlInfo = check_and_cast<IPRoutingDecision*>(arp->removeControlInfo());
00336     InterfaceEntry *ie = ift->getInterfaceById(controlInfo->getInterfaceId());
00337     delete controlInfo;
00338 
00339     //
00340     // Recipe a'la RFC 826:
00341     //
00342     // ?Do I have the hardware type in ar$hrd?
00343     // Yes: (almost definitely)
00344     //   [optionally check the hardware length ar$hln]
00345     //   ?Do I speak the protocol in ar$pro?
00346     //   Yes:
00347     //     [optionally check the protocol length ar$pln]
00348     //     Merge_flag := false
00349     //     If the pair <protocol type, sender protocol address> is
00350     //         already in my translation table, update the sender
00351     //         hardware address field of the entry with the new
00352     //         information in the packet and set Merge_flag to true.
00353     //     ?Am I the target protocol address?
00354     //     Yes:
00355     //       If Merge_flag is false, add the triplet <protocol type,
00356     //           sender protocol address, sender hardware address> to
00357     //           the translation table.
00358     //       ?Is the opcode ares_op$REQUEST?  (NOW look at the opcode!!)
00359     //       Yes:
00360     //         Swap hardware and protocol fields, putting the local
00361     //             hardware and protocol addresses in the sender fields.
00362     //         Set the ar$op field to ares_op$REPLY
00363     //         Send the packet to the (new) target hardware address on
00364     //             the same hardware on which the request was received.
00365     //
00366 
00367     MACAddress srcMACAddress = arp->getSrcMACAddress();
00368     IPAddress srcIPAddress = arp->getSrcIPAddress();
00369 
00370     if (srcMACAddress.isUnspecified())
00371         error("wrong ARP packet: source MAC address is empty");
00372     if (srcIPAddress.isUnspecified())
00373         error("wrong ARP packet: source IP address is empty");
00374 
00375     bool mergeFlag = false;
00376     // "If ... sender protocol address is already in my translation table"
00377     ARPCache::iterator it = arpCache.find(srcIPAddress);
00378     if (it!=arpCache.end())
00379     {
00380         // "update the sender hardware address field"
00381         ARPCacheEntry *entry = (*it).second;
00382         updateARPCache(entry, srcMACAddress);
00383         mergeFlag = true;
00384     }
00385 
00386     // "?Am I the target protocol address?"
00387     // if Proxy ARP is enabled, we also have to reply if we're a router to the dest IP address
00388     if (addressRecognized(arp->getDestIPAddress(), ie))
00389     {
00390         // "If Merge_flag is false, add the triplet protocol type, sender
00391         // protocol address, sender hardware address to the translation table"
00392         if (!mergeFlag)
00393         {
00394             ARPCacheEntry *entry;
00395             if (it!=arpCache.end())
00396             {
00397                 entry = (*it).second;
00398             }
00399             else
00400             {
00401                 entry = new ARPCacheEntry();
00402                 ARPCache::iterator where = arpCache.insert(arpCache.begin(), std::make_pair(srcIPAddress,entry));
00403                 entry->myIter = where;
00404                 entry->ie = ie;
00405 
00406                 entry->pending = false;
00407                 entry->timer = NULL;
00408                 entry->numRetries = 0;
00409             }
00410             updateARPCache(entry, srcMACAddress);
00411         }
00412 
00413         // "?Is the opcode ares_op$REQUEST?  (NOW look at the opcode!!)"
00414         switch (arp->getOpcode())
00415         {
00416             case ARP_REQUEST:
00417             {
00418                 EV << "Packet was ARP REQUEST, sending REPLY\n";
00419 
00420                 // find our own IP address and MAC address on the given interface
00421                 MACAddress myMACAddress = ie->getMacAddress();
00422                 IPAddress myIPAddress = ie->ipv4Data()->getIPAddress();
00423 
00424                 // "Swap hardware and protocol fields", etc.
00425                 arp->setName("arpREPLY");
00426                 IPAddress origDestAddress = arp->getDestIPAddress();
00427                 arp->setDestIPAddress(srcIPAddress);
00428                 arp->setDestMACAddress(srcMACAddress);
00429                 arp->setSrcIPAddress(origDestAddress);
00430                 arp->setSrcMACAddress(myMACAddress);
00431                 arp->setOpcode(ARP_REPLY);
00432                 delete arp->removeControlInfo();
00433                 sendPacketToNIC(arp, ie, srcMACAddress);
00434                 numRepliesSent++;
00435                 break;
00436             }
00437             case ARP_REPLY:
00438             {
00439                 EV << "Discarding packet\n";
00440                 delete arp;
00441                 break;
00442             }
00443             case ARP_RARP_REQUEST: error("RARP request received: RARP is not supported");
00444             case ARP_RARP_REPLY: error("RARP reply received: RARP is not supported");
00445             default: error("Unsupported opcode %d in received ARP packet",arp->getOpcode());
00446         }
00447     }
00448     else
00449     {
00450         // address not recognized
00451         EV << "IP address " << arp->getDestIPAddress() << " not recognized, dropping ARP packet\n";
00452         delete arp;
00453     }
00454 }
00455 
00456 void ARP::updateARPCache(ARPCacheEntry *entry, const MACAddress& macAddress)
00457 {
00458     EV << "Updating ARP cache entry: " << entry->myIter->first << " <--> " << macAddress << "\n";
00459 
00460     // update entry
00461     if (entry->pending)
00462     {
00463         entry->pending = false;
00464         delete cancelEvent(entry->timer);
00465         entry->timer = NULL;
00466         entry->numRetries = 0;
00467     }
00468     entry->macAddress = macAddress;
00469     entry->lastUpdate = simTime();
00470 
00471     // process queued packets
00472     MsgPtrVector& pendingPackets = entry->pendingPackets;
00473     while (!pendingPackets.empty())
00474     {
00475         MsgPtrVector::iterator i = pendingPackets.begin();
00476         cMessage *msg = (*i);
00477         pendingPackets.erase(i);
00478         pendingQueue.remove(msg);
00479         EV << "Sending out queued packet " << msg << "\n";
00480         sendPacketToNIC(msg, entry->ie, macAddress);
00481     }
00482 }
00483 
00484