From fe97e171b487e6afae5da2bbcd4d5b9854e5241b Mon Sep 17 00:00:00 2001 From: ridethepig Date: Sat, 18 Feb 2023 08:47:54 +0000 Subject: [PATCH] ctest arp pass --- libsponge/network_interface.cc | 116 +++++++++++++++++++++++++++++++-- libsponge/network_interface.hh | 10 +++ 2 files changed, 121 insertions(+), 5 deletions(-) diff --git a/libsponge/network_interface.cc b/libsponge/network_interface.cc index 202b1f3..f5ddcc0 100644 --- a/libsponge/network_interface.cc +++ b/libsponge/network_interface.cc @@ -14,7 +14,7 @@ // You will need to add private members to the class declaration in `network_interface.hh` template -void DUMMY_CODE(Targs &&... /* unused */) {} +void DUMMY_CODE(Targs &&.../* unused */) {} using namespace std; @@ -32,15 +32,121 @@ NetworkInterface::NetworkInterface(const EthernetAddress ðernet_address, cons void NetworkInterface::send_datagram(const InternetDatagram &dgram, const Address &next_hop) { // convert IP address of next hop to raw 32-bit representation (used in ARP header) const uint32_t next_hop_ip = next_hop.ipv4_numeric(); - - DUMMY_CODE(dgram, next_hop, next_hop_ip); + auto const &itr_next_hop_eth = _arp_cache.find(next_hop_ip); + if (itr_next_hop_eth != _arp_cache.end()) { + // found + auto const &next_hop_eth = itr_next_hop_eth->second; + if (next_hop_eth.first.has_value()) { + // a valid arp cache entry + EthernetFrame frame; + EthernetHeader hdr; + hdr.dst = next_hop_eth.first.value(); + hdr.src = _ethernet_address; + hdr.type = EthernetHeader::TYPE_IPv4; + frame.header() = hdr; + frame.payload() = dgram.serialize(); + _frames_out.push(frame); + } else { + // an arp has been sent for it within 5 seconds + _suspended_datagrams.push_back(std::make_pair(dgram, next_hop)); + } + } else { + // not found and no arp has been sent for the ip + _suspended_datagrams.push_back(std::make_pair(dgram, next_hop)); + ARPMessage arpmsg; + arpmsg.opcode = ARPMessage::OPCODE_REQUEST; + arpmsg.sender_ethernet_address = _ethernet_address; + arpmsg.sender_ip_address = _ip_address.ipv4_numeric(); + arpmsg.target_ip_address = next_hop_ip; + EthernetFrame frame; + EthernetHeader hdr; + hdr.dst = ETHERNET_BROADCAST; + hdr.src = _ethernet_address; + hdr.type = EthernetHeader::TYPE_ARP; + frame.header() = hdr; + frame.payload() = arpmsg.serialize(); + _frames_out.push(frame); + _arp_cache[next_hop_ip] = std::make_pair(std::optional{}, 5 * 1000); + // allocate an entry for the ARP Req we sent + } } //! \param[in] frame the incoming Ethernet frame optional NetworkInterface::recv_frame(const EthernetFrame &frame) { - DUMMY_CODE(frame); + if (frame.header().dst != _ethernet_address && frame.header().dst != ETHERNET_BROADCAST) + return {}; + if (frame.header().type == EthernetHeader::TYPE_IPv4) { + auto payload = frame.payload(); + InternetDatagram dgram; + auto parse_res = dgram.parse(payload); + if (parse_res == ParseResult::NoError) { + return dgram; + } + } else if (frame.header().type == EthernetHeader::TYPE_ARP) { + if (frame.header().src == _ethernet_address) { + cerr << "receive loopback arp req\n"; + return {}; + } + ARPMessage arpmsg; + auto parse_res = arpmsg.parse(frame.payload()); + if (parse_res != ParseResult::NoError) { + return {}; + } + if (arpmsg.opcode != ARPMessage::OPCODE_REPLY && arpmsg.opcode != ARPMessage::OPCODE_REQUEST) { + cerr << "unknown ARP opcode\n"; + return {}; + } + if (arpmsg.opcode == ARPMessage::OPCODE_REQUEST && arpmsg.target_ip_address == _ip_address.ipv4_numeric()) { + ARPMessage arpreply; + arpreply.opcode = ARPMessage::OPCODE_REPLY; + arpreply.sender_ethernet_address = _ethernet_address; + arpreply.sender_ip_address = _ip_address.ipv4_numeric(); + arpreply.target_ip_address = arpmsg.sender_ip_address; + arpreply.target_ethernet_address = arpmsg.sender_ethernet_address; + // i think this should be directly the requester's EA + EthernetFrame framereply; + EthernetHeader hdr; + hdr.dst = arpmsg.sender_ethernet_address; + hdr.src = _ethernet_address; + hdr.type = EthernetHeader::TYPE_ARP; + framereply.header() = hdr; + framereply.payload() = arpreply.serialize(); + _frames_out.push(framereply); + } + // no matter what type of arp it is, learn from it + _arp_cache[arpmsg.sender_ip_address] = + std::make_pair(std::optional{arpmsg.sender_ethernet_address}, 30 * 1000); + auto itr_dgram = _suspended_datagrams.begin(); + + while (itr_dgram != _suspended_datagrams.end()) { + if (_arp_cache.find(itr_dgram->second.ipv4_numeric()) != _arp_cache.end()) { + auto dgram = itr_dgram->first; + auto next_hop = itr_dgram->second; + itr_dgram = _suspended_datagrams.erase(itr_dgram); + this->send_datagram(dgram, next_hop); + } else { + itr_dgram++; + } + } + } return {}; } //! \param[in] ms_since_last_tick the number of milliseconds since the last call to this method -void NetworkInterface::tick(const size_t ms_since_last_tick) { DUMMY_CODE(ms_since_last_tick); } +void NetworkInterface::tick(const size_t ms_since_last_tick) { + for (auto &arp_entry : _arp_cache) { + if (arp_entry.second.second >= ms_since_last_tick) + arp_entry.second.second -= ms_since_last_tick; + else + arp_entry.second.second = 0; + } + auto arp_entry = _arp_cache.begin(); + while (arp_entry != _arp_cache.end()) { + if (arp_entry->second.second == 0) { + arp_entry = _arp_cache.erase(arp_entry); + } else { + arp_entry++; + } + } + // TODO: maybe drop packets that couldn't get ARP reply, though not required in this lab +} diff --git a/libsponge/network_interface.hh b/libsponge/network_interface.hh index 87a153f..8a0885e 100644 --- a/libsponge/network_interface.hh +++ b/libsponge/network_interface.hh @@ -7,6 +7,9 @@ #include #include +#include +#include +#include //! \brief A "network interface" that connects IP (the internet layer, or network layer) //! with Ethernet (the network access layer, or link layer). @@ -40,6 +43,13 @@ class NetworkInterface { //! outbound queue of Ethernet frames that the NetworkInterface wants sent std::queue _frames_out{}; + //! internal map as arp cache, holding mapping from ipv4(numerical form) to MAC addr with expire time. + //! if optinal has no value, it means that a ARP has been sent for the IP within 5 seconds + std::map, size_t> > _arp_cache{}; + + //! queue the IP datagrams waiting for ARP reply + std::list >_suspended_datagrams{}; + public: //! \brief Construct a network interface with given Ethernet (network-access-layer) and IP (internet-layer) addresses NetworkInterface(const EthernetAddress ðernet_address, const Address &ip_address);