diff --git a/orchagent/orchdaemon.cpp b/orchagent/orchdaemon.cpp index 0502ccbeb50..8cc859ea698 100644 --- a/orchagent/orchdaemon.cpp +++ b/orchagent/orchdaemon.cpp @@ -63,6 +63,7 @@ Srv6Orch *gSrv6Orch; FlowCounterRouteOrch *gFlowCounterRouteOrch; DebugCounterOrch *gDebugCounterOrch; MonitorOrch *gMonitorOrch; +BfdMonitorOrch *gBfdMonitorOrch; TunnelDecapOrch *gTunneldecapOrch; StpOrch *gStpOrch; MuxOrch *gMuxOrch; @@ -260,6 +261,8 @@ bool OrchDaemon::init() gDirectory.set(vrf_orch); gMonitorOrch = new MonitorOrch(m_stateDb, STATE_VNET_MONITOR_TABLE_NAME); gDirectory.set(gMonitorOrch); + gBfdMonitorOrch = new BfdMonitorOrch(m_stateDb, STATE_BFD_SESSION_TABLE_NAME); + gDirectory.set(gBfdMonitorOrch); const vector chassis_frontend_tables = { CFG_PASS_THROUGH_ROUTE_TABLE_NAME, @@ -471,7 +474,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, gIcmpOrch, 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, gBfdMonitorOrch, gStpOrch}; bool initialize_dtel = false; if (platform == BFN_PLATFORM_SUBSTRING || platform == VS_PLATFORM_SUBSTRING) diff --git a/orchagent/vnetorch.cpp b/orchagent/vnetorch.cpp index 89e98c31a67..f41c4af135d 100644 --- a/orchagent/vnetorch.cpp +++ b/orchagent/vnetorch.cpp @@ -778,7 +778,7 @@ bool VNetRouteOrch::addNextHopGroup(const string& vnet, const NextHopGroupKey &n for (auto it : next_hop_set) { nh_seq_id_in_nhgrp[it] = ++seq_id; - if (monitoring != "custom" && nexthop_info_[vnet].find(it.ip_address) != nexthop_info_[vnet].end() && nexthop_info_[vnet][it.ip_address].bfd_state != SAI_BFD_SESSION_STATE_UP) + if (monitoring != VNET_MONITORING_TYPE_CUSTOM && monitoring != VNET_MONITORING_TYPE_CUSTOM_BFD && nexthop_info_[vnet].find(it.ip_address) != nexthop_info_[vnet].end() && nexthop_info_[vnet][it.ip_address].bfd_state != SAI_BFD_SESSION_STATE_UP) { continue; } @@ -960,7 +960,7 @@ bool VNetRouteOrch::createNextHopGroup(const string& vnet, next_hop_group_entry.ref_count = 0; } - if (monitoring == "custom" || nexthop_info_[vnet].find(nexthop.ip_address) == nexthop_info_[vnet].end() || nexthop_info_[vnet][nexthop.ip_address].bfd_state == SAI_BFD_SESSION_STATE_UP) + if (monitoring == VNET_MONITORING_TYPE_CUSTOM || monitoring == VNET_MONITORING_TYPE_CUSTOM_BFD || nexthop_info_[vnet].find(nexthop.ip_address) == nexthop_info_[vnet].end() || nexthop_info_[vnet][nexthop.ip_address].bfd_state == SAI_BFD_SESSION_STATE_UP) { SWSS_LOG_INFO("Adding nexthop: %s to the active group", nexthop.ip_address.to_string().c_str()); next_hop_group_entry.active_members[nexthop] = SAI_NULL_OBJECT_ID; @@ -997,12 +997,18 @@ NextHopGroupKey VNetRouteOrch::getActiveNHSet(const string& vnet, { if (monitor.second.endpoint == it) { - if (monitor.second.state == MONITOR_SESSION_STATE_UP) + if (monitor.second.monitoring_type == VNET_MONITORING_TYPE_CUSTOM && monitor.second.state == MONITOR_SESSION_STATE_UP) { // monitor session exists and is up nhg_custom.add(it); } + + if (monitor.second.monitoring_type == VNET_MONITORING_TYPE_CUSTOM_BFD && monitor.second.custom_bfd_state == SAI_BFD_SESSION_STATE_UP) + { + // BFD session exists and is up + nhg_custom.add(it); + } continue; } } @@ -1015,6 +1021,8 @@ bool VNetRouteOrch::selectNextHopGroup(const string& vnet, NextHopGroupKey& nexthops_primary, NextHopGroupKey& nexthops_secondary, const string& monitoring, + const int32_t rx_monitor_timer, + const int32_t tx_monitor_timer, IpPrefix& ipPrefix, VNetVrfObject *vrf_obj, NextHopGroupKey& nexthops_selected, @@ -1028,23 +1036,24 @@ bool VNetRouteOrch::selectNextHopGroup(const string& vnet, // depending on the endpoint monitor state. If no NHG from primary is created, we attempt // the same for secondary. - if(nexthops_secondary.getSize() != 0 && monitoring == "custom") + if(nexthops_secondary.getSize() != 0 && + (monitoring == VNET_MONITORING_TYPE_CUSTOM || monitoring == VNET_MONITORING_TYPE_CUSTOM_BFD)) { auto it_route = syncd_tunnel_routes_[vnet].find(ipPrefix); if (it_route == syncd_tunnel_routes_[vnet].end()) { - setEndpointMonitor(vnet, monitors, nexthops_primary, monitoring, ipPrefix); - setEndpointMonitor(vnet, monitors, nexthops_secondary, monitoring, ipPrefix); + setEndpointMonitor(vnet, monitors, nexthops_primary, monitoring, rx_monitor_timer, tx_monitor_timer, ipPrefix); + setEndpointMonitor(vnet, monitors, nexthops_secondary, monitoring, rx_monitor_timer, tx_monitor_timer, ipPrefix); } else { if (it_route->second.primary != nexthops_primary) { - setEndpointMonitor(vnet, monitors, nexthops_primary, monitoring, ipPrefix); + setEndpointMonitor(vnet, monitors, nexthops_primary, monitoring, rx_monitor_timer, tx_monitor_timer, ipPrefix); } if (it_route->second.secondary != nexthops_secondary) { - setEndpointMonitor(vnet, monitors, nexthops_secondary, monitoring, ipPrefix); + setEndpointMonitor(vnet, monitors, nexthops_secondary, monitoring, rx_monitor_timer, tx_monitor_timer, ipPrefix); } nexthops_selected = it_route->second.nhg_key; return true; @@ -1103,7 +1112,7 @@ bool VNetRouteOrch::selectNextHopGroup(const string& vnet, else if (!hasNextHopGroup(vnet, nexthops_primary)) { SWSS_LOG_INFO("Creating next hop group %s", nexthops_primary.to_string().c_str()); - setEndpointMonitor(vnet, monitors, nexthops_primary, monitoring, ipPrefix); + setEndpointMonitor(vnet, monitors, nexthops_primary, monitoring, rx_monitor_timer, tx_monitor_timer, ipPrefix); if (!createNextHopGroup(vnet, nexthops_primary, vrf_obj, monitoring)) { delEndpointMonitor(vnet, nexthops_primary, ipPrefix); @@ -1117,7 +1126,10 @@ bool VNetRouteOrch::selectNextHopGroup(const string& vnet, template<> bool VNetRouteOrch::doRouteTask(const string& vnet, IpPrefix& ipPrefix, NextHopGroupKey& nexthops, string& op, string& profile, - const string& monitoring, NextHopGroupKey& nexthops_secondary, + const string& monitoring, + const int32_t rx_monitor_timer, + const int32_t tx_monitor_timer, + NextHopGroupKey& nexthops_secondary, const IpPrefix& adv_prefix, const map& monitors) { @@ -1158,7 +1170,7 @@ bool VNetRouteOrch::doRouteTask(const string& vnet, IpPrefix& ipP { sai_object_id_t nh_id = SAI_NULL_OBJECT_ID; NextHopGroupKey active_nhg("", true); - if (!selectNextHopGroup(vnet, nexthops, nexthops_secondary, monitoring, ipPrefix, vrf_obj, active_nhg, monitors)) + if (!selectNextHopGroup(vnet, nexthops, nexthops_secondary, monitoring, rx_monitor_timer, tx_monitor_timer, ipPrefix, vrf_obj, active_nhg, monitors)) { return true; } @@ -1235,11 +1247,11 @@ bool VNetRouteOrch::doRouteTask(const string& vnet, IpPrefix& ipP bool priority_route_updated = false; if (it_route != syncd_tunnel_routes_[vnet].end() && ((monitoring == "" && it_route->second.nhg_key != nexthops) || - (monitoring == "custom" && (it_route->second.primary != nexthops || it_route->second.secondary != nexthops_secondary)))) + ((monitoring == VNET_MONITORING_TYPE_CUSTOM || monitoring == VNET_MONITORING_TYPE_CUSTOM_BFD) && (it_route->second.primary != nexthops || it_route->second.secondary != nexthops_secondary)))) { route_updated = true; NextHopGroupKey nhg = it_route->second.nhg_key; - if (monitoring == "custom") + if (monitoring == VNET_MONITORING_TYPE_CUSTOM || monitoring == VNET_MONITORING_TYPE_CUSTOM_BFD) { // if the previously active NHG is same as the newly created active NHG.case of primary secondary swap or //when primary is active and secondary is changed or vice versa. In these cases we dont remove the NHG @@ -1279,7 +1291,7 @@ bool VNetRouteOrch::doRouteTask(const string& vnet, IpPrefix& ipP } } } - if (monitoring != "custom") + if (monitoring != VNET_MONITORING_TYPE_CUSTOM && monitoring != VNET_MONITORING_TYPE_CUSTOM_BFD) { delEndpointMonitor(vnet, nhg, ipPrefix); } @@ -1981,7 +1993,7 @@ void VNetRouteOrch::delRoute(const IpPrefix& ipPrefix) syncd_routes_.erase(route_itr); } -void VNetRouteOrch::createBfdSession(const string& vnet, const NextHopKey& endpoint, const IpAddress& monitor_addr) +void VNetRouteOrch::createBfdSession(const string& vnet, const NextHopKey& endpoint, const IpAddress& monitor_addr, const int32_t rx_monitor_timer, const int32_t tx_monitor_timer) { SWSS_LOG_ENTER(); @@ -2012,6 +2024,19 @@ void VNetRouteOrch::createBfdSession(const string& vnet, const NextHopKey& endpo // when the device goes into TSA. The following parameter ensures that these session are // brought down while transitioning to TSA and brought back up when transitioning to TSB. data.emplace_back("shutdown_bfd_during_tsa", "true"); + + if (rx_monitor_timer >= 0) + { + FieldValueTuple fv_rx("rx_interval", to_string(rx_monitor_timer)); + data.push_back(fv_rx); + } + + if (tx_monitor_timer >= 0) + { + FieldValueTuple fv_tx("tx_interval", to_string(tx_monitor_timer)); + data.push_back(fv_tx); + } + bfd_session_producer_.set(key, data); bfd_sessions_[monitor_addr].bfd_state = SAI_BFD_SESSION_STATE_DOWN; } @@ -2039,9 +2064,7 @@ void VNetRouteOrch::removeBfdSession(const string& vnet, const NextHopKey& endpo nexthop_info_[vnet].erase(endpoint_addr); string key = "default:default:" + monitor_addr.to_string(); - bfd_session_producer_.del(key); - bfd_sessions_.erase(monitor_addr); } @@ -2100,6 +2123,58 @@ void VNetRouteOrch::createMonitoringSession(const string& vnet, const NextHopKey } +void VNetRouteOrch::createCustomBFDMonitoringSession(const string& vnet, const NextHopKey& endpoint, const IpAddress& monitor_addr, IpPrefix& ipPrefix, const int32_t rx_monitor_timer, const int32_t tx_monitor_timer) +{ + SWSS_LOG_ENTER(); + + if (bfd_sessions_.find(monitor_addr) == bfd_sessions_.end()) + { + vector data; + string key = "default:default:" + monitor_addr.to_string(); + + auto tun_name = vnet_orch_->getTunnelName(vnet); + VxlanTunnelOrch* vxlan_orch = gDirectory.get(); + auto tunnel_obj = vxlan_orch->getVxlanTunnel(tun_name); + /* + Even for local endpoints, we will use tunnel source IP as local_addr of BFD session. + */ + IpAddress src_ip = tunnel_obj->getSrcIP(); + + FieldValueTuple fvTuple("local_addr", src_ip.to_string()); + data.push_back(fvTuple); + data.emplace_back("multihop", "true"); + // The BFD sessions established by the Vnet routes with monitoring need to be brought down + // when the device goes into TSA. The following parameter ensures that these session are + // brought down while transitioning to TSA and brought back up when transitioning to TSB. + data.emplace_back("shutdown_bfd_during_tsa", "true"); + + if (rx_monitor_timer >= 0) + { + FieldValueTuple fv_rx("rx_interval", to_string(rx_monitor_timer)); + data.push_back(fv_rx); + } + + if (tx_monitor_timer >= 0) + { + FieldValueTuple fv_tx("tx_interval", to_string(tx_monitor_timer)); + data.push_back(fv_tx); + } + + bfd_session_producer_.set(key, data); + bfd_sessions_[monitor_addr].bfd_state = SAI_BFD_SESSION_STATE_DOWN; + bfd_sessions_[monitor_addr].vnet = vnet; + bfd_sessions_[monitor_addr].endpoint = endpoint; + bfd_sessions_[monitor_addr].custom_bfd = true; + } + + MonitorSessionInfo info = monitor_info_[vnet][ipPrefix][monitor_addr]; + info.endpoint = endpoint; + info.ref_count = 1; + info.monitoring_type = VNET_MONITORING_TYPE_CUSTOM_BFD; + info.custom_bfd_state = SAI_BFD_SESSION_STATE_DOWN; + monitor_info_[vnet][ipPrefix][monitor_addr] = info; +} + void VNetRouteOrch::removeMonitoringSession(const string& vnet, const NextHopKey& endpoint, const IpAddress& monitor_addr, IpPrefix& ipPrefix) { SWSS_LOG_ENTER(); @@ -2110,13 +2185,22 @@ void VNetRouteOrch::removeMonitoringSession(const string& vnet, const NextHopKey SWSS_LOG_NOTICE("Monitor session for prefix %s endpoint %s does not exist", ipPrefix.to_string().c_str(), endpoint.to_string().c_str()); } - string key = monitor_addr.to_string() + ":" + ipPrefix.to_string(); + if (monitor_info_[vnet][ipPrefix][monitor_addr].monitoring_type == VNET_MONITORING_TYPE_CUSTOM_BFD) + { + string key = "default:default:" + monitor_addr.to_string(); + bfd_session_producer_.del(key); + bfd_sessions_.erase(monitor_addr); + } + else + { + string key = monitor_addr.to_string() + ":" + ipPrefix.to_string(); + monitor_session_producer_->del(key); + } - monitor_session_producer_->del(key); monitor_info_[vnet][ipPrefix].erase(monitor_addr); } -void VNetRouteOrch::setEndpointMonitor(const string& vnet, const map& monitors, NextHopGroupKey& nexthops, const string& monitoring, IpPrefix& ipPrefix) +void VNetRouteOrch::setEndpointMonitor(const string& vnet, const map& monitors, NextHopGroupKey& nexthops, const string& monitoring, const int32_t rx_monitor_timer, const int32_t tx_monitor_timer, IpPrefix& ipPrefix) { SWSS_LOG_ENTER(); @@ -2127,12 +2211,24 @@ void VNetRouteOrch::setEndpointMonitor(const string& vnet, const map next_hop_set = nexthops.getNextHops(); if (next_hop_set.find(nh) != next_hop_set.end()) { - if (monitoring == "custom") + if (!monitoring.empty()) { if (monitor_info_[vnet].find(ipPrefix) == monitor_info_[vnet].end() || monitor_info_[vnet][ipPrefix].find(monitor_ip) == monitor_info_[vnet][ipPrefix].end()) { - createMonitoringSession(vnet, nh, monitor_ip, ipPrefix); + if (monitoring == VNET_MONITORING_TYPE_CUSTOM) + { + createMonitoringSession(vnet, nh, monitor_ip, ipPrefix); + } + else if (monitoring == VNET_MONITORING_TYPE_CUSTOM_BFD) + { + /* + * Current BFD monitoring doesn't support the failover between primary and secondary NHG. + * To avoid the complexity/regression, we temporarily introduce custom_bfd monitoring type. + * It will be same behavior as custom monitoring type, except that it will create BFD session. + */ + createCustomBFDMonitoringSession(vnet, nh, monitor_ip, ipPrefix, rx_monitor_timer, tx_monitor_timer); + } } else { @@ -2145,11 +2241,16 @@ void VNetRouteOrch::setEndpointMonitor(const string& vnet, const mapsecond; + bfd_info.bfd_state = sai_state; + string vnet = bfd_info.vnet; + NextHopKey endpoint = bfd_info.endpoint; + + if (monitor_info_.find(vnet) == monitor_info_.end()) + { + SWSS_LOG_WARN("No custom monitoring session info for vnet %s", vnet.c_str()); + return; + } + + for (auto iter : monitor_info_[vnet]) + { + auto prefix = iter.first; + if (monitor_info_[vnet][prefix].find(monitoring_ip) != monitor_info_[vnet][prefix].end() && + monitor_info_[vnet][prefix][monitoring_ip].endpoint == endpoint) + { + if ((sai_state == SAI_BFD_SESSION_STATE_UP || sai_state == SAI_BFD_SESSION_STATE_DOWN) && + monitor_info_[vnet][prefix][monitoring_ip].custom_bfd_state != sai_state) + { + SWSS_LOG_NOTICE("Custom BFD Monitor session state for %s:%s, endpoint:%s, monitoring_ip:%s changed to %s", + vnet.c_str(), + prefix.to_string().c_str(), + endpoint.ip_address.to_string().c_str(), + monitoring_ip.to_string().c_str(), + state.c_str()); + + struct MonitorUpdate status_update; + status_update.monitoring_type = VNET_MONITORING_TYPE_CUSTOM_BFD; + status_update.custom_bfd_state = sai_state; + status_update.prefix = prefix; + status_update.monitor = monitoring_ip; + status_update.vnet = vnet; + updateVnetTunnelCustomMonitor(status_update); + } + } + } +} + void VNetRouteOrch::updateMonitorState(string& op, const IpPrefix& prefix, const IpAddress& monitor, string state) { SWSS_LOG_ENTER(); @@ -2396,6 +2562,13 @@ void VNetRouteOrch::updateVnetTunnel(const BfdUpdate& update) } BfdSessionInfo& bfd_info = it_peer->second; + + if (bfd_info.custom_bfd) + { + SWSS_LOG_DEBUG("Skip single NHG BFD state update for custom BFD session %s", peer_address.to_string().c_str()); + return; + } + bfd_info.bfd_state = state; string vnet = bfd_info.vnet; @@ -2609,21 +2782,32 @@ void VNetRouteOrch::updateVnetTunnelCustomMonitor(const MonitorUpdate& update) // MONITOR_SESSION_STATE_UNKNOWN and config_update and updateRoute are set to true. // This function should never recieve MONITOR_SESSION_STATE_UNKNOWN from MonitorOrch. + auto monitoring_type = update.monitoring_type; + auto custom_bfd_state = update.custom_bfd_state; auto prefix = update.prefix; auto state = update.state; auto monitor = update.monitor; auto vnet = update.vnet; bool updateRoute = false; bool config_update = false; - if (state != MONITOR_SESSION_STATE_UNKNOWN) + + if (monitoring_type == VNET_MONITORING_TYPE_CUSTOM) { - monitor_info_[vnet][prefix][monitor].state = state; + if (state != MONITOR_SESSION_STATE_UNKNOWN) + { + monitor_info_[vnet][prefix][monitor].state = state; + } + else + { + // we are coming here as a result of route config update. We need to repost the route if applicable. + updateRoute = true; + config_update = true; + } } - else + + if(monitoring_type == VNET_MONITORING_TYPE_CUSTOM_BFD) { - // we are coming here as a result of route config update. We need to repost the route if applicable. - updateRoute = true; - config_update = true; + monitor_info_[vnet][prefix][monitor].custom_bfd_state = custom_bfd_state; } auto route = syncd_tunnel_routes_[vnet].find(prefix); @@ -2660,7 +2844,7 @@ void VNetRouteOrch::updateVnetTunnelCustomMonitor(const MonitorUpdate& update) { if (!hasNextHopGroup(vnet, nhg_custom_primary)) { - if (!createNextHopGroup(vnet, nhg_custom_primary, vrf_obj, "custom")) + if (!createNextHopGroup(vnet, nhg_custom_primary, vrf_obj, monitoring_type)) { SWSS_LOG_WARN("Failed to create primary based custom next hop group. Cannot proceed."); return; @@ -2679,9 +2863,9 @@ void VNetRouteOrch::updateVnetTunnelCustomMonitor(const MonitorUpdate& update) { if (!hasNextHopGroup(vnet, nhg_custom_secondary)) { - if (!createNextHopGroup(vnet, nhg_custom_secondary, vrf_obj, "custom")) + if (!createNextHopGroup(vnet, nhg_custom_secondary, vrf_obj, monitoring_type)) { - SWSS_LOG_WARN("Failed to create primary based custom next hop group. Cannot proceed."); + SWSS_LOG_WARN("Failed to create secondary based custom next hop group. Cannot proceed."); return; } } @@ -2865,6 +3049,8 @@ bool VNetRouteOrch::handleTunnel(const Request& request) vector primary_list; vector secondary_list; string monitoring; + int32_t rx_monitor_timer = -1; + int32_t tx_monitor_timer = -1; swss::IpPrefix adv_prefix; bool has_priority_ep = false; bool has_adv_pfx = false; @@ -2910,6 +3096,14 @@ bool VNetRouteOrch::handleTunnel(const Request& request) { check_directly_connected = request.getAttrBool(name); } + else if (name == "rx_monitor_timer") + { + rx_monitor_timer = static_cast(request.getAttrUint(name)); + } + else if (name == "tx_monitor_timer") + { + tx_monitor_timer = static_cast(request.getAttrUint(name)); + } else { SWSS_LOG_INFO("Unknown attribute: %s", name.c_str()); @@ -3045,7 +3239,7 @@ bool VNetRouteOrch::handleTunnel(const Request& request) } if (vnet_orch_->isVnetExecVrf()) { - return doRouteTask(vnet_name, ip_pfx, (has_priority_ep == true) ? nhg_primary : nhg, op, profile, monitoring, nhg_secondary, adv_prefix, monitors); + return doRouteTask(vnet_name, ip_pfx, (has_priority_ep == true) ? nhg_primary : nhg, op, profile, monitoring, rx_monitor_timer, tx_monitor_timer, nhg_secondary, adv_prefix, monitors); } return true; @@ -3246,7 +3440,7 @@ bool MonitorOrch::addOperation(const Request& request) string op = SET_COMMAND; VNetRouteOrch* vnet_route_orch = gDirectory.get(); - vnet_route_orch->updateMonitorState(op ,ip_Prefix, monitor, session_state ); + vnet_route_orch->updateMonitorState(op, ip_Prefix, monitor, session_state); return true; } @@ -3265,6 +3459,35 @@ bool MonitorOrch::delOperation(const Request& request) return true; } +BfdMonitorOrch::BfdMonitorOrch(DBConnector *db, std::string tableName): + Orch2(db, tableName, request_) +{ + SWSS_LOG_ENTER(); +} + +BfdMonitorOrch::~BfdMonitorOrch(void) +{ + SWSS_LOG_ENTER(); +} + +bool BfdMonitorOrch::addOperation(const Request& request) +{ + SWSS_LOG_ENTER(); + + auto monitor = request.getKeyIpAddress(2); + auto session_state = request.getAttrString("state"); + + VNetRouteOrch* vnet_route_orch = gDirectory.get(); + vnet_route_orch->updateCustomBfdState(monitor, session_state); + + return true; +} + +bool BfdMonitorOrch::delOperation(const Request& request) +{ + return true; +} + VNetTunnelTermAcl::VNetTunnelTermAcl(DBConnector *cfgDb, DBConnector *appDb) { SWSS_LOG_ENTER(); @@ -3420,3 +3643,4 @@ bool VNetTunnelTermAcl::getAclRule(const string vnet_name, const swss::IpPrefix& return false; } + diff --git a/orchagent/vnetorch.h b/orchagent/vnetorch.h index ab7b6dfa9bf..f51ad970976 100644 --- a/orchagent/vnetorch.h +++ b/orchagent/vnetorch.h @@ -24,6 +24,9 @@ #define VXLAN_ENCAP_TTL 128 #define VNET_BITMAP_RIF_MTU 9100 +#define VNET_MONITORING_TYPE_CUSTOM "custom" +#define VNET_MONITORING_TYPE_CUSTOM_BFD "custom_bfd" + extern sai_object_id_t gVirtualRouterId; @@ -311,6 +314,8 @@ const request_description_t vnet_route_description = { { "monitoring", REQ_T_STRING }, { "adv_prefix", REQ_T_IP_PREFIX }, { "check_directly_connected", REQ_T_BOOL }, + { "rx_monitor_timer", REQ_T_UINT }, + { "tx_monitor_timer", REQ_T_UINT }, }, { } }; @@ -323,6 +328,22 @@ const request_description_t monitor_state_request_description = { { "state" } }; +const request_description_t custom_bfd_request_description = { + { REQ_T_STRING, REQ_T_STRING, REQ_T_IP, }, + { + { "type", REQ_T_STRING }, + { "async_active", REQ_T_STRING }, + { "local_discriminator", REQ_T_STRING }, + { "local_addr", REQ_T_IP }, + { "tx_interval", REQ_T_UINT }, + { "rx_interval", REQ_T_UINT }, + { "multiplier", REQ_T_UINT }, + { "multihop", REQ_T_BOOL }, + { "state", REQ_T_STRING }, + }, + { } +}; + class MonitorStateRequest : public Request { public: @@ -342,6 +363,25 @@ class MonitorOrch : public Orch2 MonitorStateRequest request_; }; +class CustomBfdRequest : public Request +{ +public: + CustomBfdRequest() : Request(custom_bfd_request_description, '|') { } +}; + +class BfdMonitorOrch : public Orch2 +{ +public: + BfdMonitorOrch(swss::DBConnector *db, std::string tableName); + virtual ~BfdMonitorOrch(void); + +private: + virtual bool addOperation(const Request& request); + virtual bool delOperation(const Request& request); + + CustomBfdRequest request_; +}; + class VNetRouteRequest : public Request { public: @@ -380,10 +420,14 @@ struct BfdSessionInfo sai_bfd_session_state_t bfd_state; std::string vnet; NextHopKey endpoint; + + bool custom_bfd = false; }; struct MonitorSessionInfo { + std::string monitoring_type = VNET_MONITORING_TYPE_CUSTOM; + sai_bfd_session_state_t custom_bfd_state; monitor_session_state_t state; NextHopKey endpoint; int ref_count; @@ -391,6 +435,8 @@ struct MonitorSessionInfo struct MonitorUpdate { + std::string monitoring_type = VNET_MONITORING_TYPE_CUSTOM; + sai_bfd_session_state_t custom_bfd_state; monitor_session_state_t state; IpAddress monitor; IpPrefix prefix; @@ -458,6 +504,7 @@ class VNetRouteOrch : public Orch2, public Subject, public Observer void update(SubjectType, void *); void updateMonitorState(string& op, const IpPrefix& prefix , const IpAddress& endpoint, string state); + void updateCustomBfdState(const IpAddress& monitoring_ip, const string& state); void updateAllMonitoringSession(const string& vnet); private: @@ -479,16 +526,18 @@ class VNetRouteOrch : public Orch2, public Subject, public Observer const string& monitoring); NextHopGroupKey getActiveNHSet(const string&, NextHopGroupKey&, const IpPrefix& ); - bool selectNextHopGroup(const string&, NextHopGroupKey&, NextHopGroupKey&, const string&, IpPrefix&, + bool selectNextHopGroup(const string&, NextHopGroupKey&, NextHopGroupKey&, const string&, const int32_t, const int32_t, IpPrefix&, VNetVrfObject *vrf_obj, NextHopGroupKey&, const std::map& monitors=std::map()); - void createBfdSession(const string& vnet, const NextHopKey& endpoint, const IpAddress& ipAddr); + void createBfdSession(const string& vnet, const NextHopKey& endpoint, const IpAddress& ipAddr, const int32_t rx_monitor_timer, const int32_t tx_monitor_timer); void removeBfdSession(const string& vnet, const NextHopKey& endpoint, const IpAddress& ipAddr); + void createCustomBFDMonitoringSession(const string& vnet, const NextHopKey& endpoint, const IpAddress& monitor_addr, IpPrefix& ipPrefix, const int32_t rx_monitor_timer, const int32_t tx_monitor_timer); void createMonitoringSession(const string& vnet, const NextHopKey& endpoint, const IpAddress& ipAddr, IpPrefix& ipPrefix); void removeMonitoringSession(const string& vnet, const NextHopKey& endpoint, const IpAddress& ipAddr, IpPrefix& ipPrefix); void setEndpointMonitor(const string& vnet, const map& monitors, NextHopGroupKey& nexthops, - const string& monitoring, IpPrefix& ipPrefix); + const string& monitoring, const int32_t rx_monitor_timer, const int32_t tx_monitor_timer, + IpPrefix& ipPrefix); void delEndpointMonitor(const string& vnet, NextHopGroupKey& nexthops, IpPrefix& ipPrefix); void postRouteState(const string& vnet, IpPrefix& ipPrefix, NextHopGroupKey& nexthops, string& profile); void removeRouteState(const string& vnet, IpPrefix& ipPrefix); @@ -506,7 +555,8 @@ class VNetRouteOrch : public Orch2, public Subject, public Observer template bool doRouteTask(const string& vnet, IpPrefix& ipPrefix, NextHopGroupKey& nexthops, string& op, string& profile, - const string& monitoring, NextHopGroupKey& nexthops_secondary, const IpPrefix& adv_prefix, + const string& monitoring, const int32_t rx_monitor_timer, const int32_t tx_monitor_timer, + NextHopGroupKey& nexthops_secondary, const IpPrefix& adv_prefix, const std::map& monitors=std::map()); template diff --git a/tests/test_vnet.py b/tests/test_vnet.py index 51beed43949..d9449f54255 100644 --- a/tests/test_vnet.py +++ b/tests/test_vnet.py @@ -3120,7 +3120,162 @@ def test_vnet_local_route_ecmp(self, dvs, testlog): delete_vxlan_tunnel(dvs, tunnel_name) vnet_obj.check_del_vxlan_tunnel(dvs) + + ''' + Test 32 - Test for priority vnet tunnel routes with ECMP nexthop group and local nhg. test primary secondary switchover + bfd monitoring + rx and tx timer. + ''' + def test_vnet_orch_32(self, dvs, testlog): + self.setup_db(dvs) + + vnet_obj = self.get_vnet_obj() + tunnel_name = 'tunnel_32' + vnet_name = 'vnet32' + asic_db = swsscommon.DBConnector(swsscommon.ASIC_DB, dvs.redis_sock, 0) + + vnet_obj.fetch_exist_entries(dvs) + + create_vxlan_tunnel(dvs, tunnel_name, '9.9.9.9') + create_vnet_entry(dvs, vnet_name, tunnel_name, '10029', "", advertise_prefix=True, overlay_dmac="22:33:33:44:44:66") + + vnet_obj.check_vnet_entry(dvs, vnet_name) + vnet_obj.check_vxlan_tunnel_entry(dvs, tunnel_name, vnet_name, '10029') + + vnet_obj.check_vxlan_tunnel(dvs, tunnel_name, '9.9.9.9') + + # create l3 interface + self.create_l3_intf("Ethernet8", "") + self.create_l3_intf("Ethernet12", "") + + # set ip address + self.add_ip_address("Ethernet8", "9.1.0.3/32") + self.add_ip_address("Ethernet12", "9.1.0.4/32") + + # bring up interface + self.set_admin_status("Ethernet8", "up") + self.set_admin_status("Ethernet12", "up") + + # add neighbor for directly connected endpoint + self.add_neighbor("Ethernet8", "9.1.0.3", "00:01:02:03:04:05") + self.add_neighbor("Ethernet12", "9.1.0.4", "00:01:02:03:04:06") + + vnet_obj.fetch_exist_entries(dvs) + create_vnet_routes(dvs, "100.100.1.1/32", vnet_name, '9.1.0.1,9.1.0.2,9.1.0.3,9.1.0.4', ep_monitor='9.1.0.1,9.1.0.2,9.1.0.3,9.1.0.4', primary ='9.1.0.1,9.1.0.2', monitoring='custom_bfd', adv_prefix='100.100.1.0/24', check_directly_connected=True, rx_monitor_timer=100, tx_monitor_timer=100) + + # default monitor status is down, route should not be programmed in this status + vnet_obj.check_del_vnet_routes(dvs, vnet_name, ["100.100.1.1/32"]) + check_state_db_routes(dvs, vnet_name, "100.100.1.1/32", []) + check_remove_routes_advertisement(dvs, "100.100.1.0/24") + + # Route should be properly configured when all monitor session states go up. Only primary Endpoints should be in use. + update_bfd_session_state(dvs, '9.1.0.1', 'Up') + update_bfd_session_state(dvs, '9.1.0.2', 'Up') + update_bfd_session_state(dvs, '9.1.0.3', 'Up') + update_bfd_session_state(dvs, '9.1.0.4', 'Up') + time.sleep(2) + route1 = vnet_obj.check_priority_vnet_ecmp_routes(dvs, vnet_name, ['9.1.0.1','9.1.0.2'], tunnel_name) + check_state_db_routes(dvs, vnet_name, "100.100.1.1/32", ['9.1.0.1','9.1.0.2']) + # The default Vnet setting does not advertise prefix + check_routes_advertisement(dvs, "100.100.1.0/24") + + # Remove second primary endpoint from group. + update_bfd_session_state(dvs, '9.1.0.2', 'Down') + + time.sleep(2) + route1= vnet_obj.check_priority_vnet_ecmp_routes(dvs, vnet_name, ['9.1.0.1'], tunnel_name, route_ids=route1) + check_state_db_routes(dvs, vnet_name, "100.100.1.1/32", ['9.1.0.1']) + # The default Vnet setting does not advertise prefix + check_routes_advertisement(dvs, "100.100.1.0/24") + + # Switch to secondary if both primary down + update_bfd_session_state(dvs, '9.1.0.1', 'Down') + time.sleep(2) + check_state_db_routes(dvs, vnet_name, "100.100.1.1/32", ['9.1.0.3','9.1.0.4']) + # The default Vnet setting does not advertise prefix + check_routes_advertisement(dvs, "100.100.1.0/24") + + # removing first endpoint of secondary. route should remain on secondary NHG + update_bfd_session_state(dvs, '9.1.0.3', 'Down') + time.sleep(2) + check_state_db_routes(dvs, vnet_name, "100.100.1.1/32", ['9.1.0.4']) + # The default Vnet setting does not advertise prefix + check_routes_advertisement(dvs, "100.100.1.0/24") + + # removing last endpoint of secondary. route should be removed + update_bfd_session_state(dvs, '9.1.0.4', 'Down') + time.sleep(2) + + new_nhgs = get_all_created_entries(asic_db, vnet_obj.ASIC_NEXT_HOP_GROUP, []) + assert len(new_nhgs) == 0 + check_remove_routes_advertisement(dvs, "100.100.1.0/24") + vnet_obj.check_del_vnet_routes(dvs, vnet_name, ["100.100.1.1/32"]) + check_remove_state_db_routes(dvs, vnet_name, "100.100.1.1/32") + + # Route should come up with secondary endpoints. + update_bfd_session_state(dvs, '9.1.0.3', 'Up') + update_bfd_session_state(dvs, '9.1.0.4', 'Up') + + time.sleep(2) + check_state_db_routes(dvs, vnet_name, "100.100.1.1/32", ['9.1.0.3','9.1.0.4']) + # The default Vnet setting does not advertise prefix + check_routes_advertisement(dvs, "100.100.1.0/24") + + #Route should be switched to the primary endpoint. + update_bfd_session_state(dvs, '9.1.0.1', 'Up') + time.sleep(2) + route1= vnet_obj.check_priority_vnet_ecmp_routes(dvs, vnet_name, ['9.1.0.1'], tunnel_name, route_ids=route1) + check_state_db_routes(dvs, vnet_name, "100.100.1.1/32", ['9.1.0.1']) + # The default Vnet setting does not advertise prefix + check_routes_advertisement(dvs, "100.100.1.0/24") + + #Route should be updated with the second primary endpoint. + update_bfd_session_state(dvs, '9.1.0.2', 'Up') + time.sleep(2) + route1 = vnet_obj.check_priority_vnet_ecmp_routes(dvs, vnet_name, ['9.1.0.1','9.1.0.2'], tunnel_name, route_ids=route1) + check_state_db_routes(dvs, vnet_name, "100.100.1.1/32", ['9.1.0.1','9.1.0.2']) + # The default Vnet setting does not advertise prefix + check_routes_advertisement(dvs, "100.100.1.0/24") + + #Route should not be impacted by seconday endpoints going down. + update_bfd_session_state(dvs, '9.1.0.3', 'Down') + update_bfd_session_state(dvs, '9.1.0.4', 'Down') + time.sleep(2) + route1 = vnet_obj.check_priority_vnet_ecmp_routes(dvs, vnet_name, ['9.1.0.1','9.1.0.2'], tunnel_name, route_ids=route1) + check_state_db_routes(dvs, vnet_name, "100.100.1.1/32", ['9.1.0.1','9.1.0.2']) + # The default Vnet setting does not advertise prefix + check_routes_advertisement(dvs, "100.100.1.0/24") + + #Route should not be impacted by seconday endpoints coming back up. + update_bfd_session_state(dvs, '9.1.0.3', 'Up') + update_bfd_session_state(dvs, '9.1.0.4', 'Up') + time.sleep(2) + route1 = vnet_obj.check_priority_vnet_ecmp_routes(dvs, vnet_name, ['9.1.0.1','9.1.0.2'], tunnel_name, route_ids=route1) + check_state_db_routes(dvs, vnet_name, "100.100.1.1/32", ['9.1.0.1','9.1.0.2']) + # The default Vnet setting does not advertise prefix + check_routes_advertisement(dvs, "100.100.1.0/24") + + # Remove tunnel route 1 + delete_vnet_routes(dvs, "100.100.1.1/32", vnet_name) + time.sleep(2) + vnet_obj.check_del_vnet_routes(dvs, vnet_name, ["100.100.1.1/32"]) + check_remove_state_db_routes(dvs, vnet_name, "100.100.1.1/32") + check_remove_routes_advertisement(dvs, "100.100.1.0/24") + + # Confirm the monitor sessions are removed + check_del_bfd_session(dvs, ['9.1.0.1', '9.1.0.2', '9.1.0.3', '9.1.0.4']) + + delete_vnet_entry(dvs, vnet_name) + vnet_obj.check_del_vnet_entry(dvs, vnet_name) + delete_vxlan_tunnel(dvs, tunnel_name) + + self.remove_neighbor("Ethernet8", "9.1.0.3") + self.remove_ip_address("Ethernet8", "9.1.0.3/32") + self.set_admin_status("Ethernet8", "down") + + self.remove_neighbor("Ethernet12", "9.1.0.4") + self.remove_ip_address("Ethernet12", "9.1.0.4/32") + self.set_admin_status("Ethernet12", "down") + # Add Dummy always-pass test at end as workaroud # for issue when Flaky fail on final test it invokes module tear-down before retrying def test_nonflaky_dummy(): - pass + pass \ No newline at end of file diff --git a/tests/vnet_lib.py b/tests/vnet_lib.py index 81f23b321ab..2eb0162194c 100644 --- a/tests/vnet_lib.py +++ b/tests/vnet_lib.py @@ -140,11 +140,11 @@ def delete_vnet_local_routes(dvs, prefix, vnet_name): time.sleep(2) -def create_vnet_routes(dvs, prefix, vnet_name, endpoint, mac="", vni=0, ep_monitor="", profile="", primary="", monitoring="", adv_prefix="", check_directly_connected=False): - set_vnet_routes(dvs, prefix, vnet_name, endpoint, mac=mac, vni=vni, ep_monitor=ep_monitor, profile=profile, primary=primary, monitoring=monitoring, adv_prefix=adv_prefix, check_directly_connected=check_directly_connected) +def create_vnet_routes(dvs, prefix, vnet_name, endpoint, mac="", vni=0, ep_monitor="", profile="", primary="", monitoring="", rx_monitor_timer=-1, tx_monitor_timer=-1, adv_prefix="", check_directly_connected=False): + set_vnet_routes(dvs, prefix, vnet_name, endpoint, mac=mac, vni=vni, ep_monitor=ep_monitor, profile=profile, primary=primary, monitoring=monitoring, rx_monitor_timer=rx_monitor_timer, tx_monitor_timer=tx_monitor_timer, adv_prefix=adv_prefix, check_directly_connected=check_directly_connected) -def set_vnet_routes(dvs, prefix, vnet_name, endpoint, mac="", vni=0, ep_monitor="", profile="", primary="", monitoring="", adv_prefix="", check_directly_connected=False): +def set_vnet_routes(dvs, prefix, vnet_name, endpoint, mac="", vni=0, ep_monitor="", profile="", primary="", monitoring="", rx_monitor_timer=-1, tx_monitor_timer=-1, adv_prefix="", check_directly_connected=False): conf_db = swsscommon.DBConnector(swsscommon.CONFIG_DB, dvs.redis_sock, 0) attrs = [ @@ -175,6 +175,12 @@ def set_vnet_routes(dvs, prefix, vnet_name, endpoint, mac="", vni=0, ep_monitor= if check_directly_connected: attrs.append(('check_directly_connected', 'true')) + if rx_monitor_timer != -1: + attrs.append(('rx_monitor_timer', str(rx_monitor_timer))) + + if tx_monitor_timer != -1: + attrs.append(('tx_monitor_timer', str(tx_monitor_timer))) + tbl = swsscommon.Table(conf_db, "VNET_ROUTE_TUNNEL") fvs = swsscommon.FieldValuePairs(attrs) tbl.set("%s|%s" % (vnet_name, prefix), fvs)