diff --git a/cfgmgr/stpmgr.cpp b/cfgmgr/stpmgr.cpp index d3b75e53b1..f0cb631644 100644 --- a/cfgmgr/stpmgr.cpp +++ b/cfgmgr/stpmgr.cpp @@ -3,6 +3,9 @@ #include "logger.h" #include "tokenize.h" #include "warm_restart.h" +#include +#include +#include #include #include @@ -28,18 +31,21 @@ StpMgr::StpMgr(DBConnector *confDb, DBConnector *applDb, DBConnector *statDb, m_stateVlanTable(statDb, STATE_VLAN_TABLE_NAME), m_stateLagTable(statDb, STATE_LAG_TABLE_NAME), m_stateStpTable(statDb, STATE_STP_TABLE_NAME), - m_stateVlanMemberTable(statDb, STATE_VLAN_MEMBER_TABLE_NAME) + m_stateVlanMemberTable(statDb, STATE_VLAN_MEMBER_TABLE_NAME), + m_cfgMstGlobalTable(confDb, "STP_MST"), + m_cfgMstInstTable(confDb, "STP_MST_INST"), + m_cfgMstInstPortTable(confDb, "STP_MST_PORT") { SWSS_LOG_ENTER(); l2ProtoEnabled = L2_NONE; - stpGlobalTask = stpVlanTask = stpVlanPortTask = stpPortTask = false; + stpGlobalTask = stpVlanTask = stpVlanPortTask = stpPortTask = stpMstInstTask = false; // Initialize all VLANs to Invalid instance fill_n(m_vlanInstMap, MAX_VLANS, INVALID_INSTANCE); int ret = system("ebtables -D FORWARD -d 01:00:0c:cc:cc:cd -j DROP"); - SWSS_LOG_DEBUG("ebtables ret %d", ret); + SWSS_LOG_DEBUG("ebtables ret %d", ret); } void StpMgr::doTask(Consumer &consumer) @@ -60,6 +66,14 @@ void StpMgr::doTask(Consumer &consumer) doLagMemUpdateTask(consumer); else if (table == STATE_VLAN_MEMBER_TABLE_NAME) doVlanMemUpdateTask(consumer); + else if (table == "STP_MST") + doStpMstGlobalTask(consumer); + else if (table == "STP_MST_INST") + doStpMstInstTask(consumer); + else if (table == "STP_MST_PORT") + doStpMstInstPortTask(consumer); + else if (table == CFG_STP_PORT_TABLE_NAME) + doStpPortTask(consumer); else SWSS_LOG_ERROR("Invalid table %s", table.c_str()); } @@ -95,19 +109,32 @@ void StpMgr::doStpGlobalTask(Consumer &consumer) { if (l2ProtoEnabled == L2_NONE) { - const std::string cmd = std::string("") + + const std::string cmd = std::string("") + " ebtables -A FORWARD -d 01:00:0c:cc:cc:cd -j DROP"; std::string res; int ret = swss::exec(cmd, res); if (ret != 0) - SWSS_LOG_ERROR("ebtables add failed %d", ret); + SWSS_LOG_ERROR("ebtables add failed for PVST %d", ret); l2ProtoEnabled = L2_PVSTP; } msg.stp_mode = L2_PVSTP; } + else if (fvValue(i) == "mst") + { + if (l2ProtoEnabled == L2_NONE) + { + l2ProtoEnabled = L2_MSTP; + } + msg.stp_mode = L2_MSTP; + + // Assign all VLANs to zero instance for MSTP + fill_n(m_vlanInstMap, MAX_VLANS, 0); + } else - SWSS_LOG_ERROR("Error invalid mode %s", fvValue(i).c_str()); + { + SWSS_LOG_ERROR("Error: Invalid mode %s", fvValue(i).c_str()); + } } else if (fvField(i) == "rootguard_timeout") { @@ -120,28 +147,35 @@ void StpMgr::doStpGlobalTask(Consumer &consumer) else if (op == DEL_COMMAND) { msg.opcode = STP_DEL_COMMAND; - l2ProtoEnabled = L2_NONE; - //Free Up all instances + // Free Up all instances FREE_ALL_INST_ID(); - + // Initialize all VLANs to Invalid instance fill_n(m_vlanInstMap, MAX_VLANS, INVALID_INSTANCE); - const std::string cmd = std::string("") + - " ebtables -D FORWARD -d 01:00:0c:cc:cc:cd -j DROP"; - std::string res; - int ret = swss::exec(cmd, res); - if (ret != 0) - SWSS_LOG_ERROR("ebtables del failed %d", ret); + // Remove ebtables rule based on protocol mode + if (l2ProtoEnabled == L2_PVSTP) + { + const std::string pvst_cmd = + "ebtables -D FORWARD -d 01:00:0c:cc:cc:cd -j DROP"; + std::string res_pvst; + int ret_pvst = swss::exec(pvst_cmd, res_pvst); + if (ret_pvst != 0) + SWSS_LOG_ERROR("ebtables del failed for PVST %d", ret_pvst); + } + l2ProtoEnabled = L2_NONE; } + // Send the message to the daemon sendMsgStpd(STP_BRIDGE_CONFIG, sizeof(msg), (void *)&msg); + // Move to the next item it = consumer.m_toSync.erase(it); } } + void StpMgr::doStpVlanTask(Consumer &consumer) { SWSS_LOG_ENTER(); @@ -165,8 +199,8 @@ void StpMgr::doStpVlanTask(Consumer &consumer) KeyOpFieldsValuesTuple t = it->second; string key = kfvKey(t); - string op = kfvOp(t); - + string op = kfvOp(t); + string vlanKey = key.substr(4); // Remove Vlan prefix int vlan_id = stoi(vlanKey.c_str()); @@ -216,7 +250,7 @@ void StpMgr::doStpVlanTask(Consumer &consumer) } } - len = sizeof(STP_VLAN_CONFIG_MSG); + len = sizeof(STP_VLAN_CONFIG_MSG); if (stpEnable == true) { vector port_list; @@ -237,17 +271,17 @@ void StpMgr::doStpVlanTask(Consumer &consumer) portCnt = getAllVlanMem(key, port_list); SWSS_LOG_DEBUG("Port count %d", portCnt); } - + len += (uint32_t)(portCnt * sizeof(PORT_ATTR)); } - msg = (STP_VLAN_CONFIG_MSG *)calloc(1, len); + msg = (STP_VLAN_CONFIG_MSG *)calloc(1, len); if (!msg) { SWSS_LOG_ERROR("mem failed for vlan %d", vlan_id); return; } - + msg->opcode = STP_SET_COMMAND; msg->vlan_id = vlan_id; msg->newInstance = newInstance; @@ -276,13 +310,13 @@ void StpMgr::doStpVlanTask(Consumer &consumer) { if (m_vlanInstMap[vlan_id] == INVALID_INSTANCE) { - // Already deallocated. NoOp. This can happen when STP + // Already deallocated. NoOp. This can happen when STP // is disabled on a VLAN more than once it = consumer.m_toSync.erase(it); continue; } - msg = (STP_VLAN_CONFIG_MSG *)calloc(1, len); + msg = (STP_VLAN_CONFIG_MSG *)calloc(1, len); if (!msg) { SWSS_LOG_ERROR("mem failed for vlan %d", vlan_id); @@ -294,7 +328,7 @@ void StpMgr::doStpVlanTask(Consumer &consumer) deallocL2Instance(vlan_id); } - + sendMsgStpd(STP_VLAN_CONFIG, len, (void *)msg); if (msg) free(msg); @@ -303,6 +337,74 @@ void StpMgr::doStpVlanTask(Consumer &consumer) } } +void StpMgr::doStpMstGlobalTask(Consumer &consumer) +{ + SWSS_LOG_ENTER(); + + if (stpGlobalTask == false) + return; + + auto it = consumer.m_toSync.begin(); + while (it != consumer.m_toSync.end()) + { + KeyOpFieldsValuesTuple t = it->second; + string key = kfvKey(t); + string op = kfvOp(t); + + SWSS_LOG_INFO("STP MST global key %s op %s", key.c_str(), op.c_str()); + + STP_MST_GLOBAL_CONFIG_MSG msg; + memset(&msg, 0, sizeof(msg)); // Initialize message structure to zero + + if (op == SET_COMMAND) + { + msg.opcode = STP_SET_COMMAND; + + for (auto i : kfvFieldsValues(t)) + { + SWSS_LOG_DEBUG("Field: %s Val: %s", fvField(i).c_str(), fvValue(i).c_str()); + + if (fvField(i) == "name") + { + strncpy(msg.name, fvValue(i).c_str(), sizeof(msg.name) - 1); + } + else if (fvField(i) == "revision") + { + msg.revision_number = static_cast(stoi(fvValue(i))); + } + else if (fvField(i) == "forward_delay") + { + msg.forward_delay = static_cast(stoi(fvValue(i))); + } + else if (fvField(i) == "hello_time") + { + msg.hello_time = static_cast(stoi(fvValue(i))); + } + else if (fvField(i) == "max_age") + { + msg.max_age = static_cast(stoi(fvValue(i))); + } + else if (fvField(i) == "max_hops") + { + msg.max_hops = static_cast(stoi(fvValue(i))); + } + else + { + SWSS_LOG_ERROR("Invalid field: %s", fvField(i).c_str()); + } + } + } + else if (op == DEL_COMMAND) + { + msg.opcode = STP_DEL_COMMAND; + } + + sendMsgStpd(STP_MST_GLOBAL_CONFIG, sizeof(msg), (void *)&msg); + + it = consumer.m_toSync.erase(it); + } +} + void StpMgr::processStpVlanPortAttr(const string op, uint32_t vlan_id, const string intfName, vector&tupEntry) { @@ -435,12 +537,14 @@ void StpMgr::processStpPortAttr(const string op, SWSS_LOG_ERROR("calloc failed for interface %s", intfName.c_str()); return; } - // Copy interface name and VLAN count into the message. strncpy(msg->intf_name, intfName.c_str(), IFNAMSIZ - 1); msg->count = vlanCnt; SWSS_LOG_INFO("VLAN count for %s is %d", intfName.c_str(), vlanCnt); + SWSS_LOG_INFO("VLAN count for %s is %d", intfName.c_str(), vlanCnt); + // If there are VLANs, copy them into the message structure. + if (msg->count > 0) // If there are VLANs, copy them into the message structure. if (msg->count > 0) { @@ -492,14 +596,22 @@ void StpMgr::processStpPortAttr(const string op, { msg->priority = stoi(value); } - else if (field == "portfast") + else if (field == "portfast" && l2ProtoEnabled == L2_PVSTP) { msg->portfast = (value == "true") ? 1 : 0; } - else if (field == "uplink_fast") + else if (field == "uplink_fast" && l2ProtoEnabled ==L2_PVSTP) { msg->uplink_fast = (value == "true") ? 1 : 0; } + else if (field == "edge_port" && l2ProtoEnabled ==L2_MSTP) + { + msg->edge_port = (value == "true") ? 1 : 0; + } + else if (field== "link_type" && l2ProtoEnabled == L2_MSTP) + { + msg->link_type = static_cast(stoi(field.c_str())); + } } } else if (op == DEL_COMMAND) @@ -595,7 +707,7 @@ void StpMgr::doVlanMemUpdateTask(Consumer &consumer) it = consumer.m_toSync.erase(it); continue; } - + SWSS_LOG_INFO("STP vlan mem key:%s op:%s inst:%d", key.c_str(), op.c_str(), m_vlanInstMap[vlan_id]); // If STP is running on this VLAN, notify STPd if (m_vlanInstMap[vlan_id] != INVALID_INSTANCE && !isLagEmpty(intfName)) @@ -685,7 +797,7 @@ void StpMgr::doLagMemUpdateTask(Consumer &consumer) auto elm = m_lagMap.find(po_name); if (elm == m_lagMap.end()) { - // First Member added to the LAG + // First Member added to the LAG m_lagMap[po_name] = 1; notifyStpd = true; } @@ -703,7 +815,7 @@ void StpMgr::doLagMemUpdateTask(Consumer &consumer) if (elm->second == 0) { - // Last Member deleted from the LAG + // Last Member deleted from the LAG m_lagMap.erase(po_name); //notifyStpd = true; } @@ -721,7 +833,7 @@ void StpMgr::doLagMemUpdateTask(Consumer &consumer) { //Push STP_PORT configs for this port processStpPortAttr(op, tupEntry, po_name); - + getAllPortVlan(po_name, vlan_list); //Push STP_VLAN_PORT configs for this port for (auto p = vlan_list.begin(); p != vlan_list.end(); p++) @@ -736,9 +848,9 @@ void StpMgr::doLagMemUpdateTask(Consumer &consumer) } SWSS_LOG_DEBUG("LagMap"); - for (auto itr = m_lagMap.begin(); itr != m_lagMap.end(); ++itr) { + for (auto itr = m_lagMap.begin(); itr != m_lagMap.end(); ++itr) { SWSS_LOG_DEBUG("PO: %s Cnt:%d", itr->first.c_str(), itr->second); - } + } it = consumer.m_toSync.erase(it); } @@ -851,7 +963,6 @@ int StpMgr::getAllVlanMem(const string &vlanKey, vector&port_list) SWSS_LOG_ERROR("invalid mode %s", key.c_str()); continue; } - port_id.enabled = isStpEnabled(intfName); strncpy(port_id.intf_name, intfName.c_str(), IFNAMSIZ-1); port_list.push_back(port_id); @@ -862,6 +973,8 @@ int StpMgr::getAllVlanMem(const string &vlanKey, vector&port_list) return (int)port_list.size(); } + + int StpMgr::getAllPortVlan(const string &intfKey, vector&vlan_list) { VLAN_ATTR vlan; @@ -894,7 +1007,7 @@ int StpMgr::getAllPortVlan(const string &intfKey, vector&vlan_list) SWSS_LOG_ERROR("invalid mode %s", key.c_str()); continue; } - + vlan.vlan_id = vlan_id; vlan.inst_id = m_vlanInstMap[vlan_id]; vlan_list.push_back(vlan); @@ -907,6 +1020,200 @@ int StpMgr::getAllPortVlan(const string &intfKey, vector&vlan_list) return (int)vlan_list.size(); } +void StpMgr::doStpMstInstTask(Consumer &consumer) +{ + SWSS_LOG_ENTER(); + + if (stpGlobalTask == false || (stpPortTask == false && !isStpPortEmpty())) + return; + + if (stpMstInstTask == false) + stpMstInstTask = true; + + auto it = consumer.m_toSync.begin(); + while (it != consumer.m_toSync.end()) + { + STP_MST_INST_CONFIG_MSG *msg = NULL; + uint32_t len = 0; + + KeyOpFieldsValuesTuple t = it->second; + + string key = kfvKey(t); + string op = kfvOp(t); + + string instance = key.substr(13); // Remove "MST_INSTANCE|" prefix + uint16_t instance_id = static_cast(stoi(instance.c_str())); + + uint16_t priority = 32768; // Default bridge priority + string vlan_list_str; + vector vlan_ids; + + SWSS_LOG_INFO("STP_MST instance key %s op %s", key.c_str(), op.c_str()); + if (op == SET_COMMAND) + { + for (auto i : kfvFieldsValues(t)) + { + SWSS_LOG_DEBUG("Field: %s Val: %s", fvField(i).c_str(), fvValue(i).c_str()); + + if (fvField(i) == "bridge_priority") + { + priority = static_cast(stoi((fvValue(i).c_str()))); + } + else if (fvField(i) == "vlan_list") + { + vlan_list_str = fvValue(i); + vlan_ids = parseVlanList(vlan_list_str); + } + updateVlanInstanceMap(instance_id, vlan_ids, true); + } + + uint32_t vlan_count = static_cast(vlan_ids.size()); + len = sizeof(STP_MST_INST_CONFIG_MSG) + static_cast(vlan_count * sizeof(VLAN_LIST)); + + msg = (STP_MST_INST_CONFIG_MSG *)calloc(1, len); + if (!msg) + { + SWSS_LOG_ERROR("Memory allocation failed for STP_MST_INST_CONFIG_MSG"); + return; + } + + msg->opcode = STP_SET_COMMAND; + msg->mst_id = instance_id; + msg->priority = priority; + msg->vlan_count = static_cast(vlan_ids.size()); + #pragma GCC diagnostic push + #pragma GCC diagnostic ignored "-Waddress-of-packed-member" + VLAN_LIST *vlan_attr = (VLAN_LIST *)&msg->vlan_list; + #pragma GCC diagnostic pop + for (size_t i = 0; i < vlan_ids.size(); i++) + { + vlan_attr[i].vlan_id = vlan_ids[i]; + } + } + else if (op == DEL_COMMAND) + { + len = sizeof(STP_MST_INST_CONFIG_MSG); + msg = (STP_MST_INST_CONFIG_MSG *)calloc(1, len); + if (!msg) + { + SWSS_LOG_ERROR("Memory allocation failed for MST_INST_CONFIG_MSG"); + return; + } + + msg->opcode = STP_DEL_COMMAND; + msg->mst_id = instance_id; + updateVlanInstanceMap(instance_id, vlan_ids, false); + } + + sendMsgStpd(STP_MST_INST_CONFIG, len, (void *)msg); + if (msg) + free(msg); + + it = consumer.m_toSync.erase(it); + } +} + +void StpMgr::processStpMstInstPortAttr(const string op, uint16_t mst_id, const string intfName, + vector& tupEntry) +{ + STP_MST_INST_PORT_CONFIG_MSG msg; + memset(&msg, 0, sizeof(STP_MST_INST_PORT_CONFIG_MSG)); + + // Populate the message fields + msg.mst_id = mst_id; + strncpy(msg.intf_name, intfName.c_str(), IFNAMSIZ - 1); + + // Set opcode and process the fields from the tuple + if (op == SET_COMMAND) + { + msg.opcode = STP_SET_COMMAND; + msg.priority = -1; + + for (auto i : tupEntry) + { + SWSS_LOG_DEBUG("Field: %s Val: %s", fvField(i).c_str(), fvValue(i).c_str()); + + if (fvField(i) == "path_cost") + { + msg.path_cost = stoi(fvValue(i).c_str()); + } + else if (fvField(i) == "priority") + { + msg.priority = stoi(fvValue(i).c_str()); + } + } + } + else if (op == DEL_COMMAND) + { + msg.opcode = STP_DEL_COMMAND; + } + + // Send the message to the daemon + sendMsgStpd(STP_MST_INST_PORT_CONFIG, sizeof(msg), (void *)&msg); +} + + +void StpMgr::doStpMstInstPortTask(Consumer &consumer) +{ + SWSS_LOG_ENTER(); + + if (stpGlobalTask == false || stpMstInstTask == false || stpPortTask == false) + return; + + auto it = consumer.m_toSync.begin(); + while (it != consumer.m_toSync.end()) + { + STP_MST_INST_PORT_CONFIG_MSG msg; + memset(&msg, 0, sizeof(STP_MST_INST_PORT_CONFIG_MSG)); + + KeyOpFieldsValuesTuple t = it->second; + + string key = kfvKey(t); + string op = kfvOp(t); + + string mstKey = key.substr(9);//Remove INSTANCE keyword + size_t found = mstKey.find(CONFIGDB_KEY_SEPARATOR); + + uint16_t mst_id; + string intfName; + if (found != string::npos) + { + mst_id = static_cast(stoi(mstKey.substr(0, found))); + intfName = mstKey.substr(found + 1); + } + else + { + SWSS_LOG_ERROR("Invalid key format %s", kfvKey(t).c_str()); + it = consumer.m_toSync.erase(it); + continue; + } + + SWSS_LOG_INFO("STP MST intf key:%s op:%s", key.c_str(), op.c_str()); + + if (op == SET_COMMAND) + { + if ((l2ProtoEnabled == L2_NONE)) + { + // Wait till STP/MST instance is configured + it++; + continue; + } + } + else + { + if (l2ProtoEnabled == L2_NONE || !(isInstanceMapped(mst_id))) + { + it = consumer.m_toSync.erase(it); + continue; + } + } + + processStpMstInstPortAttr(op, mst_id, intfName, kfvFieldsValues(t)); + + it = consumer.m_toSync.erase(it); + } +} + // Send Message to STPd int StpMgr::sendMsgStpd(STP_MSG_TYPE msgType, uint32_t msgLen, void *data) { @@ -937,7 +1244,7 @@ int StpMgr::sendMsgStpd(STP_MSG_TYPE msgType, uint32_t msgLen, void *data) if (rc == -1) { SWSS_LOG_ERROR("tx_msg send error\n"); - } + } else { SWSS_LOG_INFO("tx_msg sent %d", rc); @@ -998,7 +1305,7 @@ bool StpMgr::isLagStateOk(const string &alias) bool StpMgr::isLagEmpty(const string &key) { - size_t po_find = key.find("PortChannel"); + size_t po_find = key.find("PortChannel"); if (po_find != string::npos) { // If Lag, check if members present @@ -1046,7 +1353,7 @@ bool StpMgr::isStpEnabled(const string &intf_name) } } } - + SWSS_LOG_NOTICE("STP NOT enabled on %s", intf_name.c_str()); return false; } @@ -1105,3 +1412,83 @@ uint16_t StpMgr::getStpMaxInstances(void) return max_stp_instances; } + +std::vector StpMgr::getVlanAliasesForInstance(uint16_t instance) { + std::vector vlanAliases; + + for (uint16_t vlanId = 0; vlanId < MAX_VLANS; ++vlanId) { + if (m_vlanInstMap[vlanId] == instance) { + vlanAliases.push_back("VLAN" + std::to_string(vlanId)); + } + } + + return vlanAliases; +} + +//Function to parse the VLAN list and handle ranges +std::vector StpMgr::parseVlanList(const std::string &vlanStr) { + std::vector vlanList; + std::stringstream ss(vlanStr); + std::string segment; + + // Split the string by commas + while (std::getline(ss, segment, ',')) { + size_t dashPos = segment.find('-'); + if (dashPos != std::string::npos) { + // If a dash is found, it's a range like "22-25" + int start = std::stoi(segment.substr(0, dashPos)); + int end = std::stoi(segment.substr(dashPos + 1)); + + // Add all VLANs in the range to the list + for (int i = start; i <= end; ++i) { + vlanList.push_back(static_cast(i)); + } + } else { + // Single VLAN, add it to the list + vlanList.push_back(static_cast(std::stoi(segment))); + } + } + return vlanList; +} + +void StpMgr::updateVlanInstanceMap(int instance, const std::vector& newVlanList, bool operation) { + if (!operation) { + // Delete instance: Reset all VLANs mapped to this instance + for (int vlan = 0; vlan < MAX_VLANS; ++vlan) { + if (m_vlanInstMap[vlan] == instance) { + m_vlanInstMap[vlan] = 0; // Reset to default instance + } + } + } + else { + // Add/Update instance: Handle additions and deletions + // Use an unordered_set for efficient lookup of new VLAN list + std::unordered_set newVlanSet(newVlanList.begin(), newVlanList.end()); + + // Iterate over the current mapping to handle deletions + for (int vlan = 0; vlan < MAX_VLANS; ++vlan) { + if (m_vlanInstMap[vlan] == instance) { + // If a VLAN is mapped to this instance but not in the new list, reset it to 0 + if (newVlanSet.find(vlan) == newVlanSet.end()) { + m_vlanInstMap[vlan] = 0; + } + } + } + + // Handle additions + for (int vlan : newVlanList) { + if (vlan >= 0 && vlan < MAX_VLANS) { + m_vlanInstMap[vlan] = instance; + } + } + } +} + +bool StpMgr::isInstanceMapped(uint16_t instance) { + for (int i = 0; i < MAX_VLANS; ++i) { + if (m_vlanInstMap[i] == static_cast(instance)) { + return true; // Instance found + } + } + return false; // Instance not, found +} \ No newline at end of file diff --git a/cfgmgr/stpmgr.h b/cfgmgr/stpmgr.h index b832091170..263bac46fc 100644 --- a/cfgmgr/stpmgr.h +++ b/cfgmgr/stpmgr.h @@ -7,6 +7,9 @@ #include #include #include +#include +#include +#include #include "dbconnector.h" #include "netmsg.h" @@ -31,9 +34,9 @@ #define MAX_VLANS 4096 // Maximum number of instances supported -#define L2_INSTANCE_MAX MAX_VLANS -#define STP_DEFAULT_MAX_INSTANCES 255 -#define INVALID_INSTANCE -1 +#define L2_INSTANCE_MAX MAX_VLANS +#define STP_DEFAULT_MAX_INSTANCES 255 +#define INVALID_INSTANCE -1 #define GET_FIRST_FREE_INST_ID(_idx) \ while (_idx < (int)l2InstPool.size() && l2InstPool.test(_idx)) ++_idx; \ @@ -49,7 +52,15 @@ typedef enum L2_PROTO_MODE { L2_NONE, L2_PVSTP, -} L2_PROTO_MODE; + L2_MSTP +}L2_PROTO_MODE; + + +typedef enum LinkType { + AUTO = 0, // Auto + POINT_TO_POINT = 1, // Point-to-point + SHARED = 2 // Shared +} LinkType; typedef enum STP_MSG_TYPE { STP_INVALID_MSG, @@ -60,6 +71,9 @@ typedef enum STP_MSG_TYPE { STP_PORT_CONFIG, STP_VLAN_MEM_CONFIG, STP_STPCTL_MSG, + STP_MST_GLOBAL_CONFIG, + STP_MST_INST_CONFIG, + STP_MST_INST_PORT_CONFIG, STP_MAX_MSG } STP_MSG_TYPE; @@ -151,22 +165,26 @@ typedef struct VLAN_ATTR { uint8_t padding[3]; } ALIGNED(4) VLAN_ATTR; +typedef struct VLAN_LIST{ + uint16_t vlan_id; +}VLAN_LIST; + typedef struct STP_PORT_CONFIG_MSG { - uint8_t opcode; // enable/disable - char intf_name[IFNAMSIZ]; - uint8_t enabled; - uint8_t root_guard; - uint8_t bpdu_guard; - uint8_t bpdu_guard_do_disable; - uint8_t portfast; - uint8_t uplink_fast; - // Add 2 bytes padding for alignment - uint16_t padding; - int path_cost; - int priority; - int count; - VLAN_ATTR vlan_list[0]; -} ALIGNED(4) STP_PORT_CONFIG_MSG; + uint8_t opcode; // enable/disable + char intf_name[IFNAMSIZ]; + uint8_t enabled; + uint8_t root_guard; + uint8_t bpdu_guard; + uint8_t bpdu_guard_do_disable; + uint8_t portfast; // PVST only + uint8_t uplink_fast; // PVST only + uint8_t edge_port; // MSTP only + LinkType link_type; // MSTP only + int path_cost; + int priority; + int count; + VLAN_ATTR vlan_list[0]; +} STP_PORT_CONFIG_MSG;; typedef struct STP_VLAN_MEM_CONFIG_MSG { uint8_t opcode; // enable/disable @@ -181,6 +199,32 @@ typedef struct STP_VLAN_MEM_CONFIG_MSG { int priority; } ALIGNED(4) STP_VLAN_MEM_CONFIG_MSG; +typedef struct STP_MST_GLOBAL_CONFIG_MSG { + uint8_t opcode; // enable/disable + uint32_t revision_number; + char name[32]; + uint8_t forward_delay; + uint8_t hello_time; + uint8_t max_age; + uint8_t max_hops; +}__attribute__ ((packed))STP_MST_GLOBAL_CONFIG_MSG; + +typedef struct STP_MST_INST_CONFIG_MSG { + uint8_t opcode; // enable/disable + uint16_t mst_id; // MST instance ID + int priority; // Bridge priority + uint16_t vlan_count; // Number of VLANs in this instance + VLAN_LIST vlan_list[0]; // Flexible array for VLAN IDs +}__attribute__((packed)) STP_MST_INST_CONFIG_MSG; + +typedef struct STP_MST_INST_PORT_CONFIG_MSG { + uint8_t opcode; // enable/disable + char intf_name[IFNAMSIZ]; // Interface name + uint16_t mst_id; // MST instance ID + int path_cost; // Path cost + int priority; // Port priority +} __attribute__((packed)) STP_MST_INST_PORT_CONFIG_MSG; + namespace swss { class StpMgr : public Orch @@ -194,7 +238,7 @@ class StpMgr : public Orch int sendMsgStpd(STP_MSG_TYPE msgType, uint32_t msgLen, void *data); MacAddress macAddress; bool isPortInitDone(DBConnector *app_db); - uint16_t getStpMaxInstances(void); + uint16_t getStpMaxInstances(void); private: Table m_cfgStpGlobalTable; @@ -207,6 +251,9 @@ class StpMgr : public Orch Table m_stateVlanMemberTable; Table m_stateLagTable; Table m_stateStpTable; + Table m_cfgMstGlobalTable; + Table m_cfgMstInstTable; + Table m_cfgMstInstPortTable; std::bitset l2InstPool; int stpd_fd; @@ -220,6 +267,7 @@ class StpMgr : public Orch bool stpVlanTask; bool stpVlanPortTask; bool stpPortTask; + bool stpMstInstTask; void doTask(Consumer &consumer); void doStpGlobalTask(Consumer &consumer); @@ -228,6 +276,9 @@ class StpMgr : public Orch void doStpPortTask(Consumer &consumer); void doVlanMemUpdateTask(Consumer &consumer); void doLagMemUpdateTask(Consumer &consumer); + void doStpMstGlobalTask(Consumer &consumer); + void doStpMstInstTask(Consumer &consumer); + void doStpMstInstPortTask(Consumer &consumer); bool isVlanStateOk(const std::string &alias); bool isLagStateOk(const std::string &alias); @@ -239,17 +290,19 @@ class StpMgr : public Orch int allocL2Instance(uint32_t vlan_id); void deallocL2Instance(uint32_t vlan_id); bool isLagEmpty(const std::string &key); - - // The rest of the methods remain unchanged except we replaced the old approach with alignment-based approach - void processStpPortAttr(const std::string op, - std::vector &tupEntry, - const std::string intfName); - void processStpVlanPortAttr(const std::string op, - uint32_t vlan_id, - const std::string intfName, - std::vector &tupEntry); + void processStpPortAttr(const std::string op, std::vector&tupEntry, const std::string intfName); + void processStpVlanPortAttr(const std::string op, uint32_t vlan_id, const std::string intfName, + std::vector&tupEntry); + void processStpMstInstPortAttr(const std::string op, uint16_t mst_id, const std::string intfName, + std::vector&tupEntry); + std::vector parseVlanList(const std::string &vlanStr); + void updateVlanInstanceMap(int instance, const std::vector&newVlanList, bool operation); + bool isInstanceMapped(uint16_t instance); + std::vector getVlanAliasesForInstance(uint16_t instance); + + }; -} // namespace swss - +} #endif + diff --git a/cfgmgr/stpmgrd.cpp b/cfgmgr/stpmgrd.cpp index bd20b2a3fc..52b9f5a9dd 100644 --- a/cfgmgr/stpmgrd.cpp +++ b/cfgmgr/stpmgrd.cpp @@ -42,20 +42,24 @@ int main(int argc, char **argv) TableConnector conf_stp_vlan_table(&conf_db, CFG_STP_VLAN_TABLE_NAME); TableConnector conf_stp_vlan_port_table(&conf_db, CFG_STP_VLAN_PORT_TABLE_NAME); TableConnector conf_stp_port_table(&conf_db, CFG_STP_PORT_TABLE_NAME); - + TableConnector conf_mst_global_table(&conf_db, "STP_MST"); + TableConnector conf_mst_inst_table(&conf_db, "STP_MST_INST"); + TableConnector conf_mst_inst_port_table(&conf_db, "STP_MST_PORT"); // VLAN DB Tables TableConnector state_vlan_member_table(&state_db, STATE_VLAN_MEMBER_TABLE_NAME); // LAG Tables TableConnector conf_lag_member_table(&conf_db, CFG_LAG_MEMBER_TABLE_NAME); - vector tables = { conf_stp_global_table, conf_stp_vlan_table, conf_stp_vlan_port_table, conf_stp_port_table, conf_lag_member_table, - state_vlan_member_table + state_vlan_member_table, + conf_mst_global_table, + conf_mst_inst_table, + conf_mst_inst_port_table }; @@ -64,7 +68,7 @@ int main(int argc, char **argv) // Open a Unix Domain Socket with STPd for communication stpmgr.ipcInitStpd(); stpmgr.isPortInitDone(&app_db); - + // Get max STP instances from state DB and send to stpd STP_INIT_READY_MSG msg; memset(&msg, 0, sizeof(STP_INIT_READY_MSG)); @@ -116,4 +120,4 @@ int main(int argc, char **argv) } return -1; -} +} \ No newline at end of file diff --git a/orchagent/orchdaemon.cpp b/orchagent/orchdaemon.cpp index bde73dcbb3..df6771668b 100644 --- a/orchagent/orchdaemon.cpp +++ b/orchagent/orchdaemon.cpp @@ -231,7 +231,8 @@ bool OrchDaemon::init() vector stp_tables = { APP_STP_VLAN_INSTANCE_TABLE_NAME, APP_STP_PORT_STATE_TABLE_NAME, - APP_STP_FASTAGEING_FLUSH_TABLE_NAME + APP_STP_FASTAGEING_FLUSH_TABLE_NAME, + APP_STP_INST_PORT_FLUSH_TABLE_NAME }; gStpOrch = new StpOrch(m_applDb, m_stateDb, stp_tables); gDirectory.set(gStpOrch); @@ -1316,4 +1317,4 @@ bool DpuOrchDaemon::init() addOrchList(dash_port_map_orch); return true; -} +} \ No newline at end of file diff --git a/orchagent/p4orch/tests/mock_sai_stp.h b/orchagent/p4orch/tests/mock_sai_stp.h index 5ac6397c9c..c0e60c59f0 100644 --- a/orchagent/p4orch/tests/mock_sai_stp.h +++ b/orchagent/p4orch/tests/mock_sai_stp.h @@ -6,15 +6,15 @@ extern "C" { #include "sai.h" } - + // Mock class for SAI STP APIs class MockSaiStp { public: // Mock method for creating an STP instance MOCK_METHOD4(create_stp, - sai_status_t(_Out_ sai_object_id_t *stp_instance_id, - _In_ sai_object_id_t switch_id, - _In_ uint32_t attr_count, + sai_status_t(_Out_ sai_object_id_t *stp_instance_id, + _In_ sai_object_id_t switch_id, + _In_ uint32_t attr_count, _In_ const sai_attribute_t *attr_list)); // Mock method for removing an STP instance @@ -22,20 +22,20 @@ class MockSaiStp { // Mock method for setting STP instance attributes MOCK_METHOD2(set_stp_attribute, - sai_status_t(_In_ sai_object_id_t stp_instance_id, + sai_status_t(_In_ sai_object_id_t stp_instance_id, _In_ const sai_attribute_t *attr)); // Mock method for getting STP instance attributes MOCK_METHOD3(get_stp_attribute, - sai_status_t(_Out_ sai_object_id_t stp_instance_id, - _In_ uint32_t attr_count, + sai_status_t(_Out_ sai_object_id_t stp_instance_id, + _In_ uint32_t attr_count, _In_ sai_attribute_t *attr_list)); // Mock method for creating an STP port MOCK_METHOD4(create_stp_port, - sai_status_t(_Out_ sai_object_id_t *stp_port_id, - _In_ sai_object_id_t switch_id, - _In_ uint32_t attr_count, + sai_status_t(_Out_ sai_object_id_t *stp_port_id, + _In_ sai_object_id_t switch_id, + _In_ uint32_t attr_count, _In_ const sai_attribute_t *attr_list)); // Mock method for removing an STP port @@ -44,22 +44,28 @@ class MockSaiStp { // Mock method for setting STP port attributes MOCK_METHOD2(set_stp_port_attribute, - sai_status_t(_Out_ sai_object_id_t stp_port_id, + sai_status_t(_Out_ sai_object_id_t stp_port_id, _In_ const sai_attribute_t *attr)); // Mock method for getting STP port attributes MOCK_METHOD3(get_stp_port_attribute, - sai_status_t(_Out_ sai_object_id_t stp_port_id, + sai_status_t(_Out_ sai_object_id_t stp_port_id, _In_ uint32_t attr_count, _In_ sai_attribute_t *attr_list)); + + // Mock method for flushing FDB entries + MOCK_METHOD3(flush_fdb_entries, + sai_status_t(_In_ sai_object_id_t switch_id, + _In_ uint32_t attr_count, + _In_ const sai_attribute_t *attr_list)); }; // Global mock object for SAI STP APIs MockSaiStp *mock_sai_stp; -sai_status_t mock_create_stp(_Out_ sai_object_id_t *stp_instance_id, - _In_ sai_object_id_t switch_id, - _In_ uint32_t attr_count, +sai_status_t mock_create_stp(_Out_ sai_object_id_t *stp_instance_id, + _In_ sai_object_id_t switch_id, + _In_ uint32_t attr_count, _In_ const sai_attribute_t *attr_list) { return mock_sai_stp->create_stp(stp_instance_id, switch_id, attr_count, attr_list); @@ -75,14 +81,14 @@ sai_status_t mock_set_stp_attribute(_In_ sai_object_id_t stp_instance_id, _In_ c return mock_sai_stp->set_stp_attribute(stp_instance_id, attr); } -sai_status_t mock_get_stp_attribute(_Out_ sai_object_id_t stp_instance_id, +sai_status_t mock_get_stp_attribute(_Out_ sai_object_id_t stp_instance_id, _In_ uint32_t attr_count, _Inout_ sai_attribute_t *attr_list) { return mock_sai_stp->get_stp_attribute(stp_instance_id, attr_count, attr_list); } -sai_status_t mock_create_stp_port(_Out_ sai_object_id_t *stp_port_id, - _In_ sai_object_id_t switch_id, - _In_ uint32_t attr_count, +sai_status_t mock_create_stp_port(_Out_ sai_object_id_t *stp_port_id, + _In_ sai_object_id_t switch_id, + _In_ uint32_t attr_count, _In_ const sai_attribute_t *attr_list) { return mock_sai_stp->create_stp_port(stp_port_id, switch_id,attr_count, attr_list); @@ -93,18 +99,17 @@ sai_status_t mock_remove_stp_port(_In_ sai_object_id_t stp_port_id) return mock_sai_stp->remove_stp_port(stp_port_id); } -sai_status_t mock_set_stp_port_attribute(_In_ sai_object_id_t stp_port_id, +sai_status_t mock_set_stp_port_attribute(_In_ sai_object_id_t stp_port_id, _In_ const sai_attribute_t *attr) { return mock_sai_stp->set_stp_port_attribute(stp_port_id, attr); } -sai_status_t mock_get_stp_port_attribute(_Out_ sai_object_id_t stp_port_id, +sai_status_t mock_get_stp_port_attribute(_Out_ sai_object_id_t stp_port_id, _In_ uint32_t attr_count, - _Inout_ sai_attribute_t *attr_list) + _Inout_ sai_attribute_t *attr_list) { return mock_sai_stp->get_stp_port_attribute(stp_port_id, attr_count, attr_list); } #endif // MOCK_SAI_STP_H - diff --git a/orchagent/stporch.cpp b/orchagent/stporch.cpp index b4b66fe920..22f348c297 100644 --- a/orchagent/stporch.cpp +++ b/orchagent/stporch.cpp @@ -11,6 +11,7 @@ extern sai_switch_api_t *sai_switch_api; extern FdbOrch *gFdbOrch; extern PortsOrch *gPortsOrch; + extern sai_object_id_t gSwitchId; StpOrch::StpOrch(DBConnector * db, DBConnector * stateDb, vector &tableNames) : @@ -23,13 +24,13 @@ StpOrch::StpOrch(DBConnector * db, DBConnector * stateDb, vector &tableN bool ret = false; m_stpTable = unique_ptr(new Table(stateDb, STATE_STP_TABLE_NAME)); - + vector attrs; attr.id = SAI_SWITCH_ATTR_DEFAULT_STP_INST_ID; attrs.push_back(attr); + attr.id = SAI_SWITCH_ATTR_MAX_STP_INSTANCE; attrs.push_back(attr); - status = sai_switch_api->get_switch_attribute(gSwitchId, (uint32_t)attrs.size(), attrs.data()); if (status == SAI_STATUS_SUCCESS) { @@ -62,14 +63,14 @@ sai_object_id_t StpOrch::addStpInstance(sai_uint16_t stp_instance) attr.id = 0; attr.value.u32 = 0; - + sai_status_t status = sai_stp_api->create_stp(&stp_oid, gSwitchId, 0, &attr); if (status != SAI_STATUS_SUCCESS) { SWSS_LOG_ERROR("Failed to create STP instance %u status %u", stp_instance, status); return SAI_NULL_OBJECT_ID; } - + m_stpInstToOid[stp_instance] = stp_oid; SWSS_LOG_INFO("Added STP instance:%hu oid:%" PRIx64 "", stp_instance, stp_oid); return stp_oid; @@ -84,7 +85,7 @@ bool StpOrch::removeStpInstance(sai_uint16_t stp_instance) { return false; } - + /* Remove all STP ports before deleting the STP instance */ auto portList = gPortsOrch->getAllPorts(); for (auto &it: portList) @@ -123,7 +124,7 @@ bool StpOrch::addVlanToStpInstance(string vlan_alias, sai_uint16_t stp_instance) { return false; } - + stp_oid = getStpInstanceOid(stp_instance); if (stp_oid == SAI_NULL_OBJECT_ID) { @@ -144,6 +145,19 @@ bool StpOrch::addVlanToStpInstance(string vlan_alias, sai_uint16_t stp_instance) vlan.m_stp_id = stp_instance; gPortsOrch->setPort(vlan_alias, vlan); + + // Update the new map structure + auto it = m_vlanAliasToStpInstanceMap.find(stp_instance); + if (it == m_vlanAliasToStpInstanceMap.end()) + { + StpInstEntry entry = {stp_oid, {vlan_alias}}; + m_vlanAliasToStpInstanceMap[stp_instance] = entry; + } + else + { + it->second.stp_inst_vlan_list.insert(vlan_alias); + } + SWSS_LOG_INFO("Add VLAN %s to STP instance:%hu m_stp_id:%d", vlan_alias.c_str(), stp_instance, vlan.m_stp_id); return true; } @@ -166,12 +180,23 @@ bool StpOrch::removeVlanFromStpInstance(string vlan_alias, sai_uint16_t stp_inst sai_status_t status = sai_vlan_api->set_vlan_attribute(vlan.m_vlan_info.vlan_oid, &attr); if (status != SAI_STATUS_SUCCESS) { - SWSS_LOG_ERROR("Failed to add VLAN %s to STP instance:%d status %u", vlan_alias.c_str(), vlan.m_stp_id, status); + SWSS_LOG_ERROR("Failed to remove VLAN %s from STP instance:%d status %u", vlan_alias.c_str(), vlan.m_stp_id, status); return false; } SWSS_LOG_INFO("Remove %s from instance:%d add instance:%" PRIx64 "", vlan_alias.c_str(), vlan.m_stp_id, m_defaultStpId); - + + // Update the new map structure + auto it = m_vlanAliasToStpInstanceMap.find(stp_instance); + if (it != m_vlanAliasToStpInstanceMap.end()) + { + it->second.stp_inst_vlan_list.erase(vlan_alias); + if (it->second.stp_inst_vlan_list.empty()) + { + //removeStpInstance(stp_instance); + m_vlanAliasToStpInstanceMap.erase(it); + } + } removeStpInstance(vlan.m_stp_id); vlan.m_stp_id = -1; gPortsOrch->setPort(vlan_alias, vlan); @@ -189,7 +214,7 @@ sai_object_id_t StpOrch::addStpPort(Port &port, sai_uint16_t stp_instance) { return port.m_stp_port_ids[stp_instance]; } - + if(port.m_bridge_port_id == SAI_NULL_OBJECT_ID) { gPortsOrch->addBridgePort(port); @@ -202,7 +227,7 @@ sai_object_id_t StpOrch::addStpPort(Port &port, sai_uint16_t stp_instance) } attr[0].id = SAI_STP_PORT_ATTR_BRIDGE_PORT; attr[0].value.oid = port.m_bridge_port_id; - + stp_id = getStpInstanceOid(stp_instance); if(stp_id == SAI_NULL_OBJECT_ID) { @@ -215,7 +240,7 @@ sai_object_id_t StpOrch::addStpPort(Port &port, sai_uint16_t stp_instance) attr[1].id = SAI_STP_PORT_ATTR_STP; attr[1].value.oid = stp_id; - + attr[2].id = SAI_STP_PORT_ATTR_STATE; attr[2].value.s32 = SAI_STP_PORT_STATE_BLOCKING; @@ -243,12 +268,12 @@ bool StpOrch::removeStpPort(Port &port, sai_uint16_t stp_instance) sai_status_t status = sai_stp_api->remove_stp_port(port.m_stp_port_ids[stp_instance]); if (status != SAI_STATUS_SUCCESS) { - SWSS_LOG_ERROR("Failed to remove STP port %s instance %d oid %" PRIx64 " status %x", port.m_alias.c_str(), stp_instance, + SWSS_LOG_ERROR("Failed to remove STP port %s instance %d oid %" PRIx64 " status %x", port.m_alias.c_str(), stp_instance, port.m_stp_port_ids[stp_instance], status); return false; } - SWSS_LOG_INFO("Remove STP port %s instance %d oid %" PRIx64 " size %zu", port.m_alias.c_str(), stp_instance, + SWSS_LOG_INFO("Remove STP port %s instance %d oid %" PRIx64 " size %zu", port.m_alias.c_str(), stp_instance, port.m_stp_port_ids[stp_instance], port.m_stp_port_ids.size()); port.m_stp_port_ids.erase(stp_instance); gPortsOrch->setPort(port.m_alias, port); @@ -329,7 +354,7 @@ bool StpOrch::updateStpPortState(Port &port, sai_uint16_t stp_instance, sai_uint SWSS_LOG_ERROR("Failed to set STP port state %s instance %d state %d status %x", port.m_alias.c_str(), stp_instance, stp_state, status); return false; } - + SWSS_LOG_INFO("Set STP port state %s instance %d state %d ", port.m_alias.c_str(), stp_instance, stp_state); return true; @@ -347,7 +372,7 @@ bool StpOrch::stpVlanFdbFlush(string vlan_alias) } gFdbOrch->flushFdbByVlan(vlan_alias); - + SWSS_LOG_INFO("Set STP FDB flush vlan %s ", vlan_alias.c_str()); return true; } @@ -493,6 +518,59 @@ void StpOrch::doStpFastageTask(Consumer &consumer) } } +void StpOrch::doMstInstPortFlushTask(Consumer &consumer) +{ + SWSS_LOG_ENTER(); + + for (auto it = consumer.m_toSync.begin(); it != consumer.m_toSync.end(); ) + { + auto &t = it->second; + string op = kfvOp(t); + string key = kfvKey(t); + size_t found = key.find(':'); + /* Return if the format of key is wrong */ + if (found == string::npos) + { + return; + } + + if (op == SET_COMMAND) + { + string state; + + string instance_alias = key.substr(0, found); + string port_alias = key.substr(found+1); + uint16_t instance = static_cast(stoi(instance_alias)); + + for (auto i : kfvFieldsValues(t)) + { + if (fvField(i) == "state") + state = fvValue(i); + } + + if (state.compare("true") == 0) + { + // Get all VLAN aliases for the given STP instance + auto it_map = m_vlanAliasToStpInstanceMap.find(instance); + if (it_map != m_vlanAliasToStpInstanceMap.end()) + { + for (const auto& vlan_alias : it_map->second.stp_inst_vlan_list) + { + stpVlanFdbFlush(vlan_alias); + } + } + } + } + else if (op == DEL_COMMAND) + { + // Handle delete command if necessary + } + + it = consumer.m_toSync.erase(it); + } +} + + void StpOrch::doTask(Consumer &consumer) { SWSS_LOG_ENTER(); @@ -515,6 +593,11 @@ void StpOrch::doTask(Consumer &consumer) { doStpFastageTask(consumer); } + + else if (table_name == APP_STP_INST_PORT_FLUSH_TABLE_NAME) + { + doMstInstPortFlushTask(consumer); + } } bool StpOrch::updateMaxStpInstance(uint32_t max_stp_instances) @@ -529,4 +612,5 @@ bool StpOrch::updateMaxStpInstance(uint32_t max_stp_instances) m_stpTable->set("GLOBAL", tuples); return true; + } \ No newline at end of file diff --git a/orchagent/stporch.h b/orchagent/stporch.h index b6c4e126f5..6d27b133b7 100644 --- a/orchagent/stporch.h +++ b/orchagent/stporch.h @@ -6,6 +6,7 @@ #include "orch.h" #define STP_INVALID_INSTANCE 0xFFFF +#define APP_STP_INST_PORT_FLUSH_TABLE_NAME "STP_INST_PORT_FLUSH_TABLE" typedef enum _stp_state { @@ -17,6 +18,12 @@ typedef enum _stp_state STP_STATE_INVALID = 5 }stp_state; +typedef struct StpInstEntry +{ + sai_object_id_t stp_inst_oid; + std::set stp_inst_vlan_list; +} StpInstEntry; + class StpOrch : public Orch { @@ -31,12 +38,16 @@ class StpOrch : public Orch unique_ptr
m_stpTable; std::map m_stpInstToOid;//Mapping from STP instance id to corresponding object id sai_object_id_t m_defaultStpId; + std::map m_vlanAliasToStpInstanceMap; + sai_uint16_t m_maxStpInstance; + void doStpTask(Consumer &consumer); void doStpPortStateTask(Consumer &consumer); void doStpFastageTask(Consumer &consumer); void doStpVlanIntfFlushTask(Consumer &consumer); + void doMstInstPortFlushTask(Consumer &consumer); sai_object_id_t addStpInstance(sai_uint16_t stp_instance); bool removeStpInstance(sai_uint16_t stp_instance); @@ -50,5 +61,4 @@ class StpOrch : public Orch void doTask(Consumer& consumer); }; -#endif /* SWSS_STPORCH_H */ - +#endif /* SWSS_STPORCH_H */ \ No newline at end of file diff --git a/tests/mock_tests/stporch_ut.cpp b/tests/mock_tests/stporch_ut.cpp index 0b5bf1278a..1a8d9ce88c 100644 --- a/tests/mock_tests/stporch_ut.cpp +++ b/tests/mock_tests/stporch_ut.cpp @@ -28,6 +28,7 @@ namespace stporch_test using ::testing::_; using ::testing::Return; + using ::gStpOrch; sai_status_t _ut_stub_sai_set_vlan_attribute(_In_ sai_object_id_t vlan_oid, _In_ const sai_attribute_t *attr) @@ -91,11 +92,12 @@ namespace stporch_test } void PostSetUp() override { - vector tableNames = - {"STP_TABLE", + vector tableNames = + {"STP_TABLE", "STP_VLAN_INSTANCE_TABLE", "STP_PORT_STATE_TABLE", - "STP_FASTAGEING_FLUSH_TABLE"}; + "STP_FASTAGEING_FLUSH_TABLE", + "STP_INST_PORT_FLUSH_TABLE"}; _hook_sai_switch_api(); gStpOrch = new StpOrch(m_app_db.get(), m_state_db.get(), tableNames); } @@ -190,16 +192,16 @@ namespace stporch_test ASSERT_TRUE(gPortsOrch->getPort(ETHERNET0, port)); ASSERT_TRUE(gPortsOrch->getPort(ETHERNET4, port1)); - EXPECT_CALL(mock_sai_stp_, + EXPECT_CALL(mock_sai_stp_, create_stp(_, _, _, _)).WillOnce(::testing::DoAll(::testing::SetArgPointee<0>(stp_oid), ::testing::Return(SAI_STATUS_SUCCESS))); result = gStpOrch->addVlanToStpInstance(VLAN_1000, stp_instance); ASSERT_TRUE(result); - EXPECT_CALL(mock_sai_stp_, + EXPECT_CALL(mock_sai_stp_, create_stp_port(_, _, 3, _)).WillOnce(::testing::DoAll(::testing::SetArgPointee<0>(stp_port_oid), ::testing::Return(SAI_STATUS_SUCCESS))); - EXPECT_CALL(mock_sai_stp_, + EXPECT_CALL(mock_sai_stp_, set_stp_port_attribute(_,_)).WillOnce(::testing::Return(SAI_STATUS_SUCCESS)); port.m_bridge_port_id = 1234; result = gStpOrch->updateStpPortState(port, stp_instance, STP_STATE_FORWARDING); @@ -208,61 +210,60 @@ namespace stporch_test result = gStpOrch->stpVlanFdbFlush(VLAN_1000); ASSERT_TRUE(result); - EXPECT_CALL(mock_sai_stp_, + EXPECT_CALL(mock_sai_stp_, remove_stp_port(_)).WillOnce(::testing::Return(SAI_STATUS_SUCCESS)); result = gStpOrch->removeStpPort(port, stp_instance); ASSERT_TRUE(result); - EXPECT_CALL(mock_sai_stp_, + EXPECT_CALL(mock_sai_stp_, create_stp_port(_, _, 3, _)).WillOnce(::testing::DoAll(::testing::SetArgPointee<0>(stp_port_oid), ::testing::Return(SAI_STATUS_SUCCESS))); - EXPECT_CALL(mock_sai_stp_, + EXPECT_CALL(mock_sai_stp_, set_stp_port_attribute(_,_)).WillOnce(::testing::Return(SAI_STATUS_SUCCESS)); port1.m_bridge_port_id = 1111; result = gStpOrch->updateStpPortState(port1, stp_instance, STP_STATE_BLOCKING); ASSERT_TRUE(result); - EXPECT_CALL(mock_sai_stp_, + EXPECT_CALL(mock_sai_stp_, remove_stp_port(_)).WillOnce(::testing::Return(SAI_STATUS_SUCCESS)); result = gStpOrch->removeStpPorts(port1); ASSERT_TRUE(result); - EXPECT_CALL(mock_sai_stp_, + EXPECT_CALL(mock_sai_stp_, remove_stp(_)).WillOnce(::testing::Return(SAI_STATUS_SUCCESS)); result = gStpOrch->removeVlanFromStpInstance(VLAN_1000, stp_instance); ASSERT_TRUE(result); std::deque entries; entries.push_back({"Vlan1000", "SET", { {"stp_instance", "1"}}}); - EXPECT_CALL(mock_sai_stp_, + EXPECT_CALL(mock_sai_stp_, create_stp(_, _, _, _)).WillOnce(::testing::DoAll(::testing::SetArgPointee<0>(stp_oid), ::testing::Return(SAI_STATUS_SUCCESS))); - + auto consumer = dynamic_cast(gStpOrch->getExecutor("STP_VLAN_INSTANCE_TABLE")); consumer->addToSync(entries); static_cast(gStpOrch)->doTask(); entries.clear(); - EXPECT_CALL(mock_sai_stp_, + EXPECT_CALL(mock_sai_stp_, create_stp_port(_, _, 3, _)).WillOnce(::testing::DoAll(::testing::SetArgPointee<0>(stp_port_oid), ::testing::Return(SAI_STATUS_SUCCESS))); - EXPECT_CALL(mock_sai_stp_, - set_stp_port_attribute(_,_)).WillOnce(::testing::Return(SAI_STATUS_SUCCESS)); + EXPECT_CALL(mock_sai_stp_, + set_stp_port_attribute(_,_)).WillOnce(::testing::Return(SAI_STATUS_SUCCESS)); entries.push_back({"Ethernet0:1", "SET", { {"state", "4"}}}); consumer = dynamic_cast(gStpOrch->getExecutor("STP_PORT_STATE_TABLE")); consumer->addToSync(entries); static_cast(gStpOrch)->doTask(); - + entries.clear(); entries.push_back({"Ethernet0:1", "SET", { {"state", "true"}}}); consumer = dynamic_cast(gStpOrch->getExecutor("STP_FASTAGEING_FLUSH_TABLE")); consumer->addToSync(entries); static_cast(gStpOrch)->doTask(); - entries.clear(); entries.push_back({"Ethernet0:1", "DEL", { {} }}); - EXPECT_CALL(mock_sai_stp_, + EXPECT_CALL(mock_sai_stp_, remove_stp_port(_)).WillOnce(::testing::Return(SAI_STATUS_SUCCESS)); consumer = dynamic_cast(gStpOrch->getExecutor("STP_PORT_STATE_TABLE")); consumer->addToSync(entries); @@ -270,12 +271,18 @@ namespace stporch_test entries.clear(); entries.push_back({"Vlan1000", "DEL", { {} }}); - EXPECT_CALL(mock_sai_stp_, + EXPECT_CALL(mock_sai_stp_, remove_stp(_)).WillOnce(::testing::Return(SAI_STATUS_SUCCESS)); consumer = dynamic_cast(gStpOrch->getExecutor("STP_VLAN_INSTANCE_TABLE")); consumer->addToSync(entries); static_cast(gStpOrch)->doTask(); - + + entries.clear(); + entries.push_back({"1:Ethernet0", "SET", { {"state", "true"} }}); + consumer = dynamic_cast(gStpOrch->getExecutor(APP_STP_INST_PORT_FLUSH_TABLE_NAME)); + consumer->addToSync(entries); + static_cast(gStpOrch)->doTask(); + _unhook_sai_stp_api(); _unhook_sai_vlan_api(); _unhook_sai_fdb_api();