diff --git a/orchagent/Makefile.am b/orchagent/Makefile.am index 8f3b3bc17a0..adb3ef54196 100644 --- a/orchagent/Makefile.am +++ b/orchagent/Makefile.am @@ -103,6 +103,7 @@ orchagent_SOURCES = \ macsecorch.cpp \ lagid.cpp \ bfdorch.cpp \ + icmporch.cpp \ srv6orch.cpp \ response_publisher.cpp \ nvgreorch.cpp \ diff --git a/orchagent/icmporch.cpp b/orchagent/icmporch.cpp new file mode 100644 index 00000000000..29788136470 --- /dev/null +++ b/orchagent/icmporch.cpp @@ -0,0 +1,628 @@ +/* + * icmporch.cpp + * + * Created on: Feb 21, 2025 + * Author: Manas Kumar Mandal + */ + +#include "converter.h" +#include "swssnet.h" +#include "notifier.h" +#include "sai_serialize.h" +#include "directory.h" +#include "notifications.h" +#include "icmporch.h" +#include "switchorch.h" +#include + +using namespace std; +using namespace swss; + +extern SwitchOrch *gSwitchOrch; + +const uint32_t IcmpOrch::m_max_sessions = 1024; + +const std::map IcmpOrch::m_session_state_lkup = +{ + {SAI_ICMP_ECHO_SESSION_STATE_DOWN, "Down"}, + {SAI_ICMP_ECHO_SESSION_STATE_UP, "Up"} +}; + +const std::map IcmpOrch::m_session_state_str_lkup = +{ + {"Down", SAI_ICMP_ECHO_SESSION_STATE_DOWN}, + {"Up", SAI_ICMP_ECHO_SESSION_STATE_UP} +}; + +IcmpOrch::IcmpOrch(DBConnector *db, string tableName, TableConnector stateDbIcmpSessionTable): + Orch(db, tableName), + m_stateIcmpSessionTable(stateDbIcmpSessionTable.first, stateDbIcmpSessionTable.second), + m_register_state_change_notif{false} +{ + SWSS_LOG_ENTER(); + + // check the capability + std::string offload_capable; + gSwitchOrch->get_switch_capability("ICMP_OFFLOAD_CAPABLE", offload_capable); + if (offload_capable != "true") + { + SWSS_LOG_NOTICE("ICMP offload not supported"); + return; + } + + DBConnector *notificationsDb = new DBConnector("ASIC_DB", 0); + m_icmpStateNotificationConsumer = new swss::NotificationConsumer(notificationsDb, "NOTIFICATIONS"); + + // Clean up state database ICMP entries + vector keys; + + m_stateIcmpSessionTable.getKeys(keys); + + for (auto alias : keys) + { + m_stateIcmpSessionTable.del(alias); + } + + auto icmpStateNotifier = new Notifier(m_icmpStateNotificationConsumer, this, "ICMP_STATE_NOTIFICATIONS"); + Orch::addExecutor(icmpStateNotifier); +} + +IcmpOrch::~IcmpOrch(void) +{ + // do nothing, just log + SWSS_LOG_ENTER(); +} + +void IcmpOrch::doTask(Consumer &consumer) +{ + SWSS_LOG_ENTER(); + + auto it = consumer.m_toSync.begin(); + while (it != consumer.m_toSync.end()) + { + KeyOpFieldsValuesTuple t = it->second; + + string key = kfvKey(t); + string op = kfvOp(t); + auto data = kfvFieldsValues(t); + + if (op == SET_COMMAND) + { + if (m_icmp_session_map.find(key) != m_icmp_session_map.end()) + { + if (!update_icmp_session(key, data)) + { + it++; + continue; + } + } else { + if (!create_icmp_session(key, data)) + { + it++; + continue; + } + } + } + else if (op == DEL_COMMAND) + { + if (!remove_icmp_session(key)) + { + it++; + continue; + } + } + else + { + SWSS_LOG_ERROR("Unknown operation type %s\n", op.c_str()); + } + + it = consumer.m_toSync.erase(it); + } +} + +void IcmpOrch::doTask(NotificationConsumer &consumer) +{ + SWSS_LOG_ENTER(); + + std::string op; + std::string data; + std::vector values; + + consumer.pop(op, data, values); + + if (&consumer != m_icmpStateNotificationConsumer) + { + return; + } + + if (op == "icmp_echo_session_state_change") + { + uint32_t count = 0; + sai_icmp_echo_session_state_notification_t *icmpSessionState = nullptr; + + sai_deserialize_icmp_echo_session_state_ntf(data, count, &icmpSessionState); + + for (uint32_t i = 0; i < count; i++) + { + sai_object_id_t id = icmpSessionState[i].icmp_echo_session_id; + sai_icmp_echo_session_state_t state = icmpSessionState[i].session_state; + + SWSS_LOG_INFO("Got ICMP session state change notification id:%" PRIx64 " state: %s", id, m_session_state_lkup.at(state).c_str()); + + if (m_icmp_session_lookup.find(id) == m_icmp_session_lookup.end()) + { + SWSS_LOG_NOTICE("ICMP session missing for state change notification id:%" PRIx64 " state: %s", id, m_session_state_lkup.at(state).c_str()); + continue; + } + + // handle state update + if (state != m_icmp_session_lookup[id].state || m_icmp_session_lookup[id].init_state) + { + auto key = m_icmp_session_lookup[id].db_key; + vector fvVector; + m_stateIcmpSessionTable.get(key, fvVector); + + fvVector.push_back({IcmpSaiSessionHandler::m_state_fname, m_session_state_lkup.at(state)}); + + m_stateIcmpSessionTable.set(key, fvVector); + + SWSS_LOG_NOTICE("ICMP session state for %s changed from %s to %s", key.c_str(), + m_session_state_lkup.at(m_icmp_session_lookup[id].state).c_str(), m_session_state_lkup.at(state).c_str()); + + m_icmp_session_lookup[id].state = state; + m_icmp_session_lookup[id].init_state = false; + } + } + + sai_deserialize_free_icmp_echo_session_state_ntf(count, icmpSessionState); + } +} + +bool IcmpOrch::create_icmp_session(const string& key, const vector& data) +{ + IcmpSaiSessionHandler sai_session_handler(*this); + + if (m_num_sessions == m_max_sessions) + { + SWSS_LOG_ERROR("ICMP session creation failed, limit (%u) reached", m_num_sessions); + // return false to retry + return false; + } + + // initialize the sai session handler + auto init_status = sai_session_handler.init(sai_icmp_echo_api, key); + if (init_status != SaiOffloadHandlerStatus::SUCCESS_VALID_ENTRY) + { + SWSS_LOG_INFO("ICMP session creation failed key(%s), init_status(%s)", key.c_str(), + SaiOffloadStatusStrMap.at(init_status).c_str()); + return true; + } + + if (!m_register_state_change_notif) + { + if (!sai_session_handler.register_state_change_notification()) + { + // return false to retry registration + return false; + } + m_register_state_change_notif = true; + } + + auto create_status = sai_session_handler.create(data); + if (create_status != SaiOffloadHandlerStatus::SUCCESS_VALID_ENTRY) + { + SWSS_LOG_INFO("ICMP session creation failed key(%s), create_status(%s)", key.c_str(), + SaiOffloadStatusStrMap.at(create_status).c_str()); + // do not consume the entry for retries + bool skip_entry = create_status != SaiOffloadHandlerStatus::RETRY_VALID_ENTRY; + return skip_entry; + } + + // update the STATE DB and local session maps + auto& fvVector = sai_session_handler.get_fv_vector(); + auto& state_db_key = sai_session_handler.get_state_db_key(); + + m_stateIcmpSessionTable.set(state_db_key, fvVector); + auto session_id = sai_session_handler.get_session_id(); + IcmpSessionDataCache session_cache{session_id, sai_session_handler.get_fv_map()}; + m_icmp_session_map[key] = session_cache; + m_icmp_session_lookup[session_id] = {state_db_key, SAI_ICMP_ECHO_SESSION_STATE_DOWN, true}; + + m_num_sessions++; + + SWSS_LOG_NOTICE("Created ICMP offload session key(%s)", key.c_str()); + return true; +} + +bool IcmpOrch::update_icmp_session(const string& key, const fv_vector_t& data) +{ + IcmpSaiSessionHandler sai_session_handler(*this); + + // initialize the sai session handler + auto init_status = sai_session_handler.init(sai_icmp_echo_api, key); + if (init_status != SaiOffloadHandlerStatus::SUCCESS_VALID_ENTRY) + { + SWSS_LOG_INFO("ICMP session update failed key(%s), init_status(%s)", key.c_str(), + SaiOffloadStatusStrMap.at(init_status).c_str()); + return true; + } + + auto session_id = m_icmp_session_map[key].session_id; + auto& fv_map = m_icmp_session_map[key].fv_map; + auto update_status = sai_session_handler.update(session_id, data, fv_map); + if (update_status != SaiOffloadHandlerStatus::SUCCESS_VALID_ENTRY) + { + SWSS_LOG_INFO("ICMP session update failed key(%s), update_status(%s)", key.c_str(), + SaiOffloadStatusStrMap.at(update_status).c_str()); + // do not consume the entry for retries + bool skip_entry = update_status != SaiOffloadHandlerStatus::RETRY_VALID_ENTRY; + return skip_entry; + } + + // update the STATE DB and local session maps + auto& fvVector = sai_session_handler.get_fv_vector(); + if (fvVector.size()) { + auto& state_db_key = sai_session_handler.get_state_db_key(); + auto& fv_map_upd = sai_session_handler.get_fv_map(); + m_stateIcmpSessionTable.set(state_db_key, fvVector); + IcmpSessionDataCache session_cache{session_id, fv_map_upd}; + m_icmp_session_map[key] = session_cache; + + SWSS_LOG_NOTICE("Updated ICMP offload session key(%s)", key.c_str()); + } + return true; +} + +bool IcmpOrch::remove_icmp_session(const string& key) +{ + if (m_icmp_session_map.find(key) == m_icmp_session_map.end()) + { + SWSS_LOG_ERROR("Request to remove non-existing ICMP session for %s", key.c_str()); + return true; + } + + IcmpSaiSessionHandler sai_session_handler(*this); + + // initialize the sai session handler + auto init_status = sai_session_handler.init(sai_icmp_echo_api, key); + if (init_status != SaiOffloadHandlerStatus::SUCCESS_VALID_ENTRY) + { + SWSS_LOG_INFO("ICMP session removal failed key(%s), init_status(%s)", key.c_str(), + SaiOffloadStatusStrMap.at(init_status).c_str()); + return true; + } + + sai_object_id_t icmp_session_id = m_icmp_session_map[key].session_id; + auto remove_status = sai_session_handler.remove(icmp_session_id); + if ( remove_status != SaiOffloadHandlerStatus::SUCCESS_VALID_ENTRY) + { + // do not consume the entry for retries + SWSS_LOG_INFO("ICMP session removal failed key(%s), remove_status(%s)", key.c_str(), + SaiOffloadStatusStrMap.at(remove_status).c_str()); + bool skip_entry = remove_status != SaiOffloadHandlerStatus::RETRY_VALID_ENTRY; + return skip_entry; + } + + // delete the session from state db and remove them from local maps + m_stateIcmpSessionTable.del(m_icmp_session_lookup[icmp_session_id].db_key); + + m_icmp_session_map.erase(key); + m_icmp_session_lookup.erase(icmp_session_id); + m_num_sessions--; + + SWSS_LOG_NOTICE("Removed ICMP offload session key(%s)", key.c_str()); + return true; +} + +const std::string IcmpSaiSessionHandler::m_name = "IcmpOffload"; + +const std::string IcmpSaiSessionHandler::m_tx_interval_fname = "tx_interval"; +const std::string IcmpSaiSessionHandler::m_rx_interval_fname = "rx_interval"; +const std::string IcmpSaiSessionHandler::m_src_ip_fname = "src_ip"; +const std::string IcmpSaiSessionHandler::m_dst_ip_fname = "dst_ip"; +const std::string IcmpSaiSessionHandler::m_src_mac_fname = "src_mac"; +const std::string IcmpSaiSessionHandler::m_dst_mac_fname = "dst_mac"; +const std::string IcmpSaiSessionHandler::m_tos_fname = "tos"; +const std::string IcmpSaiSessionHandler::m_ttl_fname = "ttl"; +const std::string IcmpSaiSessionHandler::m_state_fname = "state"; +const std::string IcmpSaiSessionHandler::m_session_cookie_fname = "session_cookie"; +const std::string IcmpSaiSessionHandler::m_session_guid_fname = "session_guid"; +const std::string IcmpSaiSessionHandler::m_hw_lookup_fname = "hw_lookup"; +const std::string IcmpSaiSessionHandler::m_nexthop_switchover_fname = "nexthop_switchover"; +const std::string IcmpSaiSessionHandler::m_session_type_normal = "NORMAL"; +const std::string IcmpSaiSessionHandler::m_session_type_rx = "RX"; + +const uint32_t IcmpSaiSessionHandler::m_max_tx_interval_usec = 1200000; +const uint32_t IcmpSaiSessionHandler::m_min_tx_interval_usec = 3000; +const uint32_t IcmpSaiSessionHandler::m_max_rx_interval_usec = 24000000; +const uint32_t IcmpSaiSessionHandler::m_min_rx_interval_usec = 9000; + + +const std::unordered_set IcmpSaiSessionHandler::m_update_fields = { + m_tx_interval_fname, + m_rx_interval_fname, + m_tos_fname, +}; + +void IcmpSaiSessionHandler::handle_tx_interval_field(std::string& sval, sai_attr_id_val_map_t& id_val_map, + fv_vector_t& fvVector) +{ + sai_attribute_value_t val; + val.u32 = time_msec_to_usec(to_uint(sval)); + if (val.u32) + { + if (val.u32 < m_min_tx_interval_usec) + { + SWSS_LOG_NOTICE("IcmpOrch resetting to min tx_interval (%u)", m_min_tx_interval_usec); + val.u32 = m_min_tx_interval_usec; + sval = to_string(m_min_tx_interval_usec/1000); + } + + if (val.u32 > m_max_tx_interval_usec) + { + SWSS_LOG_NOTICE("IcmpOrch resetting to max tx_interval (%u)", m_max_tx_interval_usec); + val.u32 = m_max_tx_interval_usec; + sval = to_string(m_max_tx_interval_usec/1000); + } + } + id_val_map[SAI_ICMP_ECHO_SESSION_ATTR_TX_INTERVAL] = val; + fvVector.push_back({m_tx_interval_fname, sval}); +} + +void IcmpSaiSessionHandler::handle_rx_interval_field(std::string& sval, sai_attr_id_val_map_t& id_val_map, + fv_vector_t& fvVector) +{ + sai_attribute_value_t val; + val.u32 = time_msec_to_usec(to_uint(sval)); + + if (val.u32 < m_min_rx_interval_usec) + { + SWSS_LOG_NOTICE("IcmpOrch resetting to min rx_interval (%u)", m_min_rx_interval_usec); + val.u32 = m_min_rx_interval_usec; + sval = to_string(m_min_rx_interval_usec/1000); + } + + if (val.u32 > m_max_rx_interval_usec) + { + SWSS_LOG_NOTICE("IcmpOrch resetting to max rx_interval (%u)", m_max_rx_interval_usec); + val.u32 = m_max_rx_interval_usec; + sval = to_string(m_max_rx_interval_usec/1000); + } + + id_val_map[SAI_ICMP_ECHO_SESSION_ATTR_RX_INTERVAL] = val; + fvVector.push_back({m_rx_interval_fname, sval}); +} + +void IcmpSaiSessionHandler::handle_src_ip_field(std::string& sval, sai_attr_id_val_map_t& id_val_map, + fv_vector_t& fvVector) +{ + sai_attribute_value_t val; + auto src_ip = IpAddress(sval); + swss::copy(val.ipaddr, src_ip); + id_val_map[SAI_ICMP_ECHO_SESSION_ATTR_SRC_IP_ADDRESS] = val; + fvVector.push_back({m_src_ip_fname, sval}); + sai_attribute_value_t hdr_type; + hdr_type.u8 = src_ip.isV4() ? 4 : 6; + id_val_map[SAI_ICMP_ECHO_SESSION_ATTR_IPHDR_VERSION] = hdr_type; +} + +void IcmpSaiSessionHandler::handle_dst_ip_field(std::string& sval, sai_attr_id_val_map_t& id_val_map, + fv_vector_t& fvVector) +{ + sai_attribute_value_t val; + swss::copy(val.ipaddr, IpAddress(sval)); + id_val_map[SAI_ICMP_ECHO_SESSION_ATTR_DST_IP_ADDRESS] = val; + fvVector.push_back({m_dst_ip_fname, sval}); +} + +void IcmpSaiSessionHandler::handle_src_mac_field(std::string& sval, sai_attr_id_val_map_t& id_val_map, + fv_vector_t& fvVector) +{ + sai_attribute_value_t val; + auto mac = MacAddress(sval); + memcpy(val.mac, mac.getMac(), sizeof(sai_mac_t)); + id_val_map[SAI_ICMP_ECHO_SESSION_ATTR_SRC_MAC_ADDRESS] = val; +} + +void IcmpSaiSessionHandler::handle_dst_mac_field(std::string& sval, sai_attr_id_val_map_t& id_val_map, + fv_vector_t& fvVector) +{ + sai_attribute_value_t val; + auto mac = MacAddress(sval); + memcpy(val.mac, mac.getMac(), sizeof(sai_mac_t)); + id_val_map[SAI_ICMP_ECHO_SESSION_ATTR_DST_MAC_ADDRESS] = val; +} + +void IcmpSaiSessionHandler::handle_tos_field(std::string& sval, sai_attr_id_val_map_t& id_val_map, + fv_vector_t& fvVector) +{ + sai_attribute_value_t val; + val.u8 = to_uint(sval); + id_val_map[SAI_ICMP_ECHO_SESSION_ATTR_TOS] = val; +} + +void IcmpSaiSessionHandler::handle_ttl_field(std::string& sval, sai_attr_id_val_map_t& id_val_map, + fv_vector_t& fvVector) +{ + sai_attribute_value_t val; + val.u8 = to_uint(sval); + id_val_map[SAI_ICMP_ECHO_SESSION_ATTR_TTL] = val; +} + +void IcmpSaiSessionHandler::handle_session_guid_field(std::string& sval, sai_attr_id_val_map_t& id_val_map, + fv_vector_t& fvVector) +{ + sai_attribute_value_t val; + val.u64 = to_uint(sval); + id_val_map[SAI_ICMP_ECHO_SESSION_ATTR_GUID] = val; + fvVector.push_back({m_session_guid_fname, sval}); +} + +void IcmpSaiSessionHandler::handle_session_cookie_field(std::string& sval, sai_attr_id_val_map_t& id_val_map, + fv_vector_t& fvVector) +{ + sai_attribute_value_t val; + val.u32 = to_uint(sval); + id_val_map[SAI_ICMP_ECHO_SESSION_ATTR_COOKIE] = val; + fvVector.push_back({m_session_cookie_fname, sval}); +} + +void IcmpSaiSessionHandler::handle_hw_lookup_field(std::string& sval, sai_attr_id_val_map_t& id_val_map, + fv_vector_t& fvVector) +{ + sai_attribute_value_t val; + val.booldata = (sval == "true") ? true : false; + id_val_map[SAI_ICMP_ECHO_SESSION_ATTR_HW_LOOKUP_VALID] = val; +} + +// ICMP Sai session attribute handlers +sai_attr_handler_map_t IcmpSaiSessionHandler::m_handler_map = { + + {m_tx_interval_fname, std::make_tuple(SAI_ICMP_ECHO_SESSION_ATTR_TX_INTERVAL, + IcmpSaiSessionHandler::handle_tx_interval_field)}, + {m_rx_interval_fname, std::make_tuple(SAI_ICMP_ECHO_SESSION_ATTR_RX_INTERVAL, + IcmpSaiSessionHandler::handle_rx_interval_field)}, + {m_src_ip_fname, std::make_tuple(SAI_ICMP_ECHO_SESSION_ATTR_SRC_IP_ADDRESS, + IcmpSaiSessionHandler::handle_src_ip_field)}, + {m_dst_ip_fname, std::make_tuple(SAI_ICMP_ECHO_SESSION_ATTR_DST_IP_ADDRESS, + IcmpSaiSessionHandler::handle_dst_ip_field)}, + {m_src_mac_fname, std::make_tuple(SAI_ICMP_ECHO_SESSION_ATTR_SRC_MAC_ADDRESS, + IcmpSaiSessionHandler::handle_src_mac_field)}, + {m_dst_mac_fname, std::make_tuple(SAI_ICMP_ECHO_SESSION_ATTR_DST_MAC_ADDRESS, + IcmpSaiSessionHandler::handle_dst_mac_field)}, + {m_tos_fname, std::make_tuple(SAI_ICMP_ECHO_SESSION_ATTR_TOS, + IcmpSaiSessionHandler::handle_tos_field)}, + {m_ttl_fname, std::make_tuple(SAI_ICMP_ECHO_SESSION_ATTR_TTL, + IcmpSaiSessionHandler::handle_ttl_field)}, + {m_session_guid_fname, std::make_tuple(SAI_ICMP_ECHO_SESSION_ATTR_GUID, + IcmpSaiSessionHandler::handle_session_guid_field)}, + {m_session_cookie_fname, std::make_tuple(SAI_ICMP_ECHO_SESSION_ATTR_COOKIE, + IcmpSaiSessionHandler::handle_session_cookie_field)}, + {m_hw_lookup_fname, std::make_tuple(SAI_ICMP_ECHO_SESSION_ATTR_HW_LOOKUP_VALID, + IcmpSaiSessionHandler::handle_hw_lookup_field)} +}; + +SaiOffloadHandlerStatus IcmpSaiSessionHandler::do_init(sai_icmp_echo_api_t *api) +{ + size_t vrf_pos = m_key.find(delimiter); + if (vrf_pos == string::npos) + { + SWSS_LOG_ERROR("%s, Failed to parse key %s, no vrf is given", m_name.c_str(), m_key.c_str()); + return SaiOffloadHandlerStatus::FAILED_INVALID_ENTRY; + } + + size_t ifname_pos = m_key.find(delimiter, vrf_pos + 1); + if (ifname_pos == string::npos) + { + SWSS_LOG_ERROR("%s, Failed to parse key %s, no ifname is given", m_name.c_str(), m_key.c_str()); + return SaiOffloadHandlerStatus::FAILED_INVALID_ENTRY; + } + + size_t guid_pos = m_key.find(delimiter, ifname_pos + 1); + if (guid_pos == string::npos) + { + SWSS_LOG_ERROR("%s, Failed to parse key %s, no guid is given", m_name.c_str(), m_key.c_str()); + return SaiOffloadHandlerStatus::FAILED_INVALID_ENTRY; + } + + m_vrf_name = m_key.substr(0, vrf_pos); + m_alias = m_key.substr(vrf_pos + 1, ifname_pos - vrf_pos - 1); + m_guid = m_key.substr(ifname_pos + 1, guid_pos - ifname_pos - 1); + m_session_type = m_key.substr(guid_pos + 1); + if (m_session_type == "") + { + m_session_type = m_session_type_normal; + } + + m_state_db_key = IcmpOrch::get_state_db_key(m_vrf_name, m_alias, m_guid, m_session_type); + + // initialize the sai icmp echo session function pointers + sai_create_session = api->create_icmp_echo_session; + sai_remove_session = api->remove_icmp_echo_session; + sai_set_session_attrib = api->set_icmp_echo_session_attribute; + sai_get_session_attrib = api->get_icmp_echo_session_attribute; + + return SaiOffloadHandlerStatus::SUCCESS_VALID_ENTRY; +} + +SaiOffloadHandlerStatus IcmpSaiSessionHandler::do_create() +{ + // updating the tx_interval to 0 for PEER sessions makes sure + // that hardware will not send echo requests for the PEER session + if (m_session_type == m_session_type_rx) + { + SWSS_LOG_NOTICE("%s, Tx interval being reset to 0 for RX session, %s", m_name.c_str(), m_key.c_str()); + sai_attribute_value_t val; + val.u32 = 0; + m_attr_val_map[SAI_ICMP_ECHO_SESSION_ATTR_TX_INTERVAL] = val; + m_fv_vector.push_back({m_tx_interval_fname, "0"}); + m_fv_map[m_tx_interval_fname] = "0"; + } + + // update the hw_lookup parameter in fv_vector + auto& hw_lookup_attr_val = m_attr_val_map[SAI_ICMP_ECHO_SESSION_ATTR_HW_LOOKUP_VALID]; + if (hw_lookup_attr_val.booldata) + { + m_fv_vector.push_back({m_hw_lookup_fname, "true"}); + } + else + { + m_fv_vector.push_back({m_hw_lookup_fname, "false"}); + } + + // set the guid that we got in key + auto hsearch = m_handler_map.find(m_session_guid_fname); + if (hsearch != m_handler_map.end()) + { + auto& htuple = hsearch->second; + auto& handler = std::get<1>(htuple); + handler(m_guid, m_attr_val_map, m_fv_vector); + m_fv_map[m_session_guid_fname] = m_guid; + } + else + { + // this should never happen + SWSS_LOG_ERROR("%s, GUID handler not found, %s", m_name.c_str(), m_key.c_str()); + return SaiOffloadHandlerStatus::FAILED_VALID_ENTRY; + } + + return SaiOffloadHandlerStatus::SUCCESS_VALID_ENTRY; +} + +SaiOffloadHandlerStatus IcmpSaiSessionHandler::do_remove() +{ + // no special handling required + return SaiOffloadHandlerStatus::SUCCESS_VALID_ENTRY; +} + +SaiOffloadHandlerStatus IcmpSaiSessionHandler::do_update() +{ + // do not update tx_interval for RX sessions + if ((m_fv_map.find(m_tx_interval_fname) != m_fv_map.end()) && + (m_session_type == m_session_type_rx)) + { + SWSS_LOG_NOTICE("%s, Not updating Tx interval for RX session, %s", m_name.c_str(), m_key.c_str()); + m_attr_val_map.erase(SAI_ICMP_ECHO_SESSION_ATTR_TX_INTERVAL); + + for (auto it = m_fv_vector.begin(); it != m_fv_vector.end();) + { + if (fvField(*it) == m_tx_interval_fname) + { + it = m_fv_vector.erase(it); + } else { + it++; + } + } + + m_fv_map[m_tx_interval_fname] = "0"; + } + + return SaiOffloadHandlerStatus::SUCCESS_VALID_ENTRY; +} + +void IcmpSaiSessionHandler::on_state_change(uint32_t count, sai_icmp_echo_session_state_notification_t *data) +{ + // we do not use this registered notification handler + // as it is called in a separate thread of sairedis +} + diff --git a/orchagent/icmporch.h b/orchagent/icmporch.h new file mode 100644 index 00000000000..b43bca30951 --- /dev/null +++ b/orchagent/icmporch.h @@ -0,0 +1,345 @@ +/* + * icmporch.h + * + * Created on: Feb 21, 2025 + * Author: Manas Kumar Mandal + */ +#ifndef SWSS_ICMPORCH_H +#define SWSS_ICMPORCH_H + +#include "orch.h" +#include "observer.h" +#include "saioffloadsession.h" +#include +#include + +extern sai_icmp_echo_api_t* sai_icmp_echo_api; + +extern void sai_deserialize_icmp_session_state_ntf( const std::string& s, + uint32_t &count, + sai_icmp_echo_session_state_notification_t** bfd_session_state); +extern void sai_deserialize_free_icmp_session_state_ntf(uint32_t count, + sai_icmp_echo_session_state_notification_t* icmp_session_state); + +constexpr uint32_t time_msec_to_usec(const uint32_t val) +{ + return val * 1000; +} + +/** + *@struct IcmpUpdate + * + *@brief structure used for mapping icmp session id and state + */ +struct IcmpUpdate +{ + std::string db_key; + sai_icmp_echo_session_state_t state; + bool init_state; +}; + +/** + *@struct IcmpSessionDataCache + * + *@brief structure used for mapping icmp session key and session data + */ +struct IcmpSessionDataCache +{ + sai_object_id_t session_id; + fv_map_t fv_map; +}; + +// forward declaration of icmp sai handler +struct IcmpSaiSessionHandler; + +/** + *@class IcmpOrch + * + *@brief Orchestrator class that handles ICMP sessions + */ +class IcmpOrch: public Orch, public Subject +{ +public: + /** + *@method IcmpOrch + * + *@brief class constructor + * + *@param db(in) pointer to DBConnector object + *@param tableName(in) consumer table name + *@param stateDbIcmpSessionTable(in) producer state db table + */ + IcmpOrch(swss::DBConnector *db, std::string tableName, TableConnector stateDbIcmpSessionTable); + + /** + *@method ~IcmpOrch + * + *@brief class destructor + */ + virtual ~IcmpOrch(void); + + /** + *@method doTask + * + *@brief overriden method that consumes SET/DEL + * operations on consumer table entries + * + *@param consumer(in) reference to consumer + */ + void doTask(Consumer &consumer) override; + + /** + *@method doTask + * + *@brief overriden method that consumes notifications + * from asic db + * + *@param consumer(in) reference to notification consumer + */ + void doTask(swss::NotificationConsumer &consumer) override; + + // friend handler have access to IcmpOrch + friend struct IcmpSaiSessionHandler; + + static inline std::string get_state_db_key(const std::string& vrf, const std::string& alias, + const std::string& guid, const std::string& session_type) { + return vrf + state_db_key_delimiter + alias + state_db_key_delimiter + guid + + state_db_key_delimiter + session_type; + } + +private: + /** + *@method create_icmp_session + * + *@brief creates icmp echo sessions in hardware + * + *@param key(in) reference to session key + *@param data(in) vector of session parameters from APP_DB + * table as field value tuples + * + *@return false for retries + * true for all other cases where session entry is consumed + */ + bool create_icmp_session(const string& key, const vector& data); + + /** + *@method remove_icmp_session + * + *@brief removes icmp echo sessions from hardware + * + *@param key(in) reference to session key + * + *@return false for retries + * true for all other cases + */ + bool remove_icmp_session(const string& key); + + /** + *@method update_icmp_session + * + *@brief updates icmp echo sessions in hardware + * + *@param key(in) reference to session key + *@param data(in) vector of session parameters from APP_DB + * table as field value tuples + * + *@return false for retries + * true for all other cases where session entry is consumed + */ + bool update_icmp_session(const string& key, const vector& data); + + // map of session key to session data cache + std::map m_icmp_session_map; + // map of session object id to update data for handling notification from asic db + std::map m_icmp_session_lookup; + + // Icmp session state table produced by IcmpOrch + swss::Table m_stateIcmpSessionTable; + + // ASIC_DB ICMP state notification consumer + swss::NotificationConsumer* m_icmpStateNotificationConsumer; + // indicates notification registration is done + bool m_register_state_change_notif; + + // keeps track of number of sessions + uint32_t m_num_sessions = 0; + + // max number of sessions + static const uint32_t m_max_sessions; + + // map of sai icmp session state to string + static const std::map m_session_state_lkup; + // map of icmp session state string to sai icmp session state + static const std::map m_session_state_str_lkup; +}; + +/** + *@struct IcmpSaiSessionHandler + * + *@brief Sai session handler for ICMP sessions + */ +struct IcmpSaiSessionHandler : public SaiOffloadSessionHandler +{ + /** + *@method IcmpSaiSessionHandler + * + *@brief class default constructor + */ + IcmpSaiSessionHandler() = delete; + + /** + *@method IcmpSaiSessionHandler copy constructor + * + *@brief class copy constructor + * + *@param IcmpSaiSessionHandler(in) reference to IcmpSaiSessionHandler object to be copied + */ + IcmpSaiSessionHandler(const IcmpSaiSessionHandler &) = delete; + + /** + *@method IcmpSaiSessionHandler + * + *@brief class constructor + * + *@param IcmpOrch(in) reference to IcmpOrch object + */ + IcmpSaiSessionHandler(IcmpOrch& orch) : m_orch(orch) { } + + // enum that maps ICMP ECHO SESSION SAI attribute IDs to common SaiOffload IDs + enum class SAI_ATTR_ID { + HW_LOOKUP_ID = SAI_ICMP_ECHO_SESSION_ATTR_HW_LOOKUP_VALID, + VRF_ATTR_ID = SAI_ICMP_ECHO_SESSION_ATTR_VIRTUAL_ROUTER, + PORT_ID = SAI_ICMP_ECHO_SESSION_ATTR_PORT, + IPVER_ID = SAI_ICMP_ECHO_SESSION_ATTR_IPHDR_VERSION, + TX_INTERVAL_ID = SAI_ICMP_ECHO_SESSION_ATTR_TX_INTERVAL, + RX_INTERVAL_ID = SAI_ICMP_ECHO_SESSION_ATTR_RX_INTERVAL, + SRC_IP_ID = SAI_ICMP_ECHO_SESSION_ATTR_SRC_IP_ADDRESS, + DST_IP_ID = SAI_ICMP_ECHO_SESSION_ATTR_DST_IP_ADDRESS, + SRC_MAC_ID = SAI_ICMP_ECHO_SESSION_ATTR_SRC_MAC_ADDRESS, + DST_MAC_ID = SAI_ICMP_ECHO_SESSION_ATTR_DST_MAC_ADDRESS, + TOS_ID = SAI_ICMP_ECHO_SESSION_ATTR_TOS, + TTL_ID = SAI_ICMP_ECHO_SESSION_ATTR_TTL, + COUNT_MODE_ID = SAI_ICMP_ECHO_SESSION_ATTR_STATS_COUNT_MODE, + COUNTER_LIST_ID = SAI_ICMP_ECHO_SESSION_ATTR_SELECTIVE_COUNTER_LIST, + }; + + // enum that maps ICMP Notification attribute IDs to common SaiOffload notification IDs + enum class SAI_NOTIF_ATTR_ID { + STATE_CHANGE = SAI_SWITCH_ATTR_ICMP_ECHO_SESSION_STATE_CHANGE_NOTIFY, + AVAILABLE_SESSIONS = SAI_SWITCH_ATTR_AVAILABLE_ICMP_ECHO_SESSION + }; + + // enum that maps ICMP State to common SaiOffload State + enum class SESSION_STATE { + STATE_DOWN = SAI_ICMP_ECHO_SESSION_STATE_DOWN, + STATE_UP = SAI_ICMP_ECHO_SESSION_STATE_UP, + }; + + enum class SAI_API_TYPE { + API_TYPE = SAI_API_ICMP_ECHO + }; + + /** + *@method do_init + * + *@brief initializes the icmp sai session handler + * + *@param api(in) pointer to sai_icmp_echo_api_t + * + *@return SUCCESS_VALID_ENTRY when valid key and successfully initialized + * FAILED_INVALID_ENTRY when key is invalid + * FAILED_VALID_ENTRY when initialization fails for valid key + */ + SaiOffloadHandlerStatus do_init(sai_icmp_echo_api_t *api); + + /** + *@method do_create + * + *@brief auxilary create method for icmp echo session + * + *@return SUCCESS_VALID_ENTRY session parameters valid and created with success + * FAILED_INVALID_ENTRY session parameters are invalid + * FAILED_VALID_ENTRY session creation fails for valid key + * RETRY_VALID_ENTRY retry session creation for valid key + */ + SaiOffloadHandlerStatus do_create(); + + /** + *@method do_remove + * + *@brief auxilary remove method for icmp echo session + * + *@return SUCCESS_VALID_ENTRY session id found and removed + * FAILED_INVALID_ENTRY session id not found + * FAILED_VALID_ENTRY unable to remove session for a found id + * RETRY_VALID_ENTRY retry session removal for a found id + */ + SaiOffloadHandlerStatus do_remove(); + + /** + *@method do_update + * + *@brief auxilary update method for icmp echo session + * + *@return SUCCESS_VALID_ENTRY session parameters valid and updated with success + * FAILED_INVALID_ENTRY session parameters are invalid + * FAILED_VALID_ENTRY session update fails for valid key + * RETRY_VALID_ENTRY retry session update for valid key + */ + SaiOffloadHandlerStatus do_update(); + + // stored reference to the IcmpOrch + IcmpOrch& m_orch; + // icmp echo session type, NORMAL/RX + std::string m_session_type; + // icmp echo session guid string from key + std::string m_guid; + + // function registered for icmp session notification + static void on_state_change(uint32_t count, sai_icmp_echo_session_state_notification_t *data); + + // map of sai attributes and its handlers + static sai_attr_handler_map_t m_handler_map; + + // unordered set of fields that are updatable + static const std::unordered_set m_update_fields; + // name of the icmp orch + static const std::string m_name; + + // handlers for icmp echo session app_db fields + static void handle_tx_interval_field(std::string& sval, sai_attr_id_val_map_t& id_val_map, fv_vector_t& fvVector); + static void handle_rx_interval_field(std::string& sval, sai_attr_id_val_map_t& id_val_map, fv_vector_t& fvVector); + static void handle_src_ip_field(std::string& sval, sai_attr_id_val_map_t& id_val_map, fv_vector_t& fvVector); + static void handle_dst_ip_field(std::string& sval, sai_attr_id_val_map_t& id_val_map, fv_vector_t& fvVector); + static void handle_src_mac_field(std::string& sval, sai_attr_id_val_map_t& id_val_map, fv_vector_t& fvVector); + static void handle_dst_mac_field(std::string& sval, sai_attr_id_val_map_t& id_val_map, fv_vector_t& fvVector); + static void handle_tos_field(std::string& sval, sai_attr_id_val_map_t& id_val_map, fv_vector_t& fvVector); + static void handle_ttl_field(std::string& sval, sai_attr_id_val_map_t& id_val_map, fv_vector_t& fvVector); + static void handle_session_guid_field(std::string& sval, sai_attr_id_val_map_t& id_val_map, fv_vector_t& fvVector); + static void handle_session_cookie_field(std::string& sval, sai_attr_id_val_map_t& id_val_map, fv_vector_t& fvVector); + static void handle_hw_lookup_field(std::string& sval, sai_attr_id_val_map_t& id_val_map, fv_vector_t& fvVector); + + // fieldname strings used by app_db and state_db for icmp echo sessions + static const std::string m_tx_interval_fname; + static const std::string m_rx_interval_fname; + static const std::string m_src_ip_fname; + static const std::string m_dst_ip_fname; + static const std::string m_src_mac_fname; + static const std::string m_dst_mac_fname; + static const std::string m_tos_fname; + static const std::string m_ttl_fname; + static const std::string m_state_fname; + static const std::string m_session_cookie_fname; + static const std::string m_session_guid_fname; + static const std::string m_hw_lookup_fname; + static const std::string m_nexthop_switchover_fname; + static const std::string m_session_type_normal; + static const std::string m_session_type_rx; + + static const uint32_t m_max_tx_interval_usec; + static const uint32_t m_min_tx_interval_usec; + static const uint32_t m_max_rx_interval_usec; + static const uint32_t m_min_rx_interval_usec; +}; + +#endif /* SWSS_ICMPORCH_H */ diff --git a/orchagent/muxorch.h b/orchagent/muxorch.h index d1b9e4e3484..4fc098bb48b 100644 --- a/orchagent/muxorch.h +++ b/orchagent/muxorch.h @@ -173,6 +173,7 @@ const request_description_t mux_cfg_request_description = { { "soc_ipv4", REQ_T_IP_PREFIX }, { "soc_ipv6", REQ_T_IP_PREFIX }, { "cable_type", REQ_T_STRING }, + { "prober_type", REQ_T_STRING }, }, { } }; diff --git a/orchagent/orchdaemon.cpp b/orchagent/orchdaemon.cpp index 83ff5136dd2..9d366bcd320 100644 --- a/orchagent/orchdaemon.cpp +++ b/orchagent/orchdaemon.cpp @@ -65,6 +65,7 @@ MonitorOrch *gMonitorOrch; TunnelDecapOrch *gTunneldecapOrch; StpOrch *gStpOrch; MuxOrch *gMuxOrch; +IcmpOrch *gIcmpOrch; bool gIsNatSupported = false; event_handle_t g_events_handle; @@ -213,6 +214,11 @@ bool OrchDaemon::init() gBfdOrch = new BfdOrch(m_applDb, APP_BFD_SESSION_TABLE_NAME, stateDbBfdSessionTable); gDirectory.set(gBfdOrch); + + TableConnector stateDbIcmpSessionTable(m_stateDb, STATE_ICMP_ECHO_SESSION_TABLE_NAME); + gIcmpOrch = new IcmpOrch(m_applDb, APP_ICMP_ECHO_SESSION_TABLE_NAME, stateDbIcmpSessionTable); + gDirectory.set(gIcmpOrch); + static const vector route_pattern_tables = { CFG_FLOW_COUNTER_ROUTE_PATTERN_TABLE_NAME, }; @@ -454,7 +460,7 @@ bool OrchDaemon::init() * when iterating ConsumerMap. This is ensured implicitly by the order of keys in ordered map. * For cases when Orch has to process tables in specific order, like PortsOrch during warm start, it has to override Orch::doTask() */ - m_orchList = { gSwitchOrch, gCrmOrch, gPortsOrch, gBufferOrch, gFlowCounterRouteOrch, gIntfsOrch, gNeighOrch, gNhgMapOrch, gNhgOrch, gCbfNhgOrch, gFgNhgOrch, gRouteOrch, gCoppOrch, gQosOrch, wm_orch, gPolicerOrch, gTunneldecapOrch, sflow_orch, gDebugCounterOrch, gMacsecOrch, bgp_global_state_orch, gBfdOrch, gSrv6Orch, gMuxOrch, mux_cb_orch, gMonitorOrch, gStpOrch}; + m_orchList = { gSwitchOrch, gCrmOrch, gPortsOrch, gBufferOrch, gFlowCounterRouteOrch, gIntfsOrch, gNeighOrch, gNhgMapOrch, gNhgOrch, gCbfNhgOrch, gFgNhgOrch, gRouteOrch, gCoppOrch, gQosOrch, wm_orch, gPolicerOrch, gTunneldecapOrch, sflow_orch, gDebugCounterOrch, gMacsecOrch, bgp_global_state_orch, gBfdOrch, gIcmpOrch, gSrv6Orch, gMuxOrch, mux_cb_orch, gMonitorOrch, gStpOrch}; bool initialize_dtel = false; if (platform == BFN_PLATFORM_SUBSTRING || platform == VS_PLATFORM_SUBSTRING) diff --git a/orchagent/orchdaemon.h b/orchagent/orchdaemon.h index 4ae97a5a5e7..e318fa2c552 100644 --- a/orchagent/orchdaemon.h +++ b/orchagent/orchdaemon.h @@ -44,6 +44,7 @@ #include "macsecorch.h" #include "p4orch/p4orch.h" #include "bfdorch.h" +#include "icmporch.h" #include "srv6orch.h" #include "nvgreorch.h" #include "twamporch.h" diff --git a/orchagent/saihelper.cpp b/orchagent/saihelper.cpp index 70df01d6672..0b471c3c1e3 100644 --- a/orchagent/saihelper.cpp +++ b/orchagent/saihelper.cpp @@ -72,6 +72,7 @@ sai_srv6_api_t** sai_srv6_api;; sai_l2mc_group_api_t* sai_l2mc_group_api; sai_counter_api_t* sai_counter_api; sai_bfd_api_t* sai_bfd_api; +sai_icmp_echo_api_t* sai_icmp_echo_api; sai_my_mac_api_t* sai_my_mac_api; sai_generic_programmable_api_t* sai_generic_programmable_api; sai_dash_appliance_api_t* sai_dash_appliance_api; @@ -226,6 +227,7 @@ void initSaiApi() sai_api_query(SAI_API_L2MC_GROUP, (void **)&sai_l2mc_group_api); sai_api_query(SAI_API_COUNTER, (void **)&sai_counter_api); sai_api_query(SAI_API_BFD, (void **)&sai_bfd_api); + sai_api_query(SAI_API_ICMP_ECHO, (void **)&sai_icmp_echo_api); sai_api_query(SAI_API_MY_MAC, (void **)&sai_my_mac_api); sai_api_query(SAI_API_GENERIC_PROGRAMMABLE, (void **)&sai_generic_programmable_api); sai_api_query((sai_api_t)SAI_API_DASH_APPLIANCE, (void**)&sai_dash_appliance_api); @@ -281,6 +283,7 @@ void initSaiApi() sai_log_set(SAI_API_L2MC_GROUP, SAI_LOG_LEVEL_NOTICE); sai_log_set(SAI_API_COUNTER, SAI_LOG_LEVEL_NOTICE); sai_log_set(SAI_API_BFD, SAI_LOG_LEVEL_NOTICE); + sai_log_set(SAI_API_ICMP_ECHO, SAI_LOG_LEVEL_NOTICE); sai_log_set(SAI_API_MY_MAC, SAI_LOG_LEVEL_NOTICE); sai_log_set(SAI_API_GENERIC_PROGRAMMABLE, SAI_LOG_LEVEL_NOTICE); sai_log_set(SAI_API_TWAMP, SAI_LOG_LEVEL_NOTICE); @@ -643,6 +646,25 @@ task_process_status handleSaiCreateStatus(sai_api_t api, sai_status_t status, vo break; } break; + case SAI_API_ICMP_ECHO: + switch(status) + { + case SAI_STATUS_SUCCESS: + SWSS_LOG_WARN("SAI_STATUS_SUCCESS is not expected in handleSaiCreateStatus"); + return task_success; + /* + * Offload table resource maybe a shared resource, + * avoid abort when icmp offload table is full. + */ + case SAI_STATUS_TABLE_FULL: + return task_need_retry; + default: + SWSS_LOG_ERROR("Encountered failure in create operation, exiting orchagent, SAI API: %s, status: %s", + sai_serialize_api(api).c_str(), sai_serialize_status(status).c_str()); + handleSaiFailure(true); + break; + } + break; default: switch (status) { diff --git a/orchagent/saioffloadsession.h b/orchagent/saioffloadsession.h new file mode 100644 index 00000000000..f81fd70f7b3 --- /dev/null +++ b/orchagent/saioffloadsession.h @@ -0,0 +1,574 @@ +/* + * saioffloadsession.h + * + * Created on: Feb 21, 2025 + * Author: Manas Kumar Mandal + */ +#ifndef SWSS_SAIOFFLOADSESSION_H +#define SWSS_SAIOFFLOADSESSION_H + +#include +#include +#include +#include +#include "portsorch.h" +#include "vrforch.h" + +using namespace std; +using namespace swss; + +using sai_attr_id_val_map_t = std::unordered_map; +using fv_vector_t = std::vector; +using fv_map_t = std::map; +using session_fv_map_t = std::map; +// handler type for setting sai attr map and FieldValues +using sai_attr_handler_map_t = std::unordered_map>; + + +extern sai_object_id_t gSwitchId; +extern sai_object_id_t gVirtualRouterId; +extern PortsOrch* gPortsOrch; +extern sai_switch_api_t* sai_switch_api; +extern Directory gDirectory; + +// saioffload handler types for BFD and ICMP +template +struct SaiOffloadHandlerTraits { }; + +template<> +struct SaiOffloadHandlerTraits +{ + using api_t = sai_bfd_api_t; + using create_session_fn = sai_create_bfd_session_fn; + using remove_session_fn = sai_remove_bfd_session_fn; + using set_session_attribute_fn = sai_set_bfd_session_attribute_fn; + using get_session_attribute_fn = sai_get_bfd_session_attribute_fn; + using get_session_stats_fn = sai_get_bfd_session_stats_fn; + using get_session_stats_ext_fn = sai_get_bfd_session_stats_ext_fn; + using clear_session_stats_fn = sai_clear_bfd_session_stats_fn; + using notif_t = sai_bfd_session_state_notification_t; +}; + +template<> +struct SaiOffloadHandlerTraits +{ + using api_t = sai_icmp_echo_api_t; + using create_session_fn = sai_create_icmp_echo_session_fn; + using remove_session_fn = sai_remove_icmp_echo_session_fn; + using set_session_attribute_fn = sai_set_icmp_echo_session_attribute_fn; + using get_session_attribute_fn = sai_get_icmp_echo_session_attribute_fn; + using get_session_stats_fn = sai_get_icmp_echo_session_stats_fn; + using get_session_stats_ext_fn = sai_get_icmp_echo_session_stats_ext_fn; + using clear_session_stats_fn = sai_clear_icmp_echo_session_stats_fn; + using notif_t = sai_icmp_echo_session_state_notification_t; +}; + +/** + *@enum SaiOffloadHandlerStatus + * + *@brief Enumerated status used by SaiOffloadSessionHandler + */ +enum class SaiOffloadHandlerStatus { + SUCCESS_VALID_ENTRY = 0, + RETRY_VALID_ENTRY = 1, + FAILED_VALID_ENTRY = 2, + FAILED_INVALID_ENTRY = 3 +}; + +const std::unordered_map SaiOffloadStatusStrMap = +{ + {SaiOffloadHandlerStatus::RETRY_VALID_ENTRY, "RETRY_VALID_ENTRY"}, + {SaiOffloadHandlerStatus::SUCCESS_VALID_ENTRY, "SUCCESS_VALID_ENTRY"}, + {SaiOffloadHandlerStatus::FAILED_VALID_ENTRY, "FAILED_VALID_ENTRY"}, + {SaiOffloadHandlerStatus::FAILED_INVALID_ENTRY, "FAILED_INVALID_ENTRY"} +}; + +/** + *@struct SaiOffloadSessionHandler + * + *@brief Common Sai Offload session handler used as CRTP + */ +template +struct SaiOffloadSessionHandler { + using Tapis = SaiOffloadHandlerTraits; + + /** + *@method init + * + *@brief Initialize the handler + * + *@param api(in) SAI API function pointers + *@param key(in) Session key + * + *@return SUCCESS_VALID_ENTRY when valid key and successfully initialized + * FAILED_INVALID_ENTRY when key is invalid + * FAILED_VALID_ENTRY when initialization fails for valid key + */ + SaiOffloadHandlerStatus init(typename Tapis::api_t *api, const string &key); + + /** + *@method create + * + *@brief Create SAI offload session + * + *@param fv_data(in) session parameters as Field Value tuples + * + *@return SUCCESS_VALID_ENTRY session parameters valid and created with success + * FAILED_INVALID_ENTRY session parameters are invalid + * FAILED_VALID_ENTRY session creation fails for valid key + * RETRY_VALID_ENTRY retry session creation for valid key + */ + SaiOffloadHandlerStatus create(const fv_vector_t& fv_data); + + /** + *@method handle_hwlookup + * + *@brief Set the hwlookup session attrib based on other session attribs + * + *@return SUCCESS_VALID_ENTRY session parameters valid for hwlookup and consumed without error + * FAILED_INVALID_ENTRY session parameters are invalid for hwlookup + * FAILED_VALID_ENTRY failure in handling hwlookup for valid parameters + */ + SaiOffloadHandlerStatus handle_hwlookup(); + + /** + *@method remove + * + *@brief Remove the SAI offload session + * + *@param id(in) sai session object id to delete + * + *@return SUCEES_VALID_ENTRY session id found and removed + * FAILED_INVALID_ENTRY session id not found + * FAILED_VALID_ENTRY unable to remove session for a found id + * RETRY_VALID_ENTRY retry session removal for a found id + */ + SaiOffloadHandlerStatus remove(sai_object_id_t id); + + /** + *@method update + * + *@brief Update SAI offload session + * + *@param id(in) sai session object id to update + * fv_data(in) session parameters as Field Value tuples + * fv_map(in) existing map of session parameters Field Value + * + *@return SUCCESS_VALID_ENTRY session parameters valid and updated with success + * FAILED_INVALID_ENTRY session parameters are invalid + * FAILED_VALID_ENTRY session update fails for valid key + * RETRY_VALID_ENTRY retry session update for valid key + */ + SaiOffloadHandlerStatus update(sai_object_id_t session_id, const fv_vector_t& fv_data, const fv_map_t& fv_map); + + /** + *@method register_state_change_notification + * + *@brief Registers function pointer to SAI state change notification + * + *@return True on success, False on failure + */ + bool register_state_change_notification(); + + /** + *@method get_fv_vector + * + *@brief Return the vector of field value tuples of a session + * + *@return vector of field value tuples + */ + inline fv_vector_t& get_fv_vector() { + return m_fv_vector; + } + + /** + *@method get_fv_map + * + *@brief Return the map of field value of a session + * + *@return map of field value + */ + inline fv_map_t& get_fv_map() { + return m_fv_map; + } + /** + *@method get_state_db_key + * + *@brief Returns the formatted state db key of the session + * + *@return reference to string of formatted state db key + */ + inline std::string& get_state_db_key() { + return m_state_db_key; + } + + /** + *@method get_session_id + * + *@brief Returns the session id + * + *@return SAI object id of the session + */ + inline sai_object_id_t get_session_id() { + return m_session_id; + } + +protected: + SaiOffloadSessionHandler() = default; + + typename Tapis::create_session_fn sai_create_session; + typename Tapis::remove_session_fn sai_remove_session; + typename Tapis::set_session_attribute_fn sai_set_session_attrib; + typename Tapis::get_session_attribute_fn sai_get_session_attrib; + + string m_key; + // field value vector + fv_vector_t m_data; + // field value vector for state db + fv_vector_t m_fv_vector; + // field value map for session cache + fv_map_t m_fv_map; + string m_alias; + string m_vrf_name; + string m_state_db_key; + uint32_t m_port_id; + uint32_t m_vrf_id; + // session id + sai_object_id_t m_session_id; + // map of sai attribute id and its value + sai_attr_id_val_map_t m_attr_val_map; + // attribute vector used for session creation + std::vector m_attrs; +}; + +template +SaiOffloadHandlerStatus SaiOffloadSessionHandler::init(typename Tapis::api_t *api, const string &key) +{ + m_key = key; + return static_cast(this)->do_init(api); +} + +template +SaiOffloadHandlerStatus SaiOffloadSessionHandler::create(const fv_vector_t& fv_data) +{ + constexpr auto atype = static_cast(SaiOrchHandlerClass::SAI_API_TYPE::API_TYPE); + constexpr auto& name = static_cast(this)->m_name; + auto& handler_map = static_cast(this)->m_handler_map; + + m_data = fv_data; + + // call the handler for each field-value tuple + // and fill the m_attr_val_map and m_fv_vector + for (auto& data : m_data) + { + auto field = fvField(data); + auto value = fvValue(data); + m_fv_map[field] = value; + auto hsearch = handler_map.find(field); + if (hsearch != handler_map.end()) + { + auto& htuple = hsearch->second; + auto& handler = std::get<1>(htuple); + handler(value, m_attr_val_map, m_fv_vector); + } + else + { + SWSS_LOG_ERROR("%s, Unsupported sai attribute handler for %s", name.c_str(), field.c_str()); + continue; + } + } + + // set the SAI hwlookup attribute based on other sai attributes + auto hwlookup_status = handle_hwlookup(); + if (hwlookup_status != SaiOffloadHandlerStatus::SUCCESS_VALID_ENTRY) + { + return hwlookup_status; + } + + // call the derived orch's create + auto do_create_status = static_cast(this)->do_create(); + if (do_create_status != SaiOffloadHandlerStatus::SUCCESS_VALID_ENTRY) + { + return do_create_status; + } + + // for the sai attribute vector for create + sai_attribute_t attr; + for (auto it = m_attr_val_map.begin(); it != m_attr_val_map.end(); it++) + { + attr.id = it->first; + attr.value = it->second; + m_attrs.emplace_back(attr); + } + + m_session_id = SAI_NULL_OBJECT_ID; + sai_status_t status = sai_create_session(&m_session_id, gSwitchId, (uint32_t)m_attrs.size(), m_attrs.data()); + + if (status != SAI_STATUS_SUCCESS) + { + SWSS_LOG_ERROR("%s, SAI create offload session failed %s, rv:%d", name.c_str(), m_key.c_str(), status); + task_process_status handle_status = handleSaiCreateStatus(atype, status); + if (handle_status != task_success) + { + // check for retries + if (parseHandleSaiStatusFailure(handle_status)) + { + return SaiOffloadHandlerStatus::FAILED_VALID_ENTRY; + } + else + { + return SaiOffloadHandlerStatus::RETRY_VALID_ENTRY; + } + } + } + return SaiOffloadHandlerStatus::SUCCESS_VALID_ENTRY; +} + +template +SaiOffloadHandlerStatus SaiOffloadSessionHandler::handle_hwlookup() +{ + + constexpr auto dst_mac_attr_id = static_cast(SaiOrchHandlerClass::SAI_ATTR_ID::DST_MAC_ID); + constexpr auto src_mac_attr_id = static_cast(SaiOrchHandlerClass::SAI_ATTR_ID::SRC_MAC_ID); + constexpr auto hw_lookup_attr_id = static_cast(SaiOrchHandlerClass::SAI_ATTR_ID::HW_LOOKUP_ID); + constexpr auto port_attr_id = static_cast(SaiOrchHandlerClass::SAI_ATTR_ID::PORT_ID); + constexpr auto vrf_attr_id = static_cast(SaiOrchHandlerClass::SAI_ATTR_ID::VRF_ATTR_ID); + + constexpr auto& name = static_cast(this)->m_name; + auto dmac_it = m_attr_val_map.find(dst_mac_attr_id); + + // hw lookup is not needed when outgoing port is specified + if (m_alias != "default") + { + Port port; + if (!gPortsOrch->getPort(m_alias, port)) + { + SWSS_LOG_ERROR("%s, Failed to locate port %s", name.c_str(), m_alias.c_str()); + return SaiOffloadHandlerStatus::RETRY_VALID_ENTRY; + } + + // dmac is needed as no lookup is performed in hardware + if (dmac_it == m_attr_val_map.end()) + { + SWSS_LOG_ERROR("%s, Failed to create offload session %s: destination MAC address required when hardware lookup not valid", + name.c_str(), m_key.c_str()); + return SaiOffloadHandlerStatus::FAILED_INVALID_ENTRY; + } + + // supported only for default vrf + if (m_vrf_name != "default") + { + SWSS_LOG_ERROR("%s, Failed to create offload session %s: vrf is not supported when hardware lookup not valid", + name.c_str(), m_key.c_str()); + return SaiOffloadHandlerStatus::FAILED_INVALID_ENTRY; + } + + sai_attribute_value_t val; + val.booldata = false; + m_attr_val_map[hw_lookup_attr_id] = val; + + sai_attribute_value_t val_port; + val_port.oid = port.m_port_id; + m_attr_val_map[port_attr_id] = val_port; + + sai_attribute_value_t val_smac; + auto smac_it = m_attr_val_map.find(src_mac_attr_id); + if (smac_it == m_attr_val_map.end()) + { + memcpy(val_smac.mac, port.m_mac.getMac(), sizeof(sai_mac_t)); + } + m_attr_val_map[src_mac_attr_id] = val_smac; + } + else + { + // dmac is obtained by hardware lookup + if (dmac_it != m_attr_val_map.end()) + { + SWSS_LOG_ERROR("%s, Failed to create session %s: destination MAC address not supported when hardware lookup valid", + name.c_str(), m_key.c_str()); + return SaiOffloadHandlerStatus::FAILED_INVALID_ENTRY; + } + + // vrf id needed when hardware lookup is enabled + sai_attribute_value_t vrf_val; + if (m_vrf_name == "default") + { + vrf_val.oid = gVirtualRouterId; + } + else + { + VRFOrch* vrf_orch = gDirectory.get(); + vrf_val.oid = vrf_orch->getVRFid(m_vrf_name); + } + m_attr_val_map[vrf_attr_id] = vrf_val; + + sai_attribute_value_t hw_val; + hw_val.booldata = true; + m_attr_val_map[hw_lookup_attr_id] = hw_val; + } + + return SaiOffloadHandlerStatus::SUCCESS_VALID_ENTRY; +} + +template +SaiOffloadHandlerStatus SaiOffloadSessionHandler::remove(sai_object_id_t id) +{ + constexpr auto& name = static_cast(this)->m_name; + constexpr auto atype = static_cast(SaiOrchHandlerClass::SAI_API_TYPE::API_TYPE); + + sai_status_t status = sai_remove_session(id); + if (status != SAI_STATUS_SUCCESS) + { + SWSS_LOG_ERROR("%s, Failed to remove offload session %s, rv:%d", name.c_str(), + m_key.c_str(), status); + task_process_status handle_status = handleSaiRemoveStatus(atype, status); + if (handle_status != task_success) + { + // check for retries + if (parseHandleSaiStatusFailure(handle_status)) + { + return SaiOffloadHandlerStatus::FAILED_VALID_ENTRY; + } + else + { + return SaiOffloadHandlerStatus::RETRY_VALID_ENTRY; + } + } + } + + // call the derived orch's remove + auto do_remove_status = static_cast(this)->do_remove(); + if (do_remove_status != SaiOffloadHandlerStatus::SUCCESS_VALID_ENTRY) + { + return do_remove_status; + } + + return SaiOffloadHandlerStatus::SUCCESS_VALID_ENTRY; +} + +template +SaiOffloadHandlerStatus SaiOffloadSessionHandler::update(sai_object_id_t session_id, const fv_vector_t& fv_data, const fv_map_t& fv_map) +{ + constexpr auto& name = static_cast(this)->m_name; + auto& handler_map = static_cast(this)->m_handler_map; + auto& update_fields = static_cast(this)->m_update_fields; + + m_data = fv_data; + m_session_id = session_id; + + // call the handler for field if updatable and + // fill the m_attr_val_map and m_fv_vector + for (auto& data : m_data) + { + auto field = fvField(data); + auto value = fvValue(data); + m_fv_map[field] = value; + + // check for new update field + if (fv_map.find(field) == fv_map.end()) + { + SWSS_LOG_ERROR("%s, Unsupported new field update %s:%s for %s", + name.c_str(), field.c_str(), value.c_str(), m_key.c_str()); + return SaiOffloadHandlerStatus::FAILED_INVALID_ENTRY; + } + + // check if field needs update + if (fv_map.at(field) == value) + { + continue; + } + + // check if this field update supported + if (update_fields.find(field) == update_fields.end()) + { + SWSS_LOG_ERROR("%s, Unsupported field update %s:%s for %s", + name.c_str(), field.c_str(), value.c_str(), m_key.c_str()); + return SaiOffloadHandlerStatus::FAILED_INVALID_ENTRY; + } + + SWSS_LOG_INFO("%s, field update %s:%s for %s", name.c_str(), + field.c_str(), value.c_str(), m_key.c_str()); + + auto hsearch = handler_map.find(field); + if (hsearch != handler_map.end()) + { + auto& htuple = hsearch->second; + auto& handler = std::get<1>(htuple); + handler(value, m_attr_val_map, m_fv_vector); + } + else + { + SWSS_LOG_ERROR("%s, Unsupported sai attribute handler field %s for %s", + name.c_str(), field.c_str(), m_key.c_str()); + } + } + + // call the derived orch's update + auto do_update_status = static_cast(this)->do_update(); + if (do_update_status != SaiOffloadHandlerStatus::SUCCESS_VALID_ENTRY) + { + return do_update_status; + } + + // update the session attributes + // for the sai attribute vector for create + sai_attribute_t attr; + for (auto it = m_attr_val_map.begin(); it != m_attr_val_map.end(); it++) + { + attr.id = it->first; + attr.value = it->second; + + sai_status_t status = sai_set_session_attrib(m_session_id, &attr); + + if (status != SAI_STATUS_SUCCESS) + { + SWSS_LOG_ERROR("%s, SAI offload session attrib id %u set failed %s, rv:%d", + name.c_str(), attr.id, m_key.c_str(), status); + return SaiOffloadHandlerStatus::FAILED_VALID_ENTRY; + } + } + + return SaiOffloadHandlerStatus::SUCCESS_VALID_ENTRY; +} + +template +bool SaiOffloadSessionHandler::register_state_change_notification() +{ + constexpr auto& name = static_cast(this)->m_name; + constexpr auto notify_attr_id = static_cast(SaiOrchHandlerClass::SAI_NOTIF_ATTR_ID::STATE_CHANGE); + sai_attribute_t attr; + sai_status_t status; + sai_attr_capability_t capability; + + status = sai_query_attribute_capability(gSwitchId, SAI_OBJECT_TYPE_SWITCH, + notify_attr_id, + &capability); + + if (status != SAI_STATUS_SUCCESS) + { + SWSS_LOG_ERROR("%s, Unable to query the change notification capability", name.c_str()); + return false; + } + + if (!capability.set_implemented) + { + SWSS_LOG_ERROR("%s, register change notification not supported", name.c_str()); + return false; + } + + attr.id = notify_attr_id; + attr.value.ptr = (void *)&SaiOrchHandlerClass::on_state_change; + + status = sai_switch_api->set_switch_attribute(gSwitchId, &attr); + + if (status != SAI_STATUS_SUCCESS) + { + SWSS_LOG_ERROR("%s, Failed to register notification handler", name.c_str()); + return false; + } + return true; +} + +#endif diff --git a/orchagent/switchorch.cpp b/orchagent/switchorch.cpp index 5aeed420f29..2f103f35647 100644 --- a/orchagent/switchorch.cpp +++ b/orchagent/switchorch.cpp @@ -136,6 +136,7 @@ SwitchOrch::SwitchOrch(DBConnector *db, vector& connectors, Tabl querySwitchTpidCapability(); querySwitchPortEgressSampleCapability(); querySwitchHashDefaults(); + setSwitchIcmpOffloadCapability(); auto executorT = new ExecutableTimer(m_sensorsPollerTimer, this, "ASIC_SENSORS_POLL_TIMER"); Orch::addExecutor(executorT); @@ -1395,6 +1396,11 @@ void SwitchOrch::set_switch_capability(const std::vector& value m_switchTable.set("switch", values); } +void SwitchOrch::get_switch_capability(const std::string& capability, std::string& val) +{ + m_switchTable.hget("switch", capability, val); +} + void SwitchOrch::querySwitchPortEgressSampleCapability() { vector fvVector; @@ -1509,6 +1515,27 @@ void SwitchOrch::querySwitchHashDefaults() } } +void SwitchOrch::setSwitchIcmpOffloadCapability() +{ + SWSS_LOG_ENTER(); + + vector fvVector; + // icmp echo offload does not support capability attribute, + // we depend on its notification capability + bool supported = querySwitchCapability(SAI_OBJECT_TYPE_SWITCH, SAI_SWITCH_ATTR_ICMP_ECHO_SESSION_STATE_CHANGE_NOTIFY); + if (supported == false) + { + SWSS_LOG_NOTICE("Icmp Echo Offload not supported"); + fvVector.emplace_back(SWITCH_CAPABILITY_TABLE_ICMP_OFFLOAD_CAPABLE, "false"); + } + else + { + SWSS_LOG_NOTICE("Icmp Echo Offload supported"); + fvVector.emplace_back(SWITCH_CAPABILITY_TABLE_ICMP_OFFLOAD_CAPABLE, "true"); + } + set_switch_capability(fvVector); +} + bool SwitchOrch::querySwitchCapability(sai_object_type_t sai_object, sai_attr_id_t attr_id) { SWSS_LOG_ENTER(); diff --git a/orchagent/switchorch.h b/orchagent/switchorch.h index 45ceb58e5ff..5d78660300f 100644 --- a/orchagent/switchorch.h +++ b/orchagent/switchorch.h @@ -16,6 +16,7 @@ #define SWITCH_CAPABILITY_TABLE_PFC_DLR_INIT_CAPABLE "PFC_DLR_INIT_CAPABLE" #define SWITCH_CAPABILITY_TABLE_PORT_EGRESS_SAMPLE_CAPABLE "PORT_EGRESS_SAMPLE_CAPABLE" #define SWITCH_CAPABILITY_TABLE_PATH_TRACING_CAPABLE "PATH_TRACING_CAPABLE" +#define SWITCH_CAPABILITY_TABLE_ICMP_OFFLOAD_CAPABLE "ICMP_OFFLOAD_CAPABLE" #define ASIC_SDK_HEALTH_EVENT_ELIMINATE_INTERVAL 3600 #define SWITCH_CAPABILITY_TABLE_ASIC_SDK_HEALTH_EVENT_CAPABLE "ASIC_SDK_HEALTH_EVENT" @@ -41,6 +42,7 @@ class SwitchOrch : public Orch void restartCheckReply(const std::string &op, const std::string &data, std::vector &values); bool setAgingFDB(uint32_t sec); void set_switch_capability(const std::vector& values); + void get_switch_capability(const std::string& capability, std::string& val); bool querySwitchCapability(sai_object_type_t sai_object, sai_attr_id_t attr_id); bool checkPfcDlrInitEnable() { return m_PfcDlrInitEnable; } void set_switch_pfc_dlr_init_capability(); @@ -86,6 +88,7 @@ class SwitchOrch : public Orch bool getSwitchHashOidSai(sai_object_id_t &oid, bool isEcmpHash) const; void querySwitchHashDefaults(); + void setSwitchIcmpOffloadCapability(); sai_status_t setSwitchTunnelVxlanParams(swss::FieldValueTuple &val); void setSwitchNonSaiAttributes(swss::FieldValueTuple &val); diff --git a/tests/mock_tests/Makefile.am b/tests/mock_tests/Makefile.am index e952720afbc..bf685c47349 100644 --- a/tests/mock_tests/Makefile.am +++ b/tests/mock_tests/Makefile.am @@ -133,6 +133,7 @@ tests_SOURCES = aclorch_ut.cpp \ $(top_srcdir)/orchagent/macsecorch.cpp \ $(top_srcdir)/orchagent/lagid.cpp \ $(top_srcdir)/orchagent/bfdorch.cpp \ + $(top_srcdir)/orchagent/icmporch.cpp \ $(top_srcdir)/orchagent/srv6orch.cpp \ $(top_srcdir)/orchagent/nvgreorch.cpp \ $(top_srcdir)/cfgmgr/portmgr.cpp \ diff --git a/tests/test_icmp_echo.py b/tests/test_icmp_echo.py new file mode 100644 index 00000000000..94949c34fd1 --- /dev/null +++ b/tests/test_icmp_echo.py @@ -0,0 +1,762 @@ +import pytest +import time + +from swsscommon import swsscommon + + +class TestIcmpEcho(object): + def setup_db(self, dvs): + dvs.setup_db() + self.pdb = dvs.get_app_db() + self.adb = dvs.get_asic_db() + self.sdb = dvs.get_state_db() + self.cdb = dvs.get_config_db() + # Set switch icmp offload capability + dvs.setReadOnlyAttr('SAI_OBJECT_TYPE_SWITCH', 'ICMP_OFFLOAD_CAPABLE', 'true') + + def get_exist_icmp_echo_session(self): + return set(self.adb.get_keys("ASIC_STATE:SAI_OBJECT_TYPE_ICMP_ECHO_SESSION")) + + def create_icmp_echo_session(self, key, pairs): + tbl = swsscommon.ProducerStateTable(self.pdb.db_connection, "ICMP_ECHO_SESSION_TABLE") + fvs = swsscommon.FieldValuePairs(list(pairs.items())) + tbl.set(key, fvs) + + def remove_icmp_echo_session(self, key): + tbl = swsscommon.ProducerStateTable(self.pdb.db_connection, "ICMP_ECHO_SESSION_TABLE") + tbl._del(key) + + def check_asic_icmp_echo_session_value(self, key, expected_values): + fvs = self.adb.get_entry("ASIC_STATE:SAI_OBJECT_TYPE_ICMP_ECHO_SESSION", key) + for k, v in expected_values.items(): + assert fvs[k] == v + + def check_state_icmp_echo_session_value(self, key, expected_values): + fvs = self.sdb.get_entry("ICMP_ECHO_SESSION_TABLE", key) + for k, v in expected_values.items(): + assert fvs[k] == v + + def update_icmp_echo_session_state(self, dvs, session, state): + icmp_echo_sai_state = {"Down": "SAI_ICMP_ECHO_SESSION_STATE_DOWN", + "Up": "SAI_ICMP_ECHO_SESSION_STATE_UP"} + + ntf = swsscommon.NotificationProducer(dvs.adb, "NOTIFICATIONS") + fvp = swsscommon.FieldValuePairs() + ntf_data = "[{\"icmp_echo_session_id\":\""+session+"\",\"session_state\":\""+icmp_echo_sai_state[state]+"\"}]" + ntf.send("icmp_echo_session_state_change", ntf_data, fvp) + + def set_admin_status(self, interface, status): + self.cdb.update_entry("PORT", interface, {"admin_status": status}) + + def create_vrf(self, vrf_name): + initial_entries = set(self.adb.get_keys("ASIC_STATE:SAI_OBJECT_TYPE_VIRTUAL_ROUTER")) + + self.cdb.create_entry("VRF", vrf_name, {"empty": "empty"}) + self.adb.wait_for_n_keys("ASIC_STATE:SAI_OBJECT_TYPE_VIRTUAL_ROUTER", len(initial_entries) + 1) + + current_entries = set(self.adb.get_keys("ASIC_STATE:SAI_OBJECT_TYPE_VIRTUAL_ROUTER")) + assert len(current_entries - initial_entries) == 1 + return list(current_entries - initial_entries)[0] + + def remove_vrf(self, vrf_name): + self.cdb.delete_entry("VRF", vrf_name) + + def create_l3_intf(self, interface, vrf_name): + if len(vrf_name) == 0: + self.cdb.create_entry("INTERFACE", interface, {"NULL": "NULL"}) + else: + self.cdb.create_entry("INTERFACE", interface, {"vrf_name": vrf_name}) + + def remove_l3_intf(self, interface): + self.cdb.delete_entry("INTERFACE", interface) + + def add_ip_address(self, interface, ip): + self.cdb.create_entry("INTERFACE", interface + "|" + ip, {"NULL": "NULL"}) + + def remove_ip_address(self, interface, ip): + self.cdb.delete_entry("INTERFACE", interface + "|" + ip) + + def test_addUpdateRemoveIcmpEchoSession(self, dvs): + self.setup_db(dvs) + + icmpEchoSessions = self.get_exist_icmp_echo_session() + + # Create ICMP ECHO session + fieldValues = {"session_cookie": "12345", + "src_ip": "10.0.0.1", "dst_ip":"10.0.0.2", "tx_interval": + "10", "rx_interval": "10"} + self.create_icmp_echo_session("default:default:5000:NORMAL", fieldValues) + self.adb.wait_for_n_keys("ASIC_STATE:SAI_OBJECT_TYPE_ICMP_ECHO_SESSION", len(icmpEchoSessions) + 1) + + # Checked created ICMP ECHO session in ASIC_DB + createdSessions = self.get_exist_icmp_echo_session() - icmpEchoSessions + assert len(createdSessions) == 1 + + # self session + session = createdSessions.pop() + expected_adb_values = { + "SAI_ICMP_ECHO_SESSION_ATTR_GUID": "5000", + "SAI_ICMP_ECHO_SESSION_ATTR_COOKIE": "12345", + "SAI_ICMP_ECHO_SESSION_ATTR_TX_INTERVAL": "10000", + "SAI_ICMP_ECHO_SESSION_ATTR_RX_INTERVAL": "10000", + "SAI_ICMP_ECHO_SESSION_ATTR_SRC_IP_ADDRESS": "10.0.0.1", + "SAI_ICMP_ECHO_SESSION_ATTR_DST_IP_ADDRESS": "10.0.0.2", + "SAI_ICMP_ECHO_SESSION_ATTR_IPHDR_VERSION": "4", + "SAI_ICMP_ECHO_SESSION_ATTR_HW_LOOKUP_VALID": "true", + } + self.check_asic_icmp_echo_session_value(session, expected_adb_values) + + # Check STATE_DB entry related to the ICMP ECHO session + expected_sdb_values = {"session_guid": "5000", "session_cookie": "12345", + "src_ip": "10.0.0.1", "dst_ip": "10.0.0.2", "tx_interval" :"10", + "rx_interval": "10", "hw_lookup": "true"} + self.check_state_icmp_echo_session_value("default|default|5000|NORMAL", expected_sdb_values) + + # Send ICMP ECHO session state notification to update ICMP ECHO session state + self.update_icmp_echo_session_state(dvs, session, "Up") + time.sleep(2) + + # Confirm ICMP ECHO session state in STATE_DB is updated as expected + expected_sdb_values["state"] = "Up" + self.check_state_icmp_echo_session_value("default|default|5000|NORMAL", expected_sdb_values) + + # Update tx/rx_interval in ICMP ECHO session + update_fieldValues = {"session_guid": "5000", "session_cookie": "12345", + "src_ip": "10.0.0.1", "dst_ip":"10.0.0.2", "tx_interval": + "100", "rx_interval": "50"} + self.create_icmp_echo_session("default:default:5000:NORMAL", update_fieldValues) + # wait after update + time.sleep(2) + + # Confirm tx/rx_interval does get updated + expected_sdb_values["tx_interval"] = "100" + expected_sdb_values["rx_interval"] = "50" + self.check_state_icmp_echo_session_value("default|default|5000|NORMAL", expected_sdb_values) + + # Verify the ASIC_DB gets the updated value + expected_adb_values = { + "SAI_ICMP_ECHO_SESSION_ATTR_GUID": "5000", + "SAI_ICMP_ECHO_SESSION_ATTR_COOKIE": "12345", + "SAI_ICMP_ECHO_SESSION_ATTR_TX_INTERVAL": "100000", + "SAI_ICMP_ECHO_SESSION_ATTR_RX_INTERVAL": "50000", + "SAI_ICMP_ECHO_SESSION_ATTR_SRC_IP_ADDRESS": "10.0.0.1", + "SAI_ICMP_ECHO_SESSION_ATTR_DST_IP_ADDRESS": "10.0.0.2", + "SAI_ICMP_ECHO_SESSION_ATTR_IPHDR_VERSION": "4", + "SAI_ICMP_ECHO_SESSION_ATTR_HW_LOOKUP_VALID": "true", + } + self.check_asic_icmp_echo_session_value(session, expected_adb_values) + + # remove the session + self.remove_icmp_echo_session("default:default:5000:NORMAL") + self.adb.wait_for_deleted_entry("ASIC_STATE:SAI_OBJECT_TYPE_ICMP_ECHO_SESSION", session) + + # RX session + peer_fieldValues = {"session_cookie": "12345", + "src_ip": "10.0.0.1", "dst_ip":"10.0.0.2", "tx_interval": + "10", "rx_interval": "10"} + self.create_icmp_echo_session("default:default:5000:RX", peer_fieldValues) + self.adb.wait_for_n_keys("ASIC_STATE:SAI_OBJECT_TYPE_ICMP_ECHO_SESSION", len(icmpEchoSessions)+1) + + # Checked created ICMP ECHO session in ASIC_DB + createdSessions = self.get_exist_icmp_echo_session() - icmpEchoSessions + assert len(createdSessions) == 1 + + session = createdSessions.pop() + expected_adb_values = { + "SAI_ICMP_ECHO_SESSION_ATTR_GUID": "5000", + "SAI_ICMP_ECHO_SESSION_ATTR_COOKIE": "12345", + "SAI_ICMP_ECHO_SESSION_ATTR_TX_INTERVAL": "0", + "SAI_ICMP_ECHO_SESSION_ATTR_RX_INTERVAL": "10000", + "SAI_ICMP_ECHO_SESSION_ATTR_SRC_IP_ADDRESS": "10.0.0.1", + "SAI_ICMP_ECHO_SESSION_ATTR_DST_IP_ADDRESS": "10.0.0.2", + "SAI_ICMP_ECHO_SESSION_ATTR_IPHDR_VERSION": "4", + } + self.check_asic_icmp_echo_session_value(session, expected_adb_values) + + # Check STATE_DB entry related to the ICMP ECHO session + expected_sdb_values = {"session_guid": "5000", "session_cookie": "12345", + "src_ip": "10.0.0.1", "dst_ip": "10.0.0.2", "tx_interval" :"0", + "rx_interval": "10", "hw_lookup": "true"} + self.check_state_icmp_echo_session_value("default|default|5000|RX", expected_sdb_values) + + # Confirm tx_interval does not get updated + peer_update_fieldValues = {"session_cookie": "12345", + "src_ip": "10.0.0.1", "dst_ip":"10.0.0.2", "tx_interval": + "100", "rx_interval": "100"} + self.create_icmp_echo_session("default:default:5000:RX", peer_update_fieldValues) + self.adb.wait_for_n_keys("ASIC_STATE:SAI_OBJECT_TYPE_ICMP_ECHO_SESSION", len(icmpEchoSessions)+1) + + # Checked ICMP ECHO session in ASIC_DB post update + createdSessions = self.get_exist_icmp_echo_session() - icmpEchoSessions + assert len(createdSessions) == 1 + + session = createdSessions.pop() + expected_adb_values = { + "SAI_ICMP_ECHO_SESSION_ATTR_GUID": "5000", + "SAI_ICMP_ECHO_SESSION_ATTR_COOKIE": "12345", + "SAI_ICMP_ECHO_SESSION_ATTR_TX_INTERVAL": "0", + "SAI_ICMP_ECHO_SESSION_ATTR_RX_INTERVAL": "100000", + "SAI_ICMP_ECHO_SESSION_ATTR_SRC_IP_ADDRESS": "10.0.0.1", + "SAI_ICMP_ECHO_SESSION_ATTR_DST_IP_ADDRESS": "10.0.0.2", + "SAI_ICMP_ECHO_SESSION_ATTR_IPHDR_VERSION": "4", + } + self.check_asic_icmp_echo_session_value(session, expected_adb_values) + + # Check STATE_DB entry related to the ICMP ECHO session post update + expected_sdb_values = {"session_guid": "5000", "session_cookie": "12345", + "src_ip": "10.0.0.1", "dst_ip": "10.0.0.2", "tx_interval" :"0", + "rx_interval": "100", "hw_lookup": "true"} + self.check_state_icmp_echo_session_value("default|default|5000|RX", expected_sdb_values) + + # Send ICMP ECHO session state notification to update ICMP ECHO session state + self.update_icmp_echo_session_state(dvs, session, "Up") + time.sleep(2) + + # Confirm ICMP ECHO session state in STATE_DB is updated as expected + expected_sdb_values["state"] = "Up" + self.check_state_icmp_echo_session_value("default|default|5000|RX", expected_sdb_values) + + # Remove the ICMP sessions + self.remove_icmp_echo_session("default:default:5000:RX") + self.adb.wait_for_deleted_entry("ASIC_STATE:SAI_OBJECT_TYPE_ICMP_ECHO_SESSION", session) + + keys = self.sdb.get_keys("ICMP_ECHO_SESSION_TABLE") + assert len(keys) == 0 + + def test_multipleIcmpEchoSessions(self, dvs): + self.setup_db(dvs) + + # create interfaces and add IP address + self.create_l3_intf("Ethernet0", "default") + self.create_l3_intf("Ethernet4", "default") + self.add_ip_address("Ethernet0", "10.0.0.0/31") + self.add_ip_address("Ethernet4", "10.0.1.0/31") + self.set_admin_status("Ethernet0", "up") + self.set_admin_status("Ethernet4", "up") + + icmpEchoSessions = self.get_exist_icmp_echo_session() + + # Create ICMP session 1 + fieldValues = {"session_cookie": "12345", + "src_ip": "10.0.0.1", "dst_ip":"10.0.0.2", "tx_interval": + "10", "rx_interval": "10", "dst_mac": "01:23:45:aa:bb:cc"} + + key1_self = "default:Ethernet0:5000:NORMAL" + self.create_icmp_echo_session(key1_self, fieldValues) + self.adb.wait_for_n_keys("ASIC_STATE:SAI_OBJECT_TYPE_ICMP_ECHO_SESSION", len(icmpEchoSessions) + 1) + + # Checked created ICMP ECHO session in ASIC_DB + createdSessions = self.get_exist_icmp_echo_session() - icmpEchoSessions + icmpEchoSessions = self.get_exist_icmp_echo_session() + assert len(createdSessions) == 1 + + # self session + session1 = createdSessions.pop() + expected_adb_values = { + "SAI_ICMP_ECHO_SESSION_ATTR_GUID": "5000", + "SAI_ICMP_ECHO_SESSION_ATTR_COOKIE": "12345", + "SAI_ICMP_ECHO_SESSION_ATTR_TX_INTERVAL": "10000", + "SAI_ICMP_ECHO_SESSION_ATTR_RX_INTERVAL": "10000", + "SAI_ICMP_ECHO_SESSION_ATTR_SRC_IP_ADDRESS": "10.0.0.1", + "SAI_ICMP_ECHO_SESSION_ATTR_DST_IP_ADDRESS": "10.0.0.2", + "SAI_ICMP_ECHO_SESSION_ATTR_IPHDR_VERSION": "4", + "SAI_ICMP_ECHO_SESSION_ATTR_HW_LOOKUP_VALID": "false", + "SAI_ICMP_ECHO_SESSION_ATTR_DST_MAC_ADDRESS": "01:23:45:AA:BB:CC", + } + self.check_asic_icmp_echo_session_value(session1, expected_adb_values) + + # Check STATE_DB entry related to the ICMP ECHO session + expected_sdb_values = {"session_guid": "5000", "session_cookie": "12345", + "src_ip": "10.0.0.1", "dst_ip": "10.0.0.2", "tx_interval" :"10", + "rx_interval": "10", "hw_lookup": "false"} + self.check_state_icmp_echo_session_value("default|Ethernet0|5000|NORMAL", expected_sdb_values) + + # Send ICMP ECHO session state notification to update ICMP ECHO session state + self.update_icmp_echo_session_state(dvs, session1, "Up") + time.sleep(2) + + # Confirm ICMP ECHO session state in STATE_DB is updated as expected + expected_sdb_values["state"] = "Up" + self.check_state_icmp_echo_session_value("default|Ethernet0|5000|NORMAL", expected_sdb_values) + + # RX session + peer_fieldValues = {"session_cookie": "12345", + "src_ip": "10.0.0.1", "dst_ip":"10.0.0.2", "tx_interval": + "10", "rx_interval": "10", "dst_mac": "01:23:45:aa:bb:cc"} + + key1_peer = "default:Ethernet0:6000:RX" + self.create_icmp_echo_session(key1_peer, peer_fieldValues) + self.adb.wait_for_n_keys("ASIC_STATE:SAI_OBJECT_TYPE_ICMP_ECHO_SESSION", len(icmpEchoSessions) + 1) + + # Checked created ICMP ECHO session in ASIC_DB + createdSessions = self.get_exist_icmp_echo_session() - icmpEchoSessions + assert len(createdSessions) == 1 + + session2 = createdSessions.pop() + expected_adb_values = { + "SAI_ICMP_ECHO_SESSION_ATTR_GUID": "6000", + "SAI_ICMP_ECHO_SESSION_ATTR_COOKIE": "12345", + "SAI_ICMP_ECHO_SESSION_ATTR_TX_INTERVAL": "0", + "SAI_ICMP_ECHO_SESSION_ATTR_RX_INTERVAL": "10000", + "SAI_ICMP_ECHO_SESSION_ATTR_SRC_IP_ADDRESS": "10.0.0.1", + "SAI_ICMP_ECHO_SESSION_ATTR_DST_IP_ADDRESS": "10.0.0.2", + "SAI_ICMP_ECHO_SESSION_ATTR_IPHDR_VERSION": "4", + "SAI_ICMP_ECHO_SESSION_ATTR_HW_LOOKUP_VALID": "false", + "SAI_ICMP_ECHO_SESSION_ATTR_DST_MAC_ADDRESS": "01:23:45:AA:BB:CC", + } + self.check_asic_icmp_echo_session_value(session2, expected_adb_values) + + # Check STATE_DB entry related to the ICMP ECHO session + expected_sdb_values = {"session_guid": "6000", "session_cookie": "12345", + "src_ip": "10.0.0.1", "dst_ip": "10.0.0.2", "tx_interval" :"0", + "rx_interval": "10", "hw_lookup": "false"} + self.check_state_icmp_echo_session_value("default|Ethernet0|6000|RX", expected_sdb_values) + + # Send ICMP ECHO session state notification to update ICMP ECHO session state + self.update_icmp_echo_session_state(dvs, session2, "Up") + time.sleep(2) + + # Confirm ICMP ECHO session state in STATE_DB is updated as expected + expected_sdb_values["state"] = "Up" + self.check_state_icmp_echo_session_value("default|Ethernet0|6000|RX", expected_sdb_values) + + # Remove the ICMP sessions + self.remove_icmp_echo_session(key1_self) + self.adb.wait_for_deleted_entry("ASIC_STATE:SAI_OBJECT_TYPE_ICMP_ECHO_SESSION", session1) + self.remove_icmp_echo_session(key1_peer) + self.adb.wait_for_deleted_entry("ASIC_STATE:SAI_OBJECT_TYPE_ICMP_ECHO_SESSION", session2) + + keys = self.sdb.get_keys("ICMP_ECHO_SESSION_TABLE") + assert len(keys) == 0 + + def test_icmp_echo_state_db_clear(self, dvs): + self.setup_db(dvs) + + icmpEchoSessions = self.get_exist_icmp_echo_session() + + # Create Icmp echo session + fieldValues = {"session_cookie": "12345", + "src_ip": "10.0.0.1", "dst_ip":"10.0.0.2", "tx_interval": + "10", "rx_interval": "10"} + + key1_self = "default:default:5000:NORMAL" + self.create_icmp_echo_session(key1_self, fieldValues) + self.adb.wait_for_n_keys("ASIC_STATE:SAI_OBJECT_TYPE_ICMP_ECHO_SESSION", len(icmpEchoSessions) + 1) + + # Checked created icmp session in ASIC_DB + createdSessions = self.get_exist_icmp_echo_session() - icmpEchoSessions + assert len(createdSessions) == 1 + + dvs.stop_swss() + dvs.start_swss() + + time.sleep(5) + keys = self.sdb.get_keys("ICMP_ECHO_SESSION_TABLE") + assert len(keys) == 0 + + def test_FailIcmpEchoSessions(self, dvs): + self.setup_db(dvs) + + # create interfaces and add IP address + self.create_l3_intf("Ethernet0", "default") + self.create_l3_intf("Ethernet4", "default") + self.add_ip_address("Ethernet0", "10.0.0.0/31") + self.add_ip_address("Ethernet4", "10.0.1.0/31") + self.set_admin_status("Ethernet0", "up") + self.set_admin_status("Ethernet4", "up") + + icmpEchoSessions = self.get_exist_icmp_echo_session() + + # Create ICMP session 1 + fieldValues = {"session_cookie": "12345", + "src_ip": "10.0.0.1", "dst_ip":"10.0.0.2", "tx_interval": "10", + "rx_interval": "10", "src_mac": "01:01:02:02:03:04", "ttl" : "3", + "hw_lookup": "true", "tos": "1"} + + # bad key + key1_self = "default:Ethernet0:5000" + self.create_icmp_echo_session(key1_self, fieldValues) + time.sleep(2) + + # Create should for bad key fail + createdSessions = self.get_exist_icmp_echo_session() - icmpEchoSessions + icmpEchoSessions = self.get_exist_icmp_echo_session() + assert len(createdSessions) == 0 + + # missing dst_mac, creation should fail with proper key + key1_self = "default:Ethernet0:5000:" + self.create_icmp_echo_session(key1_self, fieldValues) + time.sleep(2) + + # Create should fail for missing dst_mac when using non-default alias + createdSessions = self.get_exist_icmp_echo_session() - icmpEchoSessions + icmpEchoSessions = self.get_exist_icmp_echo_session() + assert len(createdSessions) == 0 + + # add the dst_mac + fieldValues["hw_lookup"] = "false" + fieldValues["dst_mac"] = "01:23:45:aa:bb:cc" + + # default alias with dst_mac should fail + key1_self = "default:default:5000:" + self.create_icmp_echo_session(key1_self, fieldValues) + time.sleep(2) + + # Create should fail + createdSessions = self.get_exist_icmp_echo_session() - icmpEchoSessions + icmpEchoSessions = self.get_exist_icmp_echo_session() + assert len(createdSessions) == 0 + + # unkown port alias should fail + key1_self = "default:Ethernet128:5000:" + self.create_icmp_echo_session(key1_self, fieldValues) + time.sleep(2) + + # Create should fail + createdSessions = self.get_exist_icmp_echo_session() - icmpEchoSessions + icmpEchoSessions = self.get_exist_icmp_echo_session() + assert len(createdSessions) == 0 + + # Remove the ICMP sessions + self.remove_icmp_echo_session(key1_self) + time.sleep(1) + + # creation should pass with unsupported attrib + fieldValues["unknown_attrib"] = "XXXX" + # creation should pass after hw_lookup is set ot false + key1_self = "default:Ethernet0:5000:" + self.create_icmp_echo_session(key1_self, fieldValues) + self.adb.wait_for_n_keys("ASIC_STATE:SAI_OBJECT_TYPE_ICMP_ECHO_SESSION", len(icmpEchoSessions) + 1) + + # Checked created ICMP ECHO session in ASIC_DB + createdSessions = self.get_exist_icmp_echo_session() - icmpEchoSessions + icmpEchoSessions = self.get_exist_icmp_echo_session() + assert len(createdSessions) == 1 + + # self session + session1 = createdSessions.pop() + expected_adb_values = { + "SAI_ICMP_ECHO_SESSION_ATTR_GUID": "5000", + "SAI_ICMP_ECHO_SESSION_ATTR_COOKIE": "12345", + "SAI_ICMP_ECHO_SESSION_ATTR_TX_INTERVAL": "10000", + "SAI_ICMP_ECHO_SESSION_ATTR_RX_INTERVAL": "10000", + "SAI_ICMP_ECHO_SESSION_ATTR_SRC_IP_ADDRESS": "10.0.0.1", + "SAI_ICMP_ECHO_SESSION_ATTR_DST_IP_ADDRESS": "10.0.0.2", + "SAI_ICMP_ECHO_SESSION_ATTR_IPHDR_VERSION": "4", + "SAI_ICMP_ECHO_SESSION_ATTR_HW_LOOKUP_VALID": "false", + "SAI_ICMP_ECHO_SESSION_ATTR_DST_MAC_ADDRESS": "01:23:45:AA:BB:CC", + "SAI_ICMP_ECHO_SESSION_ATTR_TTL": "3", + } + self.check_asic_icmp_echo_session_value(session1, expected_adb_values) + + # Check STATE_DB entry related to the ICMP ECHO session + expected_sdb_values = {"session_guid": "5000", "session_cookie": "12345", + "src_ip": "10.0.0.1", "dst_ip": "10.0.0.2", "tx_interval": "10", + "rx_interval": "10", "hw_lookup": "false"} + self.check_state_icmp_echo_session_value("default|Ethernet0|5000|NORMAL", expected_sdb_values) + + # notification with wrong key + self.update_icmp_echo_session_state(dvs, session1, "Up") + time.sleep(2) + + # Confirm ICMP ECHO session state in STATE_DB is updated as expected + expected_sdb_values["state"] = "Up" + self.check_state_icmp_echo_session_value("default|Ethernet0|5000|NORMAL", expected_sdb_values) + + # Remove the ICMP sessions + self.remove_icmp_echo_session(key1_self) + self.adb.wait_for_deleted_entry("ASIC_STATE:SAI_OBJECT_TYPE_ICMP_ECHO_SESSION", session1) + + keys = self.sdb.get_keys("ICMP_ECHO_SESSION_TABLE") + assert len(keys) == 0 + + # RX session + peer_fieldValues = {"session_cookie": "12345", "src_ip": "10.0.0.1", + "dst_ip":"10.0.0.2", "tx_interval": "10", + "rx_interval": "10", "dst_mac": "01:23:45:aa:bb:cc", + "ttl" : "10"} + + key1_peer = "default:Ethernet0:6000:RX" + self.create_icmp_echo_session(key1_peer, peer_fieldValues) + self.adb.wait_for_n_keys("ASIC_STATE:SAI_OBJECT_TYPE_ICMP_ECHO_SESSION", len(icmpEchoSessions)) + + # Checked created ICMP ECHO session in ASIC_DB + createdSessions = self.get_exist_icmp_echo_session() - icmpEchoSessions + assert len(createdSessions) == 1 + + session2 = createdSessions.pop() + expected_adb_values = { + "SAI_ICMP_ECHO_SESSION_ATTR_GUID": "6000", + "SAI_ICMP_ECHO_SESSION_ATTR_COOKIE": "12345", + "SAI_ICMP_ECHO_SESSION_ATTR_TX_INTERVAL": "0", + "SAI_ICMP_ECHO_SESSION_ATTR_RX_INTERVAL": "10000", + "SAI_ICMP_ECHO_SESSION_ATTR_SRC_IP_ADDRESS": "10.0.0.1", + "SAI_ICMP_ECHO_SESSION_ATTR_DST_IP_ADDRESS": "10.0.0.2", + "SAI_ICMP_ECHO_SESSION_ATTR_IPHDR_VERSION": "4", + "SAI_ICMP_ECHO_SESSION_ATTR_HW_LOOKUP_VALID": "false", + "SAI_ICMP_ECHO_SESSION_ATTR_DST_MAC_ADDRESS": "01:23:45:AA:BB:CC", + "SAI_ICMP_ECHO_SESSION_ATTR_TTL": "10", + } + self.check_asic_icmp_echo_session_value(session2, expected_adb_values) + + # Check STATE_DB entry related to the ICMP ECHO session + expected_sdb_values = {"session_guid": "6000", "session_cookie": "12345", + "src_ip": "10.0.0.1", "dst_ip": "10.0.0.2", "tx_interval" :"0", + "rx_interval": "10", "hw_lookup": "false"} + self.check_state_icmp_echo_session_value("default|Ethernet0|6000|RX", expected_sdb_values) + + # Send ICMP ECHO session state notification to update ICMP ECHO session state + self.update_icmp_echo_session_state(dvs, session2, "Up") + time.sleep(2) + + # Confirm ICMP ECHO session state in STATE_DB is updated as expected + expected_sdb_values["state"] = "Up" + self.check_state_icmp_echo_session_value("default|Ethernet0|6000|RX", expected_sdb_values) + + # Failure Remove the ICMP sessions + self.remove_icmp_echo_session(key1_self) + time.sleep(1) + + keys = self.sdb.get_keys("ICMP_ECHO_SESSION_TABLE") + assert len(keys) == 1 + + # Update with valid new field should fail + peer_fieldValues["tos"] = "2" + + self.create_icmp_echo_session(key1_peer, peer_fieldValues) + time.sleep(1) + + # Checked no new created session + createdSessions = self.get_exist_icmp_echo_session() - icmpEchoSessions + assert len(createdSessions) == 1 + + del peer_fieldValues["tos"] + + # Update tx_interval should fail for RX session + peer_fieldValues["tx_interval"] = "20" + + self.create_icmp_echo_session(key1_peer, peer_fieldValues) + time.sleep(1) + + # Checked no new created session + createdSessions = self.get_exist_icmp_echo_session() - icmpEchoSessions + assert len(createdSessions) == 1 + + # check expected values did not change + self.check_state_icmp_echo_session_value("default|Ethernet0|6000|RX", expected_sdb_values) + + del peer_fieldValues["tx_interval"] + + # Update unsupported field should fail + peer_fieldValues["ttl"] = "1" + + self.create_icmp_echo_session(key1_peer, peer_fieldValues) + time.sleep(1) + + # Checked no new created session + createdSessions = self.get_exist_icmp_echo_session() - icmpEchoSessions + assert len(createdSessions) == 1 + + # check expected values did not change + self.check_state_icmp_echo_session_value("default|Ethernet0|6000|RX", expected_sdb_values) + + peer_fieldValues["ttl"] = "10" + + # Update unknown field should fail + peer_fieldValues["unknown_attrib"] = "DDDD" + + self.create_icmp_echo_session(key1_peer, peer_fieldValues) + time.sleep(1) + + # Checked no new created session + createdSessions = self.get_exist_icmp_echo_session() - icmpEchoSessions + assert len(createdSessions) == 1 + + # check expected values did not change + self.check_state_icmp_echo_session_value("default|Ethernet0|6000|RX", expected_sdb_values) + + del peer_fieldValues["unknown_attrib"] + + session2 = createdSessions.pop() + + #remove the second session + self.remove_icmp_echo_session(key1_peer) + self.adb.wait_for_deleted_entry("ASIC_STATE:SAI_OBJECT_TYPE_ICMP_ECHO_SESSION", session2) + + keys = self.sdb.get_keys("ICMP_ECHO_SESSION_TABLE") + assert len(keys) == 0 + + def test_intervalIcmpEchoSessions(self, dvs): + self.setup_db(dvs) + + # create interfaces and add IP address + self.create_l3_intf("Ethernet0", "default") + self.create_l3_intf("Ethernet4", "default") + self.add_ip_address("Ethernet0", "10.0.0.0/31") + self.add_ip_address("Ethernet4", "10.0.1.0/31") + self.set_admin_status("Ethernet0", "up") + self.set_admin_status("Ethernet4", "up") + + icmpEchoSessions = self.get_exist_icmp_echo_session() + + # Create ICMP session 1, use lower than min rx/tx interval + fieldValues = {"session_cookie": "12345", + "src_ip": "10.0.0.1", "dst_ip":"10.0.0.2", "tx_interval": "1", + "rx_interval": "8", "dst_mac": "01:23:45:aa:bb:cc"} + + key1_self = "default:Ethernet0:5000:NORMAL" + self.create_icmp_echo_session(key1_self, fieldValues) + self.adb.wait_for_n_keys("ASIC_STATE:SAI_OBJECT_TYPE_ICMP_ECHO_SESSION", len(icmpEchoSessions) + 1) + + # Checked created ICMP ECHO session in ASIC_DB + createdSessions = self.get_exist_icmp_echo_session() - icmpEchoSessions + icmpEchoSessions = self.get_exist_icmp_echo_session() + assert len(createdSessions) == 1 + + # self session + session1 = createdSessions.pop() + expected_adb_values = { + "SAI_ICMP_ECHO_SESSION_ATTR_GUID": "5000", + "SAI_ICMP_ECHO_SESSION_ATTR_COOKIE": "12345", + "SAI_ICMP_ECHO_SESSION_ATTR_TX_INTERVAL": "3000", + "SAI_ICMP_ECHO_SESSION_ATTR_RX_INTERVAL": "9000", + "SAI_ICMP_ECHO_SESSION_ATTR_SRC_IP_ADDRESS": "10.0.0.1", + "SAI_ICMP_ECHO_SESSION_ATTR_DST_IP_ADDRESS": "10.0.0.2", + "SAI_ICMP_ECHO_SESSION_ATTR_IPHDR_VERSION": "4", + "SAI_ICMP_ECHO_SESSION_ATTR_HW_LOOKUP_VALID": "false", + "SAI_ICMP_ECHO_SESSION_ATTR_DST_MAC_ADDRESS": "01:23:45:AA:BB:CC", + } + self.check_asic_icmp_echo_session_value(session1, expected_adb_values) + + # Check STATE_DB entry related to the ICMP ECHO session + expected_sdb_values = {"session_guid": "5000", "session_cookie": "12345", + "src_ip": "10.0.0.1", "dst_ip": "10.0.0.2", "tx_interval" :"3", + "rx_interval": "9", "hw_lookup": "false"} + self.check_state_icmp_echo_session_value("default|Ethernet0|5000|NORMAL", expected_sdb_values) + + # Send ICMP ECHO session state notification to update ICMP ECHO session state + self.update_icmp_echo_session_state(dvs, session1, "Up") + time.sleep(2) + + # Confirm ICMP ECHO session state in STATE_DB is updated as expected + expected_sdb_values["state"] = "Up" + self.check_state_icmp_echo_session_value("default|Ethernet0|5000|NORMAL", expected_sdb_values) + + # RX session, with rx interval more than max + peer_fieldValues = {"session_cookie": "12345", "src_ip": "10.0.0.1", + "dst_ip":"10.0.0.2", "tx_interval": "10", + "rx_interval": "24001", "dst_mac": "01:23:45:aa:bb:cc"} + + key1_peer = "default:Ethernet0:6000:RX" + self.create_icmp_echo_session(key1_peer, peer_fieldValues) + self.adb.wait_for_n_keys("ASIC_STATE:SAI_OBJECT_TYPE_ICMP_ECHO_SESSION", len(icmpEchoSessions) + 1) + + # Checked created ICMP ECHO session in ASIC_DB + createdSessions = self.get_exist_icmp_echo_session() - icmpEchoSessions + assert len(createdSessions) == 1 + + session2 = createdSessions.pop() + expected_adb_values = { + "SAI_ICMP_ECHO_SESSION_ATTR_GUID": "6000", + "SAI_ICMP_ECHO_SESSION_ATTR_COOKIE": "12345", + "SAI_ICMP_ECHO_SESSION_ATTR_TX_INTERVAL": "0", + "SAI_ICMP_ECHO_SESSION_ATTR_RX_INTERVAL": "24000000", + "SAI_ICMP_ECHO_SESSION_ATTR_SRC_IP_ADDRESS": "10.0.0.1", + "SAI_ICMP_ECHO_SESSION_ATTR_DST_IP_ADDRESS": "10.0.0.2", + "SAI_ICMP_ECHO_SESSION_ATTR_IPHDR_VERSION": "4", + "SAI_ICMP_ECHO_SESSION_ATTR_HW_LOOKUP_VALID": "false", + "SAI_ICMP_ECHO_SESSION_ATTR_DST_MAC_ADDRESS": "01:23:45:AA:BB:CC", + } + self.check_asic_icmp_echo_session_value(session2, expected_adb_values) + + # Check STATE_DB entry related to the ICMP ECHO session, max rx_interval + expected_sdb_values = {"session_guid": "6000", "session_cookie": "12345", + "src_ip": "10.0.0.1", "dst_ip": "10.0.0.2", "tx_interval" :"0", + "rx_interval": "24000", "hw_lookup": "false"} + self.check_state_icmp_echo_session_value("default|Ethernet0|6000|RX", expected_sdb_values) + + # Send ICMP ECHO session state notification to update ICMP ECHO session state + self.update_icmp_echo_session_state(dvs, session2, "Up") + time.sleep(2) + + # Confirm ICMP ECHO session state in STATE_DB is updated as expected + expected_sdb_values["state"] = "Up" + self.check_state_icmp_echo_session_value("default|Ethernet0|6000|RX", expected_sdb_values) + + # update the RX session rx interval to lower than min + peer_fieldValues["rx_interval"] = "8" + self.create_icmp_echo_session(key1_peer, peer_fieldValues) + time.sleep(1) + + # Checked no extra created ICMP ECHO session in ASIC_DB + createdSessions = self.get_exist_icmp_echo_session() - icmpEchoSessions + assert len(createdSessions) == 1 + + expected_adb_values = { + "SAI_ICMP_ECHO_SESSION_ATTR_GUID": "6000", + "SAI_ICMP_ECHO_SESSION_ATTR_COOKIE": "12345", + "SAI_ICMP_ECHO_SESSION_ATTR_TX_INTERVAL": "0", + "SAI_ICMP_ECHO_SESSION_ATTR_RX_INTERVAL": "9000", + "SAI_ICMP_ECHO_SESSION_ATTR_SRC_IP_ADDRESS": "10.0.0.1", + "SAI_ICMP_ECHO_SESSION_ATTR_DST_IP_ADDRESS": "10.0.0.2", + "SAI_ICMP_ECHO_SESSION_ATTR_IPHDR_VERSION": "4", + "SAI_ICMP_ECHO_SESSION_ATTR_HW_LOOKUP_VALID": "false", + "SAI_ICMP_ECHO_SESSION_ATTR_DST_MAC_ADDRESS": "01:23:45:AA:BB:CC", + } + self.check_asic_icmp_echo_session_value(session2, expected_adb_values) + + # Check STATE_DB entry related to the ICMP ECHO session, max rx_interval + expected_sdb_values = {"session_guid": "6000", "session_cookie": "12345", + "src_ip": "10.0.0.1", "dst_ip": "10.0.0.2", "tx_interval" :"0", + "rx_interval": "9", "hw_lookup": "false"} + self.check_state_icmp_echo_session_value("default|Ethernet0|6000|RX", expected_sdb_values) + + # Remove the ICMP sessions + self.remove_icmp_echo_session(key1_self) + self.adb.wait_for_deleted_entry("ASIC_STATE:SAI_OBJECT_TYPE_ICMP_ECHO_SESSION", session1) + self.remove_icmp_echo_session(key1_peer) + self.adb.wait_for_deleted_entry("ASIC_STATE:SAI_OBJECT_TYPE_ICMP_ECHO_SESSION", session2) + + # verify max tx interval + icmpEchoSessions = self.get_exist_icmp_echo_session() + + # Create ICMP session 1, use lower than min rx/tx interval + fieldValues = {"session_cookie": "12345", + "src_ip": "10.0.0.1", "dst_ip":"10.0.0.2", "tx_interval": "1000000", + "rx_interval": "300", "dst_mac": "01:23:45:aa:bb:cc"} + + key1_self = "default:Ethernet0:5000:NORMAL" + self.create_icmp_echo_session(key1_self, fieldValues) + self.adb.wait_for_n_keys("ASIC_STATE:SAI_OBJECT_TYPE_ICMP_ECHO_SESSION", len(icmpEchoSessions) + 1) + + # Checked created ICMP ECHO session in ASIC_DB + createdSessions = self.get_exist_icmp_echo_session() - icmpEchoSessions + assert len(createdSessions) == 1 + + # self session + session1 = createdSessions.pop() + expected_adb_values = { + "SAI_ICMP_ECHO_SESSION_ATTR_GUID": "5000", + "SAI_ICMP_ECHO_SESSION_ATTR_COOKIE": "12345", + "SAI_ICMP_ECHO_SESSION_ATTR_TX_INTERVAL": "1200000", + "SAI_ICMP_ECHO_SESSION_ATTR_RX_INTERVAL": "300000", + "SAI_ICMP_ECHO_SESSION_ATTR_SRC_IP_ADDRESS": "10.0.0.1", + "SAI_ICMP_ECHO_SESSION_ATTR_DST_IP_ADDRESS": "10.0.0.2", + "SAI_ICMP_ECHO_SESSION_ATTR_IPHDR_VERSION": "4", + "SAI_ICMP_ECHO_SESSION_ATTR_HW_LOOKUP_VALID": "false", + "SAI_ICMP_ECHO_SESSION_ATTR_DST_MAC_ADDRESS": "01:23:45:AA:BB:CC", + } + self.check_asic_icmp_echo_session_value(session1, expected_adb_values) + + # Check STATE_DB entry related to the ICMP ECHO session + expected_sdb_values = {"session_guid": "5000", "session_cookie": "12345", + "src_ip": "10.0.0.1", "dst_ip": "10.0.0.2", "tx_interval" :"1200", + "rx_interval": "300", "hw_lookup": "false"} + self.check_state_icmp_echo_session_value("default|Ethernet0|5000|NORMAL", expected_sdb_values) + + # Remove the ICMP session + self.remove_icmp_echo_session(key1_self) + self.adb.wait_for_deleted_entry("ASIC_STATE:SAI_OBJECT_TYPE_ICMP_ECHO_SESSION", session1) + + keys = self.sdb.get_keys("ICMP_ECHO_SESSION_TABLE") + assert len(keys) == 0