diff --git a/cli/bus.h b/cli/bus.h index 801a913b..d4e5a17e 100644 --- a/cli/bus.h +++ b/cli/bus.h @@ -53,6 +53,7 @@ inline std::vector get_bus_requests(common::sdp::DataPlaneInSh {common::idp::requestType::dump_physical_port, "dump_physical_port"}, {common::idp::requestType::balancer_state_clear, "balancer_state_clear"}, {common::idp::requestType::neighbor_show, "neighbor_show"}, + {common::idp::requestType::neighbor_show_cache, "neighbor_show_cache"}, {common::idp::requestType::neighbor_insert, "neighbor_insert"}, {common::idp::requestType::neighbor_remove, "neighbor_remove"}, {common::idp::requestType::neighbor_clear, "neighbor_clear"}, diff --git a/cli/main.cpp b/cli/main.cpp index 46f14057..ce7957d7 100644 --- a/cli/main.cpp +++ b/cli/main.cpp @@ -82,6 +82,7 @@ std::vector(); } + auto neighbor_show_cache() const + { + return get(); + } + auto neighbor_insert(const common::idp::neighbor_insert::request& request) const { return get(request); @@ -250,6 +255,11 @@ class dataPlane return get(); } + auto neighbor_interfaces_switch() const + { + return get(); + } + protected: void connectToDataPlane() const { diff --git a/common/idp.h b/common/idp.h index cb616de8..61ae54da 100644 --- a/common/idp.h +++ b/common/idp.h @@ -74,6 +74,7 @@ enum class requestType : uint32_t dump_physical_port, balancer_state_clear, neighbor_show, + neighbor_show_cache, neighbor_insert, neighbor_remove, neighbor_clear, @@ -82,6 +83,7 @@ enum class requestType : uint32_t neighbor_stats, memory_manager_update, memory_manager_stats, + neighbor_interfaces_switch, size, // size should always be at the bottom of the list, this enum allows us to find out the size of the enum list }; @@ -965,6 +967,15 @@ namespace neighbor_show { using entry = std::tuple; ///< mac_address + +using response = std::vector; +} + +namespace neighbor_show_cache +{ +using entry = std::tuple, ///< last_update_timestamp @@ -1068,6 +1079,7 @@ using response = std::variant, get_shm_info::response, get_shm_tsc_info::response, neighbor_show::response, + neighbor_show_cache::response, neighbor_stats::response, memory_manager_stats::response>; } diff --git a/controlplane/route.cpp b/controlplane/route.cpp index d69f87cb..55f676f1 100644 --- a/controlplane/route.cpp +++ b/controlplane/route.cpp @@ -1024,6 +1024,7 @@ void route_t::reload_after() generations.switch_generation(); generations_neighbors.next_unlock(); generations.next_unlock(); + dataplane.neighbor_interfaces_switch(); } void route_t::prefix_flush_prefixes(common::idp::updateGlobalBase::request& globalbase) diff --git a/dataplane/bus.cpp b/dataplane/bus.cpp index 578495f6..4fd59581 100644 --- a/dataplane/bus.cpp +++ b/dataplane/bus.cpp @@ -318,6 +318,10 @@ void cBus::clientThread(int clientSocket) { response = dataPlane->neighbor.neighbor_show(); } + else if (type == common::idp::requestType::neighbor_show_cache) + { + response = dataPlane->neighbor.neighbor_show_cache(); + } else if (type == common::idp::requestType::neighbor_insert) { response = dataPlane->neighbor.neighbor_insert(std::get(std::get<1>(request))); @@ -350,6 +354,10 @@ void cBus::clientThread(int clientSocket) { response = dataPlane->memory_manager.memory_manager_stats(); } + else if (type == common::idp::requestType::neighbor_interfaces_switch) + { + response = dataPlane->neighbor.neighbor_interfaces_switch(); + } else { stats.errors[(uint32_t)common::idp::errorType::busParse]++; diff --git a/dataplane/controlplane.cpp b/dataplane/controlplane.cpp index a1aa5234..af51113a 100644 --- a/dataplane/controlplane.cpp +++ b/dataplane/controlplane.cpp @@ -60,6 +60,7 @@ common::idp::updateGlobalBase::response cControlPlane::updateGlobalBase(const co { return result; } + dataPlane->neighbor.UpdateFromCache(false, true); DEBUG_LATCH_WAIT(common::idp::debug_latch_update::id::global_base_switch); diff --git a/dataplane/neighbor.cpp b/dataplane/neighbor.cpp index 8a0525de..92c92f34 100644 --- a/dataplane/neighbor.cpp +++ b/dataplane/neighbor.cpp @@ -7,6 +7,18 @@ #include "base.h" #include "neighbor.h" +#define NEIGHBOR_DEBUG_LEVEL 1 +#if NEIGHBOR_DEBUG_LEVEL >= 1 +#define NEIGHBOR_INFO(msg, args...) YANET_LOG_INFO(msg, ##args) +#else +#define NEIGHBOR_INFO(msg, args...) +#endif +#if NEIGHBOR_DEBUG_LEVEL >= 2 +#define NEIGHBOR_DEBUG(msg, args...) YANET_LOG_INFO(msg, ##args) +#else +#define NEIGHBOR_DEBUG(msg, args...) +#endif + namespace dataplane::neighbor { @@ -41,6 +53,8 @@ eResult module::init( remove_timeout_ = remove_timeout; resolve_removed_ = resolve_removed; + neighbor_cache_.Init(checks_interval, remove_timeout, resolve_removed); + generation_hashtable.fill([&](neighbor::generation_hashtable& hashtable) { for (const auto socket_id : socket_ids) { @@ -59,6 +73,7 @@ eResult module::init( void module::update_worker_base(const std::vector>& base_nexts) { + NEIGHBOR_DEBUG("update_worker_base\n"); auto lock = generation_hashtable.current_lock_guard(); for (auto& [socket_id, base_next] : base_nexts) { @@ -96,24 +111,10 @@ common::idp::neighbor_show::response module::neighbor_show() const const auto& [route_name, interface_name] = it->second; - std::optional last_update_timestamp; - if (!(value.flags & flag_is_static)) - { - last_update_timestamp = current_time_provider_() - value.last_update_timestamp; - } - - std::string last_remove_timestamp; - if (value.last_remove_timestamp != 0) - { - last_remove_timestamp = std::to_string(current_time_provider_() - value.last_remove_timestamp); - } - response.emplace_back(route_name, interface_name, common::ip_address_t(key.flags & flag_is_ipv6 ? 6 : 4, key.address.bytes), - common::mac_address_t(value.ether_address.addr_bytes), - last_update_timestamp, - last_remove_timestamp); + common::mac_address_t(value.ether_address.addr_bytes)); } } } @@ -121,28 +122,19 @@ common::idp::neighbor_show::response module::neighbor_show() const return response; } +common::idp::neighbor_show_cache::response module::neighbor_show_cache() const +{ + return neighbor_cache_.NeighborShow(current_time_provider_()); +} + eResult module::neighbor_insert(const common::idp::neighbor_insert::request& request) { const auto& [route_name, interface_name, ip_address, mac_address] = request; + NEIGHBOR_INFO("neighbor_insert interface_name=%s, ip_address=%s, mac_address=%s\n", interface_name.c_str(), ip_address.toString().c_str(), mac_address.toString().c_str()); GCC_BUG_UNUSED(route_name); ///< @todo - tInterfaceId interface_id = 0; - { - auto lock = generation_interface.current_lock_guard(); - - const auto& interface_name_to_id = generation_interface.current().interface_name_to_id; - auto it = interface_name_to_id.find(interface_name); - if (it == interface_name_to_id.end()) - { - return eResult::invalidInterfaceName; - } - - interface_id = it->second; - } - dataplane::neighbor::key key; memset(&key, 0, sizeof(key)); - key.interface_id = interface_id; key.flags = 0; if (ip_address.is_ipv4()) @@ -157,11 +149,15 @@ eResult module::neighbor_insert(const common::idp::neighbor_insert::request& req dataplane::neighbor::value value; memcpy(value.ether_address.addr_bytes, mac_address.data(), 6); - value.flags = 0; - value.flags |= flag_is_static; - value.last_update_timestamp = current_time_provider_(); - value.last_remove_timestamp = 0; - value.last_resolve_timestamp = 0; + + neighbor_cache_.Insert(interface_name, key.address, ip_address.is_ipv6(), value.ether_address, current_time_provider_(), true); + + std::optional interface_id = GetInterfaceId(interface_name); + if (!interface_id.has_value()) + { + return eResult::invalidInterfaceName; + } + key.interface_id = *interface_id; auto response = generation_hashtable.update([this, key, value](neighbor::generation_hashtable& hashtable) { eResult result = eResult::success; @@ -187,25 +183,11 @@ eResult module::neighbor_insert(const common::idp::neighbor_insert::request& req eResult module::neighbor_remove(const common::idp::neighbor_remove::request& request) { const auto& [route_name, interface_name, ip_address] = request; + NEIGHBOR_INFO("neighbor_remove interface_name=%s, ip_address=%s\n", interface_name.c_str(), ip_address.toString().c_str()); GCC_BUG_UNUSED(route_name); ///< @todo - tInterfaceId interface_id = 0; - { - auto lock = generation_interface.current_lock_guard(); - - const auto& interface_name_to_id = generation_interface.current().interface_name_to_id; - auto it = interface_name_to_id.find(interface_name); - if (it == interface_name_to_id.end()) - { - return eResult::invalidInterfaceName; - } - - interface_id = it->second; - } - dataplane::neighbor::key key; memset(&key, 0, sizeof(key)); - key.interface_id = interface_id; key.flags = 0; if (ip_address.is_ipv4()) @@ -218,6 +200,15 @@ eResult module::neighbor_remove(const common::idp::neighbor_remove::request& req key.flags |= flag_is_ipv6; } + neighbor_cache_.Remove(interface_name, key.address, ip_address.is_ipv6(), current_time_provider_(), true); + + std::optional interface_id = GetInterfaceId(interface_name); + if (!interface_id.has_value()) + { + return eResult::invalidInterfaceName; + } + key.interface_id = *interface_id; + auto response = generation_hashtable.update([this, key](neighbor::generation_hashtable& hashtable) { eResult result = eResult::success; for (auto& [socket_id, hashtable_updater] : hashtable.hashtable_updater) @@ -241,7 +232,15 @@ eResult module::neighbor_remove(const common::idp::neighbor_remove::request& req eResult module::neighbor_clear() { - return DumpOSNeighbors(); + NEIGHBOR_INFO("neighbor_clear\n"); + eResult result = DumpOSNeighbors(); + if (result != eResult::success) + { + return result; + } + UpdateFromCache(true, false); + + return eResult::success; } eResult module::neighbor_flush() @@ -256,18 +255,6 @@ void module::StartNetlinkMonitor() { neighbor_provider->StartMonitor( rcvbuf_size_, - [this](const char* ifname) -> std::optional { - auto interfaces_guard = generation_interface.current_lock_guard(); - auto& ids = generation_interface.current().interface_name_to_id; - if (auto it = ids.find(ifname); it != ids.end()) - { - return it->second; - } - else - { - return std::nullopt; - } - }, [this](auto... args) { return Upsert(args...); }, [this](auto... args) { return Remove(args...); }, [this](auto... args) { return UpdateTimestamp(args...); }); @@ -282,72 +269,41 @@ void module::StopNetlinkMonitor() eResult module::DumpOSNeighbors() { - std::vector dump; - std::vector> static_entries; - { - auto interfaces_guard = generation_interface.current_lock_guard(); - auto& new_interfaces = generation_interface.current(); - auto& old_interfaces = generation_interface.next(); - dump = neighbor_provider->GetHostDump(rcvbuf_size_, new_interfaces.interface_name_to_id); - - { - auto lock = generation_hashtable.current_lock_guard(); - for (auto it : generation_hashtable.current().hashtable_updater.begin()->second.range()) - { - if (!it.is_valid()) - { - continue; - } - - if (it.value()->flags & flag_is_static) - { - auto key = *it.key(); - auto to_name = old_interfaces.interface_id_to_name.find(key.interface_id); - if (to_name == old_interfaces.interface_id_to_name.cend()) - { - continue; - } - - auto to_id = new_interfaces.interface_name_to_id.find(std::get<1>(to_name->second)); - if (to_id == new_interfaces.interface_name_to_id.cend()) - { - continue; - } - - key.interface_id = to_id->second; - static_entries.emplace_back(key, *it.value()); - } - } - } - } + NEIGHBOR_INFO("DumpOSNeighbors\n"); + std::vector dump = neighbor_provider->GetHostDump(rcvbuf_size_); + neighbor_cache_.UpdateFromDump(dump, current_time_provider_()); + auto interfaces_guard = generation_interface.current_lock_guard(); + const auto& interface_name_to_id = generation_interface.current().interface_name_to_id; eResult res = generation_hashtable.update( [&dump, now = current_time_provider_(), - &static_entries, + interface_name_to_id, this]( neighbor::generation_hashtable& hashtable) { for (auto& [socket_id, hashtable_updater] : hashtable.hashtable_updater) { - hashtable_updater.get_pointer()->clear(); - - for (const auto& [key, value] : static_entries) - { - hashtable_updater.get_pointer()->insert_or_update(key, value); - } - - for (const auto& [iface, dst, mac, is_v6] : dump) + for (const auto& entry : dump) { + const auto& [iface_name, dst, mac, is_v6] = entry; if (!mac) { - YANET_LOG_INFO("No MAC address for neighbor in dump\n"); + NEIGHBOR_INFO("No MAC address for neighbor in dump\n"); + continue; + } + + auto iter = interface_name_to_id.find(iface_name); + if (iter == interface_name_to_id.end()) + { continue; } + tInterfaceId iface = std::get<1>(iter->second); + NEIGHBOR_INFO("DumpOSNeighbors add iface=%d %s\n", iface, entry.toString().c_str()); hashtable_updater.get_pointer() ->insert_or_update( dataplane::neighbor::key{iface, is_v6 ? flag_is_ipv6 : uint16_t{}, dst}, - dataplane::neighbor::value{mac.value(), 0, now, 0, 0, 0}); + dataplane::neighbor::value{mac.value()}); stats.netlink_neighbor_update++; } } @@ -362,26 +318,121 @@ eResult module::DumpOSNeighbors() return neighbor_flush(); } +template +bool HasNewKeys(const std::unordered_map& current, const std::unordered_map& update) +{ + for (const auto& iter : update) + { + if (current.find(iter.first) == current.end()) + { + return true; + } + } + return false; +} + +template +void CopyNewKeys(std::unordered_map& current, const std::unordered_map& update) +{ + for (const auto& iter : update) + { + if (current.find(iter.first) == current.end()) + { + current.insert(iter); + } + } +} + +template +bool MapsEqual(const std::unordered_map& first, const std::unordered_map& second) +{ + if (first.size() != second.size()) + { + return false; + } + for (const auto& iter_first : first) + { + const auto iter_second = second.find(iter_first.first); + if (iter_second == second.end() || iter_first.second != iter_second->second) + { + return false; + } + } + return true; +} + eResult module::neighbor_update_interfaces(const common::idp::neighbor_update_interfaces::request& request) { + YANET_LOG_INFO("neighbor_update_interfaces\n"); + + // build new maps for next generation + decltype(generation_interface.next().interface_name_to_id) interface_name_to_id; + decltype(generation_interface.next().interface_id_to_name) interface_id_to_name; + for (const auto& [interface_id, route_name, interface_name] : request) + { + NEIGHBOR_INFO("neighbor_update_interfaces %d %s\n", interface_id, interface_name.c_str()); + interface_name_to_id[interface_name] = {route_name, interface_id}; + interface_id_to_name[interface_id] = {route_name, interface_name}; + } + + { + // check if there are any new keys + auto lock = generation_interface.current_lock_guard(); + if (HasNewKeys(generation_interface.current().interface_name_to_id, interface_name_to_id) || + HasNewKeys(generation_interface.current().interface_id_to_name, interface_id_to_name)) + { + // prepare new tmp maps + auto tmp_interface_name_to_id = generation_interface.current().interface_name_to_id; + CopyNewKeys(tmp_interface_name_to_id, interface_name_to_id); + auto tmp_interface_id_to_name = generation_interface.current().interface_id_to_name; + CopyNewKeys(tmp_interface_id_to_name, interface_id_to_name); + lock.unlock(); + + // update by new tmp maps and switch generation + generation_interface.next_lock(); + generation_interface.next().interface_name_to_id = tmp_interface_name_to_id; + generation_interface.next().interface_id_to_name = tmp_interface_id_to_name; + generation_interface.next_unlock(); + generation_interface.switch_generation(); + } + } + + // update next generations generation_interface.next_lock(); auto& generation = generation_interface.next(); - generation.interface_name_to_id.clear(); - generation.interface_id_to_name.clear(); - for (const auto& [interface_id, - route_name, - interface_name] : request) + generation.interface_name_to_id = interface_name_to_id; + generation.interface_id_to_name = interface_id_to_name; + generation_interface.next_unlock(); + + return eResult::success; +} + +eResult module::neighbor_interfaces_switch() +{ + YANET_LOG_INFO("neighbor_interfaces_switch\n"); + + // check if there have been any changes + generation_interface.current_lock(); + generation_interface.next_lock(); + bool changed = !MapsEqual(generation_interface.current().interface_id_to_name, generation_interface.next().interface_id_to_name) || + !MapsEqual(generation_interface.current().interface_name_to_id, generation_interface.next().interface_name_to_id); + generation_interface.next_unlock(); + generation_interface.current_unlock(); + + // switch generation + generation_interface.switch_generation(); + + if (changed) { - generation.interface_name_to_id[interface_name] = interface_id; - generation.interface_id_to_name[interface_id] = {route_name, interface_name}; + NEIGHBOR_INFO("neighbor_interfaces_switch changed\n"); +#ifdef CONFIG_YADECAP_AUTOTEST + UpdateFromCache(true, false); +#else // CONFIG_YADECAP_AUTOTEST + time_del_unused_ = current_time_provider_() + 30; + UpdateFromCache(false, false); +#endif // CONFIG_YADECAP_AUTOTEST } - generation_interface.switch_generation(); - generation_interface.next_unlock(); - std::lock_guard guard(mutex_restart_monitor_); - StopNetlinkMonitor(); - DumpOSNeighbors(); - StartNetlinkMonitor(); return eResult::success; } @@ -406,9 +457,14 @@ void module::StartResolveJob() { resolve_.Run([this]() mutable { std::vector keys = keys_to_resolve_provider_(); + uint32_t timestamp = current_time_provider_(); for (const auto& key : keys) { - resolve(key); + std::optional interface_name = GetInterfaceName(key.interface_id); + if (interface_name.has_value() && neighbor_cache_.NeedResolve(*interface_name, key.address, key.flags & flag_is_ipv6, timestamp)) + { + resolve(*interface_name, key.address, key.flags & flag_is_ipv6); + } } neighbor_flush(); @@ -419,10 +475,24 @@ void module::StartResolveJob() YANET_LOG_INFO("Neighbor resolve job started\n"); } -void module::Upsert(tInterfaceId iface, const ipv6_address_t& dst, bool is_v6, const rte_ether_addr& mac) +void module::Upsert(std::string iface_name, const ipv6_address_t& dst, bool is_v6, const rte_ether_addr& mac) { - TransformHashtables([k = key{iface, is_v6 ? flag_is_ipv6 : uint16_t{}, dst}, - v = value{mac, 0, current_time_provider_(), 0, 0, 0}, + NEIGHBOR_INFO("Upsert %s\n", netlink::Entry{iface_name, dst, mac, is_v6}.toString().c_str()); + bool is_static = false; +#ifdef CONFIG_YADECAP_UNITTEST + is_static = true; +#endif + neighbor_cache_.Insert(iface_name, dst, is_v6, mac, current_time_provider_(), is_static); + + std::optional iface = GetInterfaceId(iface_name); + if (!iface.has_value()) + { + stats.hashtable_insert_error++; + return; + } + + TransformHashtables([k = key{*iface, is_v6 ? flag_is_ipv6 : uint16_t{}, dst}, + v = value{mac}, this](dataplane::neighbor::hashtable& hashtable) { if (!hashtable.insert_or_update(k, v)) { @@ -435,106 +505,95 @@ void module::Upsert(tInterfaceId iface, const ipv6_address_t& dst, bool is_v6, c }); } -void module::UpdateTimestamp(tInterfaceId iface, const ipv6_address_t& dst, bool is_v6) +void module::UpdateTimestamp(std::string iface_name, const ipv6_address_t& dst, bool is_v6) { - TransformHashtables([k = key{iface, is_v6 ? flag_is_ipv6 : uint16_t{}, dst}, - this](dataplane::neighbor::hashtable& hashtable) { - dataplane::neighbor::value* value; - hashtable.lookup(k, value); - if (value) - { - value->last_update_timestamp = current_time_provider_(); - value->last_remove_timestamp = 0; - value->last_resolve_timestamp = 0; - value->number_resolve_after_remove = 0; - stats.hashtable_insert_success++; - } - else - { - stats.hashtable_insert_error++; - } - }); + NEIGHBOR_INFO("UpdateTimestamp %s\n", netlink::Entry{iface_name, dst, std::nullopt, is_v6}.toString().c_str()); + if (neighbor_cache_.UpdateTimestamp(iface_name, dst, is_v6, current_time_provider_())) + { + stats.hashtable_insert_success++; + } + else + { + stats.hashtable_insert_error++; + } } -void module::Remove(tInterfaceId iface, const ipv6_address_t& dst, bool is_v6) +void module::Remove(std::string iface_name, const ipv6_address_t& dst, bool is_v6) { - TransformHashtables([k = key{iface, is_v6 ? flag_is_ipv6 : uint16_t{}, dst}, - this](dataplane::neighbor::hashtable& hashtable) { - dataplane::neighbor::value* value; - hashtable.lookup(k, value); - if (value) - { - value->last_remove_timestamp = current_time_provider_(); - value->number_resolve_after_remove = 0; - stats.hashtable_remove_success++; - } - else - { - stats.hashtable_remove_error++; - } - }); + NEIGHBOR_INFO("Remove %s\n", netlink::Entry{iface_name, dst, std::nullopt, is_v6}.toString().c_str()); + bool is_static = false; +#ifdef CONFIG_YADECAP_UNITTEST + is_static = true; + time_del_unused_ = 1; +#endif + if (neighbor_cache_.Remove(iface_name, dst, is_v6, current_time_provider_(), is_static)) + { + stats.hashtable_remove_success++; + } + else + { + stats.hashtable_remove_error++; + } } -bool module::resolve(const dataplane::neighbor::key& key) +bool module::resolve(const std::string& interface_name, const ipv6_address_t& ip_address, bool is_v6) { stats.resolve++; - common::ip_address_t ip_address(key.flags & flag_is_ipv6 ? 6 : 4, key.address.bytes); - std::string interface_name; - { - auto lock = generation_interface.current_lock_guard(); - const auto& interface_id_to_name = generation_interface.current().interface_id_to_name; - auto it = interface_id_to_name.find(key.interface_id); - if (it == interface_id_to_name.end()) - { - YANET_LOG_ERROR("unknown interface_id: %u [ipv4_address: %s]\n", - key.interface_id, - ip_address.toString().data()); - return false; - } - - const auto& [it_route_name, it_interface_name] = it->second; - GCC_BUG_UNUSED(it_route_name); - - interface_name = it_interface_name; - } - + NEIGHBOR_DEBUG("neighbor resolve: %s, %s\n", + interface_name.c_str(), + common::ip_address_t(is_v6 ? 6 : 4, ip_address.bytes).toString().c_str()); YANET_LOG_DEBUG("resolve: %s, %s\n", interface_name.data(), - ip_address.toString().data()); + common::ip_address_t(is_v6 ? 6 : 4, ip_address.bytes).toString().data()); bool result = true; #ifdef CONFIG_YADECAP_AUTOTEST - YANET_LOG_INFO("Mocking resolve: %s, %s\n", - interface_name.data(), - ip_address.toString().data()); + NEIGHBOR_INFO("Mocking resolve: %s, %s\n", + interface_name.data(), + common::ip_address_t(is_v6 ? 6 : 4, ip_address.bytes).toString().data()); value value; value.ether_address.addr_bytes[0] = 44; value.ether_address.addr_bytes[1] = 44; - if (key.flags & flag_is_ipv6) + if (is_v6) { value.ether_address.addr_bytes[0] = 66; value.ether_address.addr_bytes[1] = 66; } + + dataplane::neighbor::key key; + memset(&key, 0, sizeof(key)); + key.address = ip_address; + if (is_v6) + { + + key.flags |= flag_is_ipv6; + } *((uint32_t*)&value.ether_address.addr_bytes[2]) = rte_hash_crc(key.address.bytes, 16, 0); - value.flags = 0 | flag_is_static; - value.last_update_timestamp = current_time_provider_(); - generation_hashtable.update([this, key, value](neighbor::generation_hashtable& hashtable) { - for (auto& [socket_id, hashtable_updater] : hashtable.hashtable_updater) - { - GCC_BUG_UNUSED(socket_id); - if (!hashtable_updater.get_pointer()->insert_or_update(key, value)) - { - stats.hashtable_insert_error++; - } - else + neighbor_cache_.Insert(interface_name, key.address, is_v6, value.ether_address, current_time_provider_(), false); + + std::optional interface_id = GetInterfaceId(interface_name); + if (interface_id.has_value()) + { + key.interface_id = *interface_id; + generation_hashtable.update([this, key, value](neighbor::generation_hashtable& hashtable) { + for (auto& [socket_id, hashtable_updater] : hashtable.hashtable_updater) { - stats.hashtable_insert_success++; + GCC_BUG_UNUSED(socket_id); + if (!hashtable_updater.get_pointer()->insert_or_update(key, value)) + { + stats.hashtable_insert_error++; + } + else + { + stats.hashtable_insert_success++; + } } - } - return eResult::success; - }); + return eResult::success; + }); + } + neighbor_flush(); #else // CONFIG_YADECAP_AUTOTEST @@ -542,7 +601,7 @@ bool module::resolve(const dataplane::neighbor::key& key) int family = AF_INET; int protocol = IPPROTO_ICMP; - if (key.flags & flag_is_ipv6) + if (is_v6) { family = AF_INET6; protocol = IPPROTO_ICMPV6; @@ -551,7 +610,9 @@ bool module::resolve(const dataplane::neighbor::key& key) int icmp_socket = socket(family, SOCK_RAW, protocol); if (icmp_socket == -1) { - YANET_LOG_WARNING("neighbor_resolve: socket(): %s\n", + YANET_LOG_WARNING("neighbor_resolve %s on %s: socket(): %s\n", + common::ip_address_t(is_v6 ? 6 : 4, ip_address.bytes).toString().data(), + interface_name.data(), strerror(errno)); return false; } @@ -563,7 +624,8 @@ bool module::resolve(const dataplane::neighbor::key& key) strlen(interface_name.data()) + 1); if (rc == -1) { - YANET_LOG_WARNING("neighbor_resolve: setsockopt(%s): %s\n", + YANET_LOG_WARNING("neighbor_resolve %s: setsockopt(%s): %s\n", + common::ip_address_t(is_v6 ? 6 : 4, ip_address.bytes).toString().data(), interface_name.data(), strerror(errno)); close(icmp_socket); @@ -579,11 +641,11 @@ bool module::resolve(const dataplane::neighbor::key& key) socklen_t address_length = sizeof(address_v4); - if (key.flags & flag_is_ipv6) + if (is_v6) { address_v6.sin6_family = AF_INET6; address_v6.sin6_port = 0; - memcpy(address_v6.sin6_addr.__in6_u.__u6_addr8, key.address.bytes, 16); + memcpy(address_v6.sin6_addr.__in6_u.__u6_addr8, ip_address.bytes, 16); address_length = sizeof(address_v6); } @@ -591,7 +653,7 @@ bool module::resolve(const dataplane::neighbor::key& key) { address_v4.sin_family = AF_INET; address_v4.sin_port = 0; - address_v4.sin_addr.s_addr = key.address.mapped_ipv4_address.address; + address_v4.sin_addr.s_addr = ip_address.mapped_ipv4_address.address; } icmphdr header; @@ -603,7 +665,9 @@ bool module::resolve(const dataplane::neighbor::key& key) &address, address_length) == -1) { - YANET_LOG_WARNING("neighbor_resolve: sendto(): %s\n", + YANET_LOG_WARNING("neighbor_resolve %s on %s: sendto(): %s\n", + common::ip_address_t(is_v6 ? 6 : 4, ip_address.bytes).toString().data(), + interface_name.data(), strerror(errno)); result = false; } @@ -625,37 +689,29 @@ void module::NeighborThreadAction(uint32_t current_time) } // find records to remove or resolve - std::vector keys_to_remove; - std::vector keys_to_resolve; - auto lock = generation_hashtable.current_lock_guard(); - const auto& hashtable_updater = generation_hashtable.current().hashtable_updater.begin()->second; - for (auto iter : hashtable_updater.range()) + auto [keys_to_remove, keys_to_resolve] = neighbor_cache_.GetKeysRemoveAndResolve(current_time_provider_()); + + // remove records + for (const key_cache& cur_key : keys_to_remove) { - if (!iter.is_valid()) - { - continue; - } - auto& key = *iter.key(); - auto& value = *iter.value(); - if ((value.last_remove_timestamp == 0) || (value.flags & flag_is_static)) + std::optional interface_id = GetInterfaceId(cur_key.iface_name); + if (!interface_id.has_value()) { + stats.hashtable_remove_error++; continue; } - else if (value.last_remove_timestamp + remove_timeout_ < current_time) - { - keys_to_remove.push_back(key); - } - else if (value.last_resolve_timestamp + checks_interval_ <= current_time && value.number_resolve_after_remove < resolve_removed_) + + dataplane::neighbor::key key_main; + memset(&key_main, 0, sizeof(key_main)); + key_main.interface_id = *interface_id; + key_main.address = cur_key.address; + if (cur_key.is_v6) { - keys_to_resolve.push_back(key); + key_main.flags |= flag_is_ipv6; } - } - // remove records - for (const key& cur_key : keys_to_remove) - { - TransformHashtables([cur_key, this](dataplane::neighbor::hashtable& hashtable) { - if (hashtable.remove(cur_key)) + TransformHashtables([key_main, this](dataplane::neighbor::hashtable& hashtable) { + if (hashtable.remove(key_main)) { stats.remove_final++; } @@ -667,27 +723,380 @@ void module::NeighborThreadAction(uint32_t current_time) } // resolve - for (const key& cur_key : keys_to_resolve) + for (const key_cache& cur_key : keys_to_resolve) { - if (!resolve(cur_key)) + if (resolve(cur_key.iface_name, cur_key.address, cur_key.is_v6)) { - continue; + neighbor_cache_.SetSentResolve(cur_key, current_time_provider_()); } - TransformHashtables([cur_key, current_time, this](dataplane::neighbor::hashtable& hashtable) { - dataplane::neighbor::value* value; - hashtable.lookup(cur_key, value); - if (value) + } + + // delete unused records + if (time_del_unused_ != 0 && current_time >= time_del_unused_) + { + time_del_unused_ = 0; + UpdateFromCache(true, false); + } +} + +std::optional module::GetInterfaceId(const std::string& iface_name) +{ + auto lock = generation_interface.current_lock_guard(); + const auto& interface_name_to_id = generation_interface.current().interface_name_to_id; + auto it = interface_name_to_id.find(iface_name); + if (it == interface_name_to_id.end()) + { + return std::nullopt; + } + + return std::get<1>(it->second); +} + +std::optional module::GetInterfaceIdNext(const std::string& iface_name) +{ + generation_interface.next_lock(); + const auto& interface_name_to_id = generation_interface.next().interface_name_to_id; + auto it = interface_name_to_id.find(iface_name); + if (it == interface_name_to_id.end()) + { + generation_interface.next_unlock(); + return std::nullopt; + } + + tInterfaceId id = std::get<1>(it->second); + generation_interface.next_unlock(); + return id; +} + +std::optional module::GetInterfaceName(tInterfaceId iface_id) +{ + auto lock = generation_interface.current_lock_guard(); + const auto& interface_id_to_name = generation_interface.current().interface_id_to_name; + auto it = interface_id_to_name.find(iface_id); + if (it == interface_id_to_name.end()) + { + return std::nullopt; + } + + const auto& [it_route_name, it_interface_name] = it->second; + GCC_BUG_UNUSED(it_route_name); + + return it_interface_name; +} + +void module::UpdateFromCache(bool remove_old, bool use_next_generation) +{ + std::lock_guard lock_cache = neighbor_cache_.LockGuard(); + std::map data = neighbor_cache_.GetData(); + + // insert or update all values + for (const auto& [cur_key, cur_value] : data) + { + std::optional iface_id = (use_next_generation ? GetInterfaceIdNext(cur_key.iface_name) : GetInterfaceId(cur_key.iface_name)); + if (iface_id.has_value()) + { + NEIGHBOR_DEBUG("UpdateFromCache %s iface_id=%d\n", netlink::Entry{cur_key.iface_name, cur_key.address, cur_value.ether_address, cur_key.is_v6}.toString().c_str(), *iface_id); + TransformHashtables([k = key{*iface_id, cur_key.is_v6 ? flag_is_ipv6 : uint16_t{}, cur_key.address}, + v = value{cur_value.ether_address}, + this](dataplane::neighbor::hashtable& hashtable) { + if (!hashtable.insert_or_update(k, v)) + { + stats.hashtable_insert_error++; + } + else + { + stats.hashtable_insert_success++; + } + }); + } + else + { + NEIGHBOR_DEBUG("UpdateFromCache %s iface_id=null\n", netlink::Entry{cur_key.iface_name, cur_key.address, cur_value.ether_address, cur_key.is_v6}.toString().c_str()); + stats.hashtable_insert_error++; + } + } + + // remove unused + if (remove_old) + { + std::set keys_del; + { + auto lock = generation_hashtable.current_lock_guard(); + for (auto it : generation_hashtable.current().hashtable_updater.begin()->second.range()) { - value->last_resolve_timestamp = current_time; - value->number_resolve_after_remove++; - stats.resolve_removed++; + if (it.is_valid()) + { + const key& cur_key = *it.key(); + bool exists = false; + std::optional iface_name = GetInterfaceName(cur_key.interface_id); + if (iface_name.has_value()) + { + key_cache check_key{*iface_name, (cur_key.flags & flag_is_ipv6) != 0, cur_key.address}; + if (data.find(check_key) != data.end()) + { + exists = true; + } + } + NEIGHBOR_DEBUG("UpdateFromCache key_exists=%d %s iface_id=%d\n", exists, netlink::Entry{"", cur_key.address, std::nullopt, true}.toString().c_str(), cur_key.interface_id); + + if (!exists) + { + keys_del.insert(cur_key); + } + } } - else + } + + for (const key& key_main : keys_del) + { + NEIGHBOR_DEBUG("UpdateFromCache %s iface_id=%d\n", netlink::Entry{"", key_main.address, std::nullopt, true}.toString().c_str(), key_main.interface_id); + TransformHashtables([key_main, this](dataplane::neighbor::hashtable& hashtable) { + if (hashtable.remove(key_main)) + { + stats.remove_final++; + } + else + { + stats.hashtable_remove_error++; + } + }); + } + } + + // flush + neighbor_flush(); +} + +void NeighborCache::Init(uint64_t checks_interval, uint64_t remove_timeout, uint64_t resolve_removed) +{ + checks_interval_ = checks_interval; + remove_timeout_ = remove_timeout; + resolve_removed_ = resolve_removed; +} + +bool NeighborCache::UpdateTimestamp(std::string iface_name, const ipv6_address_t& dst, bool is_v6, uint32_t timestamp) +{ + key_cache key; + key.iface_name = iface_name; + key.is_v6 = is_v6; + key.address = dst; + + std::lock_guard guard(mutex_); + auto iter = data_.find(key); + if (iter == data_.end()) + { + return false; + } + + dataplane::neighbor::value_cache& value = iter->second; + value.last_update_timestamp = timestamp; + value.last_remove_timestamp = 0; + value.last_resolve_timestamp = 0; + value.number_resolve_after_remove = 0; + + return true; +} + +void NeighborCache::Insert(const std::string& iface_name, const ipv6_address_t& dst, bool is_v6, const rte_ether_addr& mac, uint32_t timestamp, bool is_static) +{ + key_cache key; + key.iface_name = iface_name; + key.is_v6 = is_v6; + key.address = dst; + + std::lock_guard guard(mutex_); + if (!is_static) + { + auto iter = data_.find(key); + if (iter != data_.end() && iter->second.is_static) + { + return; + } + } + + dataplane::neighbor::value_cache value; + memcpy(value.ether_address.addr_bytes, mac.addr_bytes, 6); + value.is_static = is_static; + value.last_update_timestamp = timestamp; + value.last_remove_timestamp = 0; + value.last_resolve_timestamp = 0; + value.number_resolve_after_remove = 0; + + data_[key] = value; +} + +bool NeighborCache::Remove(std::string iface_name, const ipv6_address_t& dst, bool is_v6, uint32_t timestamp, bool is_static) +{ + key_cache key; + key.iface_name = iface_name; + key.is_v6 = is_v6; + key.address = dst; + + std::lock_guard guard(mutex_); + auto iter = data_.find(key); + if (iter == data_.end()) + { + return false; + } + +#ifdef CONFIG_YADECAP_AUTOTEST + data_.erase(key); +#else // CONFIG_YADECAP_AUTOTEST + if (is_static) + { + data_.erase(key); + } + else + { + dataplane::neighbor::value_cache& value = iter->second; + if (value.last_remove_timestamp == 0) + { + value.last_remove_timestamp = timestamp; + value.number_resolve_after_remove = 0; + } + } +#endif // CONFIG_YADECAP_AUTOTEST + + return true; +} + +common::idp::neighbor_show_cache::response NeighborCache::NeighborShow(uint32_t timestamp) const +{ + std::lock_guard guard(mutex_); + common::idp::neighbor_show_cache::response response; + for (const auto& [key, value] : data_) + { + std::optional last_update_timestamp; + if (!value.is_static) + { + last_update_timestamp = timestamp - value.last_update_timestamp; + } + + std::string last_remove_timestamp; + if (value.last_remove_timestamp != 0) + { + last_remove_timestamp = std::to_string(timestamp - value.last_remove_timestamp); + } + + response.emplace_back(key.iface_name, + common::ip_address_t(key.is_v6 ? 6 : 4, key.address.bytes), + common::mac_address_t(value.ether_address.addr_bytes), + last_update_timestamp, + last_remove_timestamp); + } + + return response; +} + +void NeighborCache::UpdateFromDump(const std::vector& dump, uint32_t timestamp) +{ + std::set all_keys; + for (const netlink::Entry& entry : dump) + { + if (entry.mac.has_value()) + { + Insert(entry.ifname, entry.dst, entry.v6, *entry.mac, timestamp, false); + all_keys.insert({entry.ifname, entry.v6, entry.dst}); + } + } + + std::set keys_del; + { + std::lock_guard guard(mutex_); + for (const auto& iter : data_) + { + if (all_keys.find(iter.first) == all_keys.end()) { - stats.hashtable_insert_error++; + keys_del.insert(iter.first); } - }); + } + } + + for (const key_cache& key : keys_del) + { + bool is_static = false; +#ifdef CONFIG_YADECAP_UNITTEST + is_static = true; +#endif + Remove(key.iface_name, key.address, key.is_v6, timestamp, is_static); } } -} // namespace dataplane::neighbor \ No newline at end of file +bool NeighborCache::NeedResolve(std::string iface_name, const ipv6_address_t& dst, bool is_v6, uint32_t timestamp) +{ + key_cache key; + key.iface_name = iface_name; + key.is_v6 = is_v6; + key.address = dst; + + std::lock_guard guard(mutex_); + auto iter = data_.find(key); + if (iter == data_.end()) + { + return true; + } + + dataplane::neighbor::value_cache& value = iter->second; + return value.last_resolve_timestamp != timestamp; +} + +std::pair, std::vector> NeighborCache::GetKeysRemoveAndResolve(uint32_t timestamp) +{ + std::vector keys_to_remove; + std::vector keys_to_resolve; + + std::lock_guard guard(mutex_); + for (const auto& [key, value] : data_) + { + NEIGHBOR_DEBUG("NeighborCache::GetKeysRemoveAndResolve check %s, timestamp=%d, last_remove_timestamp=%d, is_static=%d\n", + netlink::Entry{key.iface_name, key.address, std::nullopt, key.is_v6}.toString().c_str(), + timestamp, + value.last_remove_timestamp, + value.is_static); + if ((value.last_remove_timestamp == 0) || value.is_static) + { + continue; + } + else if (value.last_remove_timestamp + remove_timeout_ < timestamp) + { + keys_to_remove.push_back(key); + } + else if (value.last_resolve_timestamp + checks_interval_ <= timestamp && value.number_resolve_after_remove < resolve_removed_) + { + keys_to_resolve.push_back(key); + } + } + + for (const key_cache& key : keys_to_remove) + { + data_.erase(key); + } + + return {keys_to_remove, keys_to_resolve}; +} + +void NeighborCache::SetSentResolve(const key_cache& key, uint32_t timestamp) +{ + std::lock_guard guard(mutex_); + auto iter = data_.find(key); + if (iter == data_.end()) + { + return; + } + + dataplane::neighbor::value_cache& value = iter->second; + value.last_resolve_timestamp = timestamp; + value.number_resolve_after_remove++; +} + +std::map NeighborCache::GetData() const +{ + return data_; +} + +std::lock_guard NeighborCache::LockGuard() const +{ + return std::lock_guard(mutex_); +} + +} // namespace dataplane::neighbor diff --git a/dataplane/neighbor.h b/dataplane/neighbor.h index e324d9ee..cb5efd32 100644 --- a/dataplane/neighbor.h +++ b/dataplane/neighbor.h @@ -48,16 +48,71 @@ struct key } }; +struct key_cache +{ + std::string iface_name; + bool is_v6; + ipv6_address_t address; + + bool operator<(const key_cache& second) const + { + if (iface_name != second.iface_name) + { + return iface_name < second.iface_name; + } + else if (is_v6 != second.is_v6) + { + return is_v6 < second.is_v6; + } + else + { + return address < second.address; + } + } +}; + static_assert(CONFIG_YADECAP_INTERFACES_SIZE <= 0xFFFF, "invalid size"); struct value { rte_ether_addr ether_address; - uint16_t flags; - uint32_t last_update_timestamp; - uint32_t last_remove_timestamp; - uint32_t last_resolve_timestamp; - uint32_t number_resolve_after_remove; +}; + +struct value_cache +{ + rte_ether_addr ether_address; + bool is_static = false; + uint32_t last_update_timestamp = 0; + uint32_t last_remove_timestamp = 0; + uint32_t last_resolve_timestamp = 0; + uint32_t number_resolve_after_remove = 0; +}; + +class NeighborCache +{ +public: + void Init(uint64_t checks_interval, uint64_t remove_timeout, uint64_t resolve_removed); + void Insert(const std::string& iface_name, const ipv6_address_t& dst, bool is_v6, const rte_ether_addr& mac, uint32_t timestamp, bool is_static); + bool UpdateTimestamp(std::string iface_name, const ipv6_address_t& dst, bool is_v6, uint32_t timestamp); + bool Remove(std::string iface_name, const ipv6_address_t& dst, bool is_v6, uint32_t timestamp, bool is_static); + void UpdateFromDump(const std::vector& dump, uint32_t timestamp); + bool NeedResolve(std::string iface_name, const ipv6_address_t& dst, bool is_v6, uint32_t timestamp); + + common::idp::neighbor_show_cache::response NeighborShow(uint32_t timestamp) const; + + std::pair, std::vector> GetKeysRemoveAndResolve(uint32_t timestamp); + void SetSentResolve(const key_cache& key, uint32_t timestamp); + + std::map GetData() const; + std::lock_guard LockGuard() const; + +private: + mutable std::mutex mutex_; + std::map data_; + + uint64_t checks_interval_ = YANET_CONFIG_NEIGHBOR_CHECK_INTERVAL; + uint64_t remove_timeout_ = YANET_CONFIG_NEIGHBOR_REMOVE_TIMEOUT; + uint64_t resolve_removed_ = YANET_CONFIG_RESOLVE_REMOVED; }; // @@ -69,7 +124,10 @@ using hashtable = hashtable_mod_dynamic; class generation_interface { public: - std::unordered_map interface_name_to_id; + std::unordered_map> ///< interface_id + interface_name_to_id; std::unordered_map> ///< interface_name @@ -111,19 +169,22 @@ class module void update_worker_base(const std::vector>& base_nexts); common::idp::neighbor_show::response neighbor_show() const; + common::idp::neighbor_show_cache::response neighbor_show_cache() const; eResult neighbor_insert(const common::idp::neighbor_insert::request& request); eResult neighbor_remove(const common::idp::neighbor_remove::request& request); eResult neighbor_clear(); eResult neighbor_flush(); eResult neighbor_update_interfaces(const common::idp::neighbor_update_interfaces::request& request); + eResult neighbor_interfaces_switch(); common::idp::neighbor_stats::response neighbor_stats() const; void report(nlohmann::json& json); - void Upsert(tInterfaceId iface, const ipv6_address_t& dst, bool is_v6, const rte_ether_addr& mac); - void UpdateTimestamp(tInterfaceId iface, const ipv6_address_t& dst, bool is_v6); - void Remove(tInterfaceId iface, const ipv6_address_t& dst, bool is_v6); + void Upsert(std::string iface_name, const ipv6_address_t& dst, bool is_v6, const rte_ether_addr& mac); + void UpdateTimestamp(std::string iface_name, const ipv6_address_t& dst, bool is_v6); + void Remove(std::string iface_name, const ipv6_address_t& dst, bool is_v6); void NeighborThreadAction(uint32_t current_time); + void UpdateFromCache(bool remove_old, bool use_next_generation); protected: void StartResolveJob(); @@ -131,7 +192,10 @@ class module void StopNetlinkMonitor(); eResult DumpOSNeighbors(); - bool resolve(const dataplane::neighbor::key& key); + bool resolve(const std::string& interface_name, const ipv6_address_t& ip_address, bool is_v6); + std::optional GetInterfaceId(const std::string& iface_name); + std::optional GetInterfaceIdNext(const std::string& iface_name); + std::optional GetInterfaceName(tInterfaceId iface_id); protected: generation_manager generation_interface; @@ -147,6 +211,8 @@ class module void TransformHashtables(UpdaterFunc&& updater); utils::Job resolve_; + NeighborCache neighbor_cache_; + std::atomic time_del_unused_ = 0; }; template diff --git a/dataplane/netlink.cpp b/dataplane/netlink.cpp index a53e2d3f..c6890f88 100644 --- a/dataplane/netlink.cpp +++ b/dataplane/netlink.cpp @@ -9,9 +9,26 @@ namespace netlink { -std::variant ParseNeighbor( - rtnl_neigh* neigh, - std::function(const char*)> get_id) +std::string Entry::toString() const +{ + std::stringstream ss; + ss << "ifname=" << ifname; + if (v6) + { + ss << ", addr=" << common::ipv6_address_t(dst.bytes).toString(); + } + else + { + ss << ", addr=" << common::ipv4_address_t(rte_cpu_to_be_32(dst.mapped_ipv4_address.address)).toString(); + } + if (mac.has_value()) + { + ss << ", mac=" << common::mac_address_t(mac->addr_bytes).toString(); + } + return ss.str(); +} + +std::variant ParseNeighbor(rtnl_neigh* neigh) { int sysifid = rtnl_neigh_get_ifindex(neigh); Entry entry; @@ -21,15 +38,7 @@ std::variant ParseNeighbor( YANET_LOG_INFO("Skipping message for unknown OS interface '%i'\n", sysifid); return NL_OK; } - if (auto id = get_id(ifname); id.has_value()) - { - entry.id = id.value(); - } - else - { - YANET_LOG_INFO("Skipping message for unconfigured interface '%s'\n", ifname); - return NL_OK; - } + entry.ifname = ifname; nl_addr* oaddr = rtnl_neigh_get_dst(neigh); if (!oaddr) @@ -68,8 +77,7 @@ std::variant ParseNeighbor( return entry; } -std::vector Provider::GetHostDump(unsigned rcvbuf_size, - const std::unordered_map& ids) +std::vector Provider::GetHostDump(unsigned rcvbuf_size) { auto deleter = [](nl_sock* sk) { nl_socket_free(sk); }; std::unique_ptr usk{nl_socket_alloc(), deleter}; @@ -101,13 +109,7 @@ std::vector Provider::GetHostDump(unsigned rcvbuf_size, return NL_OK; } - auto var = ParseNeighbor(neigh, [&](const char* name) -> std::optional { - if (auto it = ids.find(name); it != ids.end()) - { - return it->second; - } - return std::nullopt; - }); + auto var = ParseNeighbor(neigh); if (!std::holds_alternative(var)) { @@ -154,10 +156,9 @@ std::vector Provider::GetHostDump(unsigned rcvbuf_size, } void Provider::StartMonitor(unsigned rcvbuf_size, - std::function(const char*)> get_id, - std::function upsert, - std::function remove, - std::function timestamp) + std::function upsert, + std::function remove, + std::function timestamp) { auto deleter = [](nl_sock* sk) { nl_socket_free(sk); }; std::unique_ptr usk{nl_socket_alloc(), deleter}; @@ -181,7 +182,7 @@ void Provider::StartMonitor(unsigned rcvbuf_size, } } - monitor_callback_ = [get_id, upsert, remove, timestamp](nl_msg* msg) -> int { + monitor_callback_ = [upsert, remove, timestamp](nl_msg* msg) -> int { nlmsghdr* msghdr = nlmsg_hdr(msg); if (msghdr->nlmsg_type != RTM_NEWNEIGH && msghdr->nlmsg_type != RTM_DELNEIGH) { @@ -201,7 +202,7 @@ void Provider::StartMonitor(unsigned rcvbuf_size, return NL_OK; } - auto parsed = ParseNeighbor(neigh, get_id); + auto parsed = ParseNeighbor(neigh); if (!std::holds_alternative(parsed)) { diff --git a/dataplane/netlink.hpp b/dataplane/netlink.hpp index b95aa513..ad4b37c8 100644 --- a/dataplane/netlink.hpp +++ b/dataplane/netlink.hpp @@ -15,22 +15,22 @@ namespace netlink struct Entry { - tInterfaceId id; + std::string ifname; ipv6_address_t dst; std::optional mac; bool v6; + + std::string toString() const; }; class Interface { public: - virtual std::vector GetHostDump(unsigned rcvbuf_size, - const std::unordered_map& ids) = 0; + virtual std::vector GetHostDump(unsigned rcvbuf_size) = 0; virtual void StartMonitor(unsigned rcvbuf_size, - std::function(const char*)> get_id, - std::function upsert, - std::function remove, - std::function timestamp) = 0; + std::function upsert, + std::function remove, + std::function timestamp) = 0; virtual void StopMonitor() = 0; virtual ~Interface() = default; virtual bool IsFailedWorkMonitor() = 0; @@ -42,21 +42,19 @@ class Provider : public Interface nl_sock* sk_; std::function monitor_callback_; - std::function upsert_; - std::function remove_; - std::function timestamp_; + std::function upsert_; + std::function remove_; + std::function timestamp_; utils::Job monitor_; std::atomic failed_work_monitor_{false}; public: - std::vector GetHostDump(unsigned rcvbuf_size, - const std::unordered_map& ids) final; + std::vector GetHostDump(unsigned rcvbuf_size) final; void StartMonitor(unsigned rcvbuf_size, - std::function(const char*)> get_id, - std::function upsert, - std::function remove, - std::function timestamp) final; + std::function upsert, + std::function remove, + std::function timestamp) final; void StopMonitor() final; ~Provider() final; bool IsFailedWorkMonitor() final; diff --git a/dataplane/unittest/meson.build b/dataplane/unittest/meson.build index 3beb3acf..3c597c75 100644 --- a/dataplane/unittest/meson.build +++ b/dataplane/unittest/meson.build @@ -12,7 +12,7 @@ sources = files('unittest.cpp', 'sdp.cpp') arch = 'corei7' -cpp_args_append = ['-march=' + arch] +cpp_args_append = ['-march=' + arch, '-DCONFIG_YADECAP_UNITTEST'] unittest = executable('yanet-dataplane-unittest', sources, diff --git a/dataplane/unittest/neighbor.cpp b/dataplane/unittest/neighbor.cpp index b7ecddc4..76c8cf56 100644 --- a/dataplane/unittest/neighbor.cpp +++ b/dataplane/unittest/neighbor.cpp @@ -10,16 +10,14 @@ namespace class MockProvider final : public netlink::Interface { public: - std::vector GetHostDump(unsigned rcvbuf_size, - const std::unordered_map& ids) + std::vector GetHostDump(unsigned rcvbuf_size) { return dump_; } void StartMonitor(unsigned rcvbuf_size, - std::function(const char*)> get_id, - std::function upsert, - std::function remove, - std::function timestamp) + std::function upsert, + std::function remove, + std::function timestamp) { upsert_ = std::move(upsert); timestamp_ = std::move(timestamp); @@ -33,9 +31,9 @@ class MockProvider final : public netlink::Interface { return false; } - std::function upsert_; - std::function timestamp_; - std::function remove_; + std::function upsert_; + std::function timestamp_; + std::function remove_; std::vector dump_; }; @@ -80,15 +78,6 @@ std::ostream& operator<<(std::ostream& os, entry_t e) << std::get<1>(e) << ' ' << std::get(e).toString() << ' ' << std::get(e).toString(); - if (auto& ts = std::get<4>(e)) - { - os << ' ' << ts.value(); - } - else - { - os << " {}"; - } - os << ' ' << std::get<5>(e); return os; } @@ -97,9 +86,7 @@ bool equal(entry_t a, entry_t b) return std::get<0>(a) == std::get<0>(b) && std::get<1>(a) == std::get<1>(b) && std::get<2>(a) == std::get<2>(b) && - std::get<3>(a) == std::get<3>(b) && - std::get<4>(a) == std::get<4>(b) && - std::get<5>(a) == std::get<5>(b); + std::get<3>(a) == std::get<3>(b); } bool equal(response_t a, response_t b) @@ -155,8 +142,9 @@ TEST(NeighborTest, Basic) dut.neighbor_update_interfaces({{1, "route0", "kni1"}}); common::idp::neighbor_show::response expected = { - {"route0", "kni1", Common4FromString("192.168.1.1"), {"DE:AD:BE:EF:01:02"}, {1}, {}}}; - dut.Upsert(1, Ip6FromString("192.168.1.1"), false, EthFromString("DE:AD:BE:EF:01:02")); + {"route0", "kni1", Common4FromString("192.168.1.1"), {"DE:AD:BE:EF:01:02"}}}; + dut.neighbor_interfaces_switch(); + dut.Upsert("kni1", Ip6FromString("192.168.1.1"), false, EthFromString("DE:AD:BE:EF:01:02")); dut.neighbor_flush(); now = 2; @@ -167,42 +155,42 @@ TEST(NeighborTest, Basic) EXPECT_EQ(dut.neighbor_show(), expected); now = 3; - dut.Upsert(1, Ip6FromString("100.200.1.2"), false, EthFromString("DE:AD:BE:EF:08:08")); + dut.Upsert("kni1", Ip6FromString("100.200.1.2"), false, EthFromString("DE:AD:BE:EF:08:08")); dut.neighbor_flush(); expected = { - {"route0", "kni1", Common4FromString("192.168.1.1"), {"DE:AD:BE:EF:01:02"}, {2}, {}}, - {"route0", "kni1", Common4FromString("100.200.1.2"), {"DE:AD:BE:EF:08:08"}, {0}, {}}}; + {"route0", "kni1", Common4FromString("192.168.1.1"), {"DE:AD:BE:EF:01:02"}}, + {"route0", "kni1", Common4FromString("100.200.1.2"), {"DE:AD:BE:EF:08:08"}}}; EXPECT_TRUE(equal(dut.neighbor_show(), expected)); dut.neighbor_flush(); EXPECT_TRUE(equal(dut.neighbor_show(), expected)); now = 4; - dut.UpdateTimestamp(1, Ip6FromString("100.200.1.2"), false); + dut.UpdateTimestamp("kni1", Ip6FromString("100.200.1.2"), false); dut.neighbor_flush(); expected = { - {"route0", "kni1", Common4FromString("192.168.1.1"), {"DE:AD:BE:EF:01:02"}, {3}, {}}, - {"route0", "kni1", Common4FromString("100.200.1.2"), {"DE:AD:BE:EF:08:08"}, {0}, {}}}; + {"route0", "kni1", Common4FromString("192.168.1.1"), {"DE:AD:BE:EF:01:02"}}, + {"route0", "kni1", Common4FromString("100.200.1.2"), {"DE:AD:BE:EF:08:08"}}}; EXPECT_TRUE(equal(dut.neighbor_show(), expected)); dut.neighbor_flush(); EXPECT_TRUE(equal(dut.neighbor_show(), expected)); now = 5; - dut.Remove(1, Ip6FromString("192.168.1.1"), false); + dut.Remove("kni1", Ip6FromString("192.168.1.1"), false); dut.neighbor_flush(); now = 7; expected = { - {"route0", "kni1", Common4FromString("192.168.1.1"), {"DE:AD:BE:EF:01:02"}, 6, "2"}, - {"route0", "kni1", Common4FromString("100.200.1.2"), {"DE:AD:BE:EF:08:08"}, {3}, {}}}; + {"route0", "kni1", Common4FromString("192.168.1.1"), {"DE:AD:BE:EF:01:02"}}, + {"route0", "kni1", Common4FromString("100.200.1.2"), {"DE:AD:BE:EF:08:08"}}}; EXPECT_TRUE(equal(dut.neighbor_show(), expected)); dut.neighbor_flush(); EXPECT_TRUE(equal(dut.neighbor_show(), expected)); - now = 9; + now = 1000; dut.NeighborThreadAction(now); dut.neighbor_flush(); expected = { - {"route0", "kni1", Common4FromString("100.200.1.2"), {"DE:AD:BE:EF:08:08"}, {5}, {}}}; + {"route0", "kni1", Common4FromString("100.200.1.2"), {"DE:AD:BE:EF:08:08"}}}; EXPECT_TRUE(equal(dut.neighbor_show(), expected)); dut.neighbor_flush(); @@ -241,14 +229,14 @@ TEST(NeighborTest, Provider) EXPECT_TRUE(equal(dut.neighbor_show(), {})); mock->dump_ = { - {1, Ip6FromString("192.168.1.1"), EthFromString("DE:AD:BE:EF:01:02"), false}, - {1, Ip6FromString("100.200.1.2"), EthFromString("DE:AD:BE:EF:08:08"), false}}; + {"kni1", Ip6FromString("192.168.1.1"), EthFromString("DE:AD:BE:EF:01:02"), false}, + {"kni1", Ip6FromString("100.200.1.2"), EthFromString("DE:AD:BE:EF:08:08"), false}}; dut.neighbor_clear(); common::idp::neighbor_show::response expected = { - {"route0", "kni1", Common4FromString("192.168.1.1"), {"DE:AD:BE:EF:01:02"}, {0}, {}}, - {"route0", "kni1", Common4FromString("100.200.1.2"), {"DE:AD:BE:EF:08:08"}, {0}, {}}}; + {"route0", "kni1", Common4FromString("192.168.1.1"), {"DE:AD:BE:EF:01:02"}}, + {"route0", "kni1", Common4FromString("100.200.1.2"), {"DE:AD:BE:EF:08:08"}}}; EXPECT_TRUE(equal(dut.neighbor_show(), expected)); }