/* * Copyright (C) 2014 Cloudius Systems, Ltd. * */ #ifndef ARP_HH_ #define ARP_HH_ #include "net.hh" #include "core/reactor.hh" #include "byteorder.hh" #include "ethernet.hh" #include "core/print.hh" #include namespace net { class arp; class arp_for_protocol; template class arp_for; class arp_for_protocol { protected: arp& _arp; uint16_t _proto_num; public: arp_for_protocol(arp& a, uint16_t proto_num); virtual ~arp_for_protocol(); virtual future<> received(packet p) = 0; virtual bool forward(forward_hash& out_hash_data, packet& p, size_t off) { return false; } }; class arp { interface* _netif; l3_protocol _proto; subscription _rx_packets; std::unordered_map _arp_for_protocol; circular_buffer _packetq; private: struct arp_hdr { packed htype; packed ptype; template void adjust_endianness(Adjuster a) { return a(htype, ptype); } }; public: explicit arp(interface* netif); void add(uint16_t proto_num, arp_for_protocol* afp); void del(uint16_t proto_num); private: ethernet_address l2self() { return _netif->hw_address(); } future<> process_packet(packet p, ethernet_address from); bool forward(forward_hash& out_hash_data, packet& p, size_t off); std::experimental::optional get_packet(); template friend class arp_for; }; template class arp_for : public arp_for_protocol { public: using l2addr = ethernet_address; using l3addr = typename L3::address_type; private: static constexpr auto max_waiters = 512; enum oper { op_request = 1, op_reply = 2, }; struct arp_hdr { packed htype; packed ptype; uint8_t hlen; uint8_t plen; packed oper; l2addr sender_hwaddr; l3addr sender_paddr; l2addr target_hwaddr; l3addr target_paddr; template void adjust_endianness(Adjuster a) { a(htype, ptype, oper, sender_hwaddr, sender_paddr, target_hwaddr, target_paddr); } }; struct resolution { std::vector> _waiters; timer<> _timeout_timer; }; private: l3addr _l3self = L3::broadcast_address(); std::unordered_map _table; std::unordered_map _in_progress; private: packet make_query_packet(l3addr paddr); virtual future<> received(packet p) override; future<> handle_request(arp_hdr* ah); l2addr l2self() { return _arp.l2self(); } void send(l2addr to, packet p); public: future<> send_query(const l3addr& paddr); explicit arp_for(arp& a) : arp_for_protocol(a, L3::arp_protocol_type()) {} future lookup(const l3addr& addr); void learn(l2addr l2, l3addr l3); void run(); void set_self_addr(l3addr addr) { _l3self = addr; } friend class arp; }; template packet arp_for::make_query_packet(l3addr paddr) { arp_hdr hdr; hdr.htype = ethernet::arp_hardware_type(); hdr.ptype = L3::arp_protocol_type(); hdr.hlen = sizeof(l2addr); hdr.plen = sizeof(l3addr); hdr.oper = op_request; hdr.sender_hwaddr = l2self(); hdr.sender_paddr = _l3self; hdr.target_hwaddr = ethernet::broadcast_address(); hdr.target_paddr = paddr; hdr = hton(hdr); return packet(reinterpret_cast(&hdr), sizeof(hdr)); } template void arp_for::send(l2addr to, packet p) { _arp._packetq.push_back(l3_protocol::l3packet{eth_protocol_num::arp, to, std::move(p)}); } template future<> arp_for::send_query(const l3addr& paddr) { send(ethernet::broadcast_address(), make_query_packet(paddr)); return make_ready_future<>(); } class arp_error : public std::runtime_error { public: arp_error(const std::string& msg) : std::runtime_error(msg) {} }; class arp_timeout_error : public arp_error { public: arp_timeout_error() : arp_error("ARP timeout") {} }; class arp_queue_full_error : public arp_error { public: arp_queue_full_error() : arp_error("ARP waiter's queue is full") {} }; template future arp_for::lookup(const l3addr& paddr) { auto i = _table.find(paddr); if (i != _table.end()) { return make_ready_future(i->second); } auto j = _in_progress.find(paddr); auto first_request = j == _in_progress.end(); auto& res = first_request ? _in_progress[paddr] : j->second; if (first_request) { res._timeout_timer.set_callback([paddr, this, &res] { send_query(paddr); for (auto& w : res._waiters) { w.set_exception(arp_timeout_error()); } res._waiters.clear(); }); res._timeout_timer.arm_periodic(std::chrono::seconds(1)); send_query(paddr); } if (res._waiters.size() >= max_waiters) { return make_exception_future(arp_queue_full_error()); } res._waiters.emplace_back(); return res._waiters.back().get_future(); } template void arp_for::learn(l2addr hwaddr, l3addr paddr) { _table[paddr] = hwaddr; auto i = _in_progress.find(paddr); if (i != _in_progress.end()) { auto& res = i->second; res._timeout_timer.cancel(); for (auto &&pr : res._waiters) { pr.set_value(hwaddr); } _in_progress.erase(i); } } template future<> arp_for::received(packet p) { auto ah = p.get_header(); if (!ah) { return make_ready_future<>(); } auto h = ntoh(*ah); if (h.hlen != sizeof(l2addr) || h.plen != sizeof(l3addr)) { return make_ready_future<>(); } switch (h.oper) { case op_request: return handle_request(&h); case op_reply: arp_learn(h.sender_hwaddr, h.sender_paddr); return make_ready_future<>(); default: return make_ready_future<>(); } } template future<> arp_for::handle_request(arp_hdr* ah) { if (ah->target_paddr == _l3self && _l3self != L3::broadcast_address()) { ah->oper = op_reply; ah->target_hwaddr = ah->sender_hwaddr; ah->target_paddr = ah->sender_paddr; ah->sender_hwaddr = l2self(); ah->sender_paddr = _l3self; *ah = hton(*ah); send(ah->target_hwaddr, packet(reinterpret_cast(ah), sizeof(*ah))); } return make_ready_future<>(); } } #endif /* ARP_HH_ */