/* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */ /** * Copyright (c) 2017 Alexander Afanasyev * * This program is free software: you can redistribute it and/or modify it under the terms of * the GNU General Public License as published by the Free Software Foundation, either version * 3 of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. * See the GNU General Public License for more details. * * You should have received a copy of the GNU General Public License along with this program. * If not, see . */ /* This file defines an ARP cache with ARP request queue and ARP cache entries The ARP cache entries hold IP->MAC mappings and are timed out every SR_ARPCACHE_TO seconds. -- The handle_arpreq() function is a function you should write, and it should handle sending ARP requests if necessary: function handle_arpreq(req): if now - req->timeSent > seconds(1) if req->nTimesSent >= 5: send icmp host unreachable to source addr of all pkts waiting on this request cache.removeRequest(req) else: send arp request req->timeSent = now req->nTimesSent++ -- The ARP reply processing code should move entries from the ARP request queue to the ARP cache: # When servicing an arp reply that gives us an IP->MAC mapping req = cache.insertArpEntry(ip, mac) if req != nullptr: send all packets on the req->packets linked list cache.removeRequest(req) -- To meet the guidelines in the assignment (ARP requests are sent every second until we send 5 ARP requests, then we send ICMP host unreachable back to all packets waiting on this ARP request), you must fill out the following function that is called every second and is defined in sr_arpcache.c: void ArpCache::periodicCheckArpRequestsAndCacheEntries() { for each request on m_arpRequests: handle_arpreq(request) } */ #ifndef SIMPLE_ROUTER_ARP_CACHE_HPP #define SIMPLE_ROUTER_ARP_CACHE_HPP #include "core/protocol.hpp" #include #include #include #include #include namespace simple_router { class SimpleRouter; using steady_clock = std::chrono::steady_clock; using time_point = std::chrono::steady_clock::time_point; using seconds = std::chrono::seconds; const seconds SR_ARPCACHE_TO = seconds(30); const uint32_t MAX_SENT_TIME = 5; struct PendingPacket { Buffer packet; //< A raw Ethernet frame, presumably with the dest MAC empty std::string iface; //< The outgoing interface }; struct ArpRequest { ArpRequest(uint32_t ip) : ip(ip) , nTimesSent(0) { } uint32_t ip; /** * Last time this ARP request was sent. You should update this. If * the ARP request was never sent, timeSent == time_point() */ time_point timeSent; /** * The number of times this request was sent. You should update this. */ uint32_t nTimesSent; std::list packets; }; struct ArpEntry { Buffer mac; uint32_t ip = 0; //< IP addr in network byte order time_point timeAdded; bool isValid = false; }; class ArpCache { public: ArpCache(SimpleRouter& router); ~ArpCache(); /** * IMPLEMENT THIS METHOD * * This method gets called every second. For each request sent out, * you should keep checking whether to resend a request or remove it. * * Your implementation should follow the following logic * * for each request in queued requests: * handleRequest(request) * * for each cache entry in entries: * if not entry->isValid * record entry for removal * remove all entries marked for removal */ void periodicCheckArpRequestsAndCacheEntries(); void removeEntry(const std::shared_ptr &entry); /** * Checks if an IP->MAC mapping is in the cache. IP is in network byte order. * You must free the returned structure if it is not NULL. */ std::shared_ptr lookup(uint32_t ip); std::shared_ptr lookupWithoutLock(uint32_t ip); /** * Adds an ARP request to the ARP request queue. If the request is already on * the queue, adds the packet to the linked list of packets for this sr_arpreq * that corresponds to this ARP request. The packet argument should not be * freed by the caller. * * A pointer to the ARP request is returned; it should not be freed. The caller * can remove the ARP request from the queue by calling sr_arpreq_destroy. */ std::shared_ptr queueRequest(uint32_t ip, const Buffer& packet, const std::string& iface); std::shared_ptr queueRequestWithoutLock(uint32_t ip, const Buffer& packet, const std::string& iface); /* * Frees all memory associated with this arp request entry. If this arp request * entry is on the arp request queue, it is removed from the queue. */ void removeRequest(const std::shared_ptr& entry); /** * This method performs two functions: * * 1) Looks up this IP in the request queue. If it is found, returns a pointer * to the ArpRequest with this IP. Otherwise, returns nullptr. * 2) Inserts this IP to MAC mapping in the cache, and marks it valid. */ std::shared_ptr insertArpEntry(const Buffer& mac, uint32_t ip); /** * Prints out the ARP table. */ void dump(); /** * Clear all entries in ARP cache and requests. */ void clear(); private: /** * Thread which sweeps through the cache and invalidates entries that were added * more than SR_ARPCACHE_TO seconds ago. */ void ticker(); private: SimpleRouter& m_router; std::list> m_cacheEntries; std::list> m_arpRequests; volatile bool m_shouldStop; std::thread m_tickerThread; mutable std::mutex m_mutex; friend std::ostream& operator<<(std::ostream& os, const ArpCache& cache); }; std::ostream& operator<<(std::ostream& os, const ArpCache& cache); } // namespace simple_router #endif // SIMPLE_ROUTER_ARP_CACHE_HPP