diff --git a/orchagent/Makefile.am b/orchagent/Makefile.am index 3327079a2809..7db2f403b13a 100644 --- a/orchagent/Makefile.am +++ b/orchagent/Makefile.am @@ -52,7 +52,8 @@ orchagent_SOURCES = \ dtelorch.cpp \ flexcounterorch.cpp \ watermarkorch.cpp \ - policerorch.cpp + policerorch.cpp \ + chassisorch.cpp orchagent_CFLAGS = $(DBGFLAGS) $(AM_CFLAGS) $(CFLAGS_COMMON) $(CFLAGS_SAI) orchagent_CPPFLAGS = $(DBGFLAGS) $(AM_CFLAGS) $(CFLAGS_COMMON) $(CFLAGS_SAI) diff --git a/orchagent/chassisorch.cpp b/orchagent/chassisorch.cpp new file mode 100644 index 000000000000..8a089a8cfb5e --- /dev/null +++ b/orchagent/chassisorch.cpp @@ -0,0 +1,74 @@ +#include "chassisorch.h" +#include "routeorch.h" + +ChassisOrch::ChassisOrch( + DBConnector* cfgDb, + DBConnector* applDb, + const std::vector& tableNames, + VNetRouteOrch* vNetRouteOrch) : + Orch(cfgDb, tableNames), + m_passThroughRouteTable(applDb, APP_PASS_THROUGH_ROUTE_TABLE_NAME), + m_vNetRouteOrch(vNetRouteOrch) +{ +} + +void ChassisOrch::update(SubjectType type, void* ctx) +{ + SWSS_LOG_ENTER(); + VNetNextHopUpdate* updateInfo = reinterpret_cast(ctx); + if (updateInfo->op == SET_COMMAND) + { + addRouteToPassThroughRouteTable(*updateInfo); + } + else + { + deleteRoutePassThroughRouteTable(*updateInfo); + } +} + +void ChassisOrch::addRouteToPassThroughRouteTable(const VNetNextHopUpdate& update) +{ + SWSS_LOG_ENTER(); + + std::vector fvVector; + fvVector.emplace_back("redistribute", "true"); + fvVector.emplace_back("next_vrf_name", update.vnet); + fvVector.emplace_back("next_hop_ip", update.nexthop.ips.to_string()); + fvVector.emplace_back("ifname", update.nexthop.ifname); + fvVector.emplace_back("source", "CHASSIS_ORCH"); + const std::string everflow_route = IpPrefix(update.destination.to_string()).to_string(); + m_passThroughRouteTable.set(everflow_route, fvVector); +} + +void ChassisOrch::deleteRoutePassThroughRouteTable(const VNetNextHopUpdate& update) +{ + SWSS_LOG_ENTER(); + const std::string everflow_route = IpPrefix(update.destination.to_string()).to_string(); + m_passThroughRouteTable.del(everflow_route); +} + +void ChassisOrch::doTask(Consumer &consumer) +{ + SWSS_LOG_ENTER(); + + const std::string & tableName = consumer.getTableName(); + auto it = consumer.m_toSync.begin(); + while (it != consumer.m_toSync.end()) + { + auto t = it->second; + const std::string & op = kfvOp(t); + const std::string & ip = kfvKey(t); + + if (op == SET_COMMAND) + { + m_vNetRouteOrch->attach(this, ip); + } + else + { + m_vNetRouteOrch->detach(this, ip); + } + it = consumer.m_toSync.erase(it); + } + +} + diff --git a/orchagent/chassisorch.h b/orchagent/chassisorch.h new file mode 100644 index 000000000000..260f8b6f4f6a --- /dev/null +++ b/orchagent/chassisorch.h @@ -0,0 +1,36 @@ +#ifndef SWSS_CHASSISORCH_H +#define SWSS_CHASSISORCH_H + +#include +#include +#include +#include + +#include "dbconnector.h" +#include "producerstatetable.h" +#include "orch.h" +#include "observer.h" +#include "vnetorch.h" + +class ChassisOrch : public Orch, public Observer +{ +public: + ChassisOrch( + DBConnector* cfgDb, + DBConnector *applDb, + const std::vector &tableNames, + VNetRouteOrch * vNetRouteOrch); + +private: + + void update(SubjectType, void*); + void addRouteToPassThroughRouteTable(const VNetNextHopUpdate& update); + void deleteRoutePassThroughRouteTable(const VNetNextHopUpdate& update); + + virtual void doTask(Consumer &consumer); + + Table m_passThroughRouteTable; + VNetRouteOrch* m_vNetRouteOrch; +}; + +#endif \ No newline at end of file diff --git a/orchagent/orchdaemon.cpp b/orchagent/orchdaemon.cpp index 400d24f85a96..1ab322437b46 100644 --- a/orchagent/orchdaemon.cpp +++ b/orchagent/orchdaemon.cpp @@ -8,6 +8,7 @@ #define SAI_SWITCH_ATTR_CUSTOM_RANGE_BASE SAI_SWITCH_ATTR_CUSTOM_RANGE_START #include "sairedis.h" +#include "chassisorch.h" using namespace std; using namespace swss; @@ -112,6 +113,12 @@ bool OrchDaemon::init() VRFOrch *vrf_orch = new VRFOrch(m_applDb, APP_VRF_TABLE_NAME); gDirectory.set(vrf_orch); + const vector chassis_frontend_tables = { + CFG_PASS_THROUGH_ROUTE_TABLE_NAME, + }; + ChassisOrch* chassis_frontend_orch = new ChassisOrch(m_configDb, m_applDb, chassis_frontend_tables, vnet_rt_orch); + gDirectory.set(chassis_frontend_orch); + gIntfsOrch = new IntfsOrch(m_applDb, APP_INTF_TABLE_NAME, vrf_orch); gNeighOrch = new NeighOrch(m_applDb, APP_NEIGH_TABLE_NAME, gIntfsOrch); gRouteOrch = new RouteOrch(m_applDb, APP_ROUTE_TABLE_NAME, gNeighOrch); @@ -225,6 +232,7 @@ bool OrchDaemon::init() m_orchList.push_back(gFdbOrch); m_orchList.push_back(mirror_orch); m_orchList.push_back(gAclOrch); + m_orchList.push_back(chassis_frontend_orch); m_orchList.push_back(vrf_orch); m_orchList.push_back(vxlan_tunnel_orch); m_orchList.push_back(vxlan_tunnel_map_orch); diff --git a/orchagent/vnetorch.cpp b/orchagent/vnetorch.cpp index b5a0739a6097..684d77343332 100644 --- a/orchagent/vnetorch.cpp +++ b/orchagent/vnetorch.cpp @@ -5,6 +5,7 @@ #include #include #include +#include #include "sai.h" #include "saiextensions.h" @@ -1911,6 +1912,15 @@ bool VNetRouteOrch::handleRoutes(const Request& request) SWSS_LOG_INFO("VNET-RT '%s' op '%s' for ip %s", vnet_name.c_str(), op.c_str(), ip_pfx.to_string().c_str()); + + if (op == SET_COMMAND) + { + addRoute(vnet_name, ip_pfx, nh); + } + else + { + delRoute(ip_pfx); + } if (vnet_orch_->isVnetExecVrf()) { @@ -1924,6 +1934,191 @@ bool VNetRouteOrch::handleRoutes(const Request& request) return true; } +void VNetRouteOrch::attach(Observer* observer, const IpAddress& dstAddr) +{ + SWSS_LOG_ENTER(); + + auto insert_result = next_hop_observers_.emplace(dstAddr, VNetNextHopObserverEntry()); + auto observerEntry = insert_result.first; + /* Create a new observer entry if no current observer is observing this + * IP address */ + if (insert_result.second) + { + /* Find the prefixes that cover the destination IP */ + for (auto route : syncd_routes_) + { + if (route.first.isAddressInSubnet(dstAddr)) + { + SWSS_LOG_INFO("Prefix %s covers destination address", + route.first.to_string().c_str()); + + observerEntry->second.routeTable.emplace( + route.first, + route.second + ); + } + } + } + + observerEntry->second.observers.push_back(observer); + + auto bestRoute = observerEntry->second.routeTable.rbegin(); + if (bestRoute != observerEntry->second.routeTable.rend()) + { + SWSS_LOG_NOTICE("Attached next hop observer of route %s for destination IP %s", + bestRoute->first.to_string().c_str(), + dstAddr.to_string().c_str()); + for (auto vnetEntry : bestRoute->second) + { + VNetNextHopUpdate update = + { + SET_COMMAND, + vnetEntry.first, // vnet name + dstAddr, // destination + bestRoute->first, // prefix + vnetEntry.second // nexthop + }; + observer->update(SUBJECT_TYPE_NEXTHOP_CHANGE, reinterpret_cast(&update)); + } + } +} + +void VNetRouteOrch::detach(Observer* observer, const IpAddress& dstAddr) +{ + SWSS_LOG_ENTER(); + auto observerEntry = next_hop_observers_.find(dstAddr); + + if (observerEntry == next_hop_observers_.end()) + { + SWSS_LOG_ERROR("Failed to detach observer for %s. Entry not found.", dstAddr.to_string().c_str()); + assert(false); + return; + } + + auto iter = std::find( + observerEntry->second.observers.begin(), + observerEntry->second.observers.end(), + observer); + if (iter == observerEntry->second.observers.end()) + { + SWSS_LOG_ERROR("Failed to detach observer for %s. Observer not found.", dstAddr.to_string().c_str()); + assert(false); + return; + } + + auto bestRoute = observerEntry->second.routeTable.rbegin(); + if (bestRoute != observerEntry->second.routeTable.rend()) + { + for (auto vnetEntry : bestRoute->second) + { + VNetNextHopUpdate update = + { + DEL_COMMAND, + vnetEntry.first, // vnet name + dstAddr, // destination + bestRoute->first, // prefix + vnetEntry.second // nexthop + }; + observer->update(SUBJECT_TYPE_NEXTHOP_CHANGE, reinterpret_cast(&update)); + } + } + next_hop_observers_.erase(observerEntry); +} + +void VNetRouteOrch::addRoute(const std::string& vnet, const IpPrefix& ipPrefix, const nextHop& nh) +{ + SWSS_LOG_ENTER(); + for (auto& next_hop_observer : next_hop_observers_) + { + if (ipPrefix.isAddressInSubnet(next_hop_observer.first)) + { + auto route_insert_result = next_hop_observer.second.routeTable.emplace(ipPrefix, VNetEntry()); + + auto vnet_result_result = route_insert_result.first->second.emplace(vnet, nh); + if (!vnet_result_result.second) + { + if (vnet_result_result.first->second.ips == nh.ips + && vnet_result_result.first->second.ifname == nh.ifname) + { + continue; + } + vnet_result_result.first->second = nh; + } + + // If the inserted route is the best route. (Table should not be empty. Because we inserted a new entry above) + if (route_insert_result.first == --next_hop_observer.second.routeTable.end()) + { + VNetNextHopUpdate update = + { + SET_COMMAND, + vnet, // vnet name + next_hop_observer.first, // destination + ipPrefix, // prefix + nh // nexthop + }; + for (auto& observer : next_hop_observer.second.observers) + { + observer->update(SUBJECT_TYPE_NEXTHOP_CHANGE, reinterpret_cast(&update)); + } + } + } + } + syncd_routes_.emplace(ipPrefix, VNetEntry()).first->second[vnet] = nh; +} + +void VNetRouteOrch::delRoute(const IpPrefix& ipPrefix) +{ + SWSS_LOG_ENTER(); + + auto route_itr = syncd_routes_.find(ipPrefix); + if (route_itr == syncd_routes_.end()) + { + SWSS_LOG_ERROR("Failed to find route %s.", ipPrefix.to_string().c_str()); + assert(false); + return; + } + auto next_hop_observer = next_hop_observers_.begin(); + while(next_hop_observer != next_hop_observers_.end()) + { + if (ipPrefix.isAddressInSubnet(next_hop_observer->first)) + { + auto itr = next_hop_observer->second.routeTable.find(ipPrefix); + if ( itr == next_hop_observer->second.routeTable.end()) + { + SWSS_LOG_ERROR( + "Failed to find any ip(%s) belong to this route(%s).", + next_hop_observer->first.to_string().c_str(), + ipPrefix.to_string().c_str()); + assert(false); + continue; + } + if (itr->second.empty()) + { + continue; + } + for (auto& observer : next_hop_observer->second.observers) + { + VNetNextHopUpdate update = { + DEL_COMMAND, + itr->second.rbegin()->first, // vnet name + next_hop_observer->first, // destination + itr->first, // prefix + itr->second.rbegin()->second // nexthop + }; + observer->update(SUBJECT_TYPE_NEXTHOP_CHANGE, reinterpret_cast(&update)); + } + next_hop_observer->second.routeTable.erase(itr); + if (next_hop_observer->second.routeTable.empty()) + { + next_hop_observer = next_hop_observers_.erase(next_hop_observer); + continue; + } + } + next_hop_observer++; + } + syncd_routes_.erase(route_itr); +} + bool VNetRouteOrch::handleTunnel(const Request& request) { SWSS_LOG_ENTER(); diff --git a/orchagent/vnetorch.h b/orchagent/vnetorch.h index 8999d8cbe0ec..126a737d3c78 100644 --- a/orchagent/vnetorch.h +++ b/orchagent/vnetorch.h @@ -6,10 +6,12 @@ #include #include #include +#include #include "request_parser.h" #include "ipaddresses.h" #include "producerstatetable.h" +#include "observer.h" #define VNET_BITMAP_SIZE 32 #define VNET_TUNNEL_SIZE 512 @@ -368,7 +370,27 @@ class VNetRouteRequest : public Request VNetRouteRequest() : Request(vnet_route_description, ':') { } }; -class VNetRouteOrch : public Orch2 +struct VNetNextHopUpdate +{ + std::string op; + std::string vnet; + IpAddress destination; + IpPrefix prefix; + nextHop nexthop; +}; +/* VNetEntry: vnet name, next hop IP address(es) */ +typedef std::map VNetEntry; +/* VNetRouteTable: destination network, vnet name, next hop IP address(es) */ +typedef std::map VNetRouteTable; +struct VNetNextHopObserverEntry +{ + VNetRouteTable routeTable; + list observers; +}; +/* NextHopObserverTable: Destination IP address, next hop observer entry */ +typedef std::map VNetNextHopObserverTable; + +class VNetRouteOrch : public Orch2, public Subject { public: VNetRouteOrch(DBConnector *db, vector &tableNames, VNetOrch *); @@ -376,10 +398,16 @@ class VNetRouteOrch : public Orch2 typedef pair handler_pair; typedef map handler_map; + void attach(Observer* observer, const IpAddress& dstAddr); + void detach(Observer* observer, const IpAddress& dstAddr); + private: virtual bool addOperation(const Request& request); virtual bool delOperation(const Request& request); + void addRoute(const std::string & vnet, const IpPrefix & ipPrefix, const nextHop& nh); + void delRoute(const IpPrefix& ipPrefix); + bool handleRoutes(const Request&); bool handleTunnel(const Request&); @@ -392,6 +420,9 @@ class VNetRouteOrch : public Orch2 VNetOrch *vnet_orch_; VNetRouteRequest request_; handler_map handler_map_; + + VNetRouteTable syncd_routes_; + VNetNextHopObserverTable next_hop_observers_; }; class VNetCfgRouteOrch : public Orch