// -*- mode:C++; tab-width:8; c-basic-offset:2; indent-tabs-mode:t -*- /* * This file is open source software, licensed to you under the terms * of the Apache License, Version 2.0 (the "License"). See the NOTICE file * distributed with this work for additional information regarding copyright * ownership. You may not use this file except in compliance with the License. * * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. */ /* * Copyright (C) 2014 Cloudius Systems, Ltd. * */ /* * Ceph - scalable distributed file system * * Copyright (C) 2015 XSky * * Author: Haomai Wang * * This is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License version 2.1, as published by the Free Software * Foundation. See file COPYING. * */ #ifndef CEPH_MSG_ARP_H_ #define CEPH_MSG_ARP_H_ #include #include #include #include "msg/async/Event.h" #include "ethernet.h" #include "circular_buffer.h" #include "ip_types.h" #include "net.h" #include "Packet.h" class arp; 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 int received(Packet p) = 0; virtual bool forward(forward_hash& out_hash_data, Packet& p, size_t off) { return false; } }; class interface; class arp { interface* _netif; l3_protocol _proto; subscription _rx_packets; std::unordered_map _arp_for_protocol; circular_buffer _packetq; private: struct arp_hdr { uint16_t htype; uint16_t ptype; arp_hdr ntoh() { arp_hdr hdr = *this; hdr.htype = ::ntoh(htype); hdr.ptype = ::ntoh(ptype); return hdr; } arp_hdr hton() { arp_hdr hdr = *this; hdr.htype = ::hton(htype); hdr.ptype = ::hton(ptype); return hdr; } }; 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(); } int process_packet(Packet p, ethernet_address from); bool forward(forward_hash& out_hash_data, Packet& p, size_t off); Tub 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 { uint16_t htype; uint16_t ptype; uint8_t hlen; uint8_t plen; uint16_t oper; l2addr sender_hwaddr; l3addr sender_paddr; l2addr target_hwaddr; l3addr target_paddr; arp_hdr ntoh() { arp_hdr hdr = *this; hdr.htype = ::ntoh(htype); hdr.ptype = ::ntoh(ptype); hdr.oper = ::ntoh(oper); hdr.sender_hwaddr = sender_hwaddr.ntoh(); hdr.sender_paddr = sender_paddr.ntoh(); hdr.target_hwaddr = target_hwaddr.ntoh(); hdr.target_paddr = target_paddr.ntoh(); return hdr; } arp_hdr hton() { arp_hdr hdr = *this; hdr.htype = ::hton(htype); hdr.ptype = ::hton(ptype); hdr.oper = ::hton(oper); hdr.sender_hwaddr = sender_hwaddr.hton(); hdr.sender_paddr = sender_paddr.hton(); hdr.target_hwaddr = target_hwaddr.hton(); hdr.target_paddr = target_paddr.hton(); return hdr; } }; struct resolution { std::vector> _waiters; uint64_t timeout_fd; }; class C_handle_arp_timeout : public EventCallback { arp_for *arp; l3addr paddr; bool first_request; public: C_handle_arp_timeout(arp_for *a, l3addr addr, bool first): arp(a), paddr(addr), first_request(first) {} void do_request(int r) { arp->send_query(paddr); auto &res = arp->_in_progress[paddr]; for (auto& p : res._waiters) { p.first(ethernet_address(), std::move(p.second), -ETIMEDOUT); } res._waiters.clear(); res.timeout_fd = arp->center->create_time_event( 1*1000*1000, this); } }; friend class C_handle_arp_timeout; private: CephContext *cct; EventCenter *center; l3addr _l3self = L3::broadcast_address(); std::unordered_map _table; std::unordered_map _in_progress; private: Packet make_query_packet(l3addr paddr); virtual int received(Packet p) override; int handle_request(arp_hdr* ah); l2addr l2self() { return _arp.l2self(); } void send(l2addr to, Packet &&p); public: void send_query(const l3addr& paddr); explicit arp_for(CephContext *c, arp& a, EventCenter *cen) : arp_for_protocol(a, L3::arp_protocol_type()), cct(c), center(cen) { _table[L3::broadcast_address()] = ethernet::broadcast_address(); } ~arp_for() { for (auto && p : _in_progress) center->delete_time_event(p.second.timeout_fd); } void wait(const l3addr& addr, Packet p, resolution_cb cb); void learn(l2addr l2, l3addr l3); void run(); void set_self_addr(l3addr addr) { _table.erase(_l3self); _table[addr] = l2self(); _l3self = addr; } friend class arp; }; 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 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 = hdr.hton(); return Packet(reinterpret_cast(&hdr), sizeof(hdr)); } template void arp_for::send_query(const l3addr& paddr) { send(ethernet::broadcast_address(), make_query_packet(paddr)); } 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; center->delete_time_event(res.timeout_fd); for (auto &&p : res._waiters) { p.first(hwaddr, std::move(p.second), 0); } _in_progress.erase(i); } } template void arp_for::wait(const l3addr& paddr, Packet p, resolution_cb cb) { auto i = _table.find(paddr); if (i != _table.end()) { cb(i->second, std::move(p), 0); return ; } 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_fd = center->create_time_event( 1*1000*1000, new C_handle_arp_timeout(this, paddr, first_request)); send_query(paddr); } if (res._waiters.size() >= max_waiters) { cb(ethernet_address(), std::move(p), -EBUSY); return ; } res._waiters.emplace_back(cb, std::move(p)); return ; } template int arp_for::received(Packet p) { auto ah = p.get_header(); if (!ah) { return 0; } auto h = ah->ntoh(); if (h.hlen != sizeof(l2addr) || h.plen != sizeof(l3addr)) { return 0; } switch (h.oper) { case op_request: return handle_request(&h); case op_reply: _arp._netif->arp_learn(h.sender_hwaddr, h.sender_paddr); return 0; default: return 0; } } template int 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 = ah->hton(); send(ah->target_hwaddr, Packet(reinterpret_cast(ah), sizeof(*ah))); } return 0; } #endif /* CEPH_MSG_ARP_H_ */