diff --git a/cfgmgr/Makefile.am b/cfgmgr/Makefile.am index 45afff7e9b..6f8b7bfc65 100644 --- a/cfgmgr/Makefile.am +++ b/cfgmgr/Makefile.am @@ -5,7 +5,7 @@ LIBNL_LIBS = -lnl-genl-3 -lnl-route-3 -lnl-3 SAIMETA_LIBS = -lsaimeta -lsaimetadata -lzmq COMMON_LIBS = -lswsscommon -lpthread -bin_PROGRAMS = vlanmgrd teammgrd portmgrd intfmgrd buffermgrd vrfmgrd nbrmgrd vxlanmgrd sflowmgrd natmgrd coppmgrd tunnelmgrd macsecmgrd fabricmgrd +bin_PROGRAMS = vlanmgrd teammgrd portmgrd intfmgrd buffermgrd vrfmgrd nbrmgrd vxlanmgrd sflowmgrd natmgrd coppmgrd tunnelmgrd macsecmgrd fabricmgrd stpmgrd cfgmgrdir = $(datadir)/swss @@ -101,6 +101,12 @@ macsecmgrd_CFLAGS = $(DBGFLAGS) $(AM_CFLAGS) $(CFLAGS_COMMON) $(CFLAGS_SAI) $(CF macsecmgrd_CPPFLAGS = $(DBGFLAGS) $(AM_CFLAGS) $(CFLAGS_COMMON) $(CFLAGS_SAI) $(CFLAGS_ASAN) macsecmgrd_LDADD = $(LDFLAGS_ASAN) $(COMMON_LIBS) $(SAIMETA_LIBS) +stpmgrd_SOURCES = stpmgrd.cpp stpmgr.cpp $(top_srcdir)/orchagent/orch.cpp $(top_srcdir)/orchagent/request_parser.cpp $(top_srcdir)/orchagent/response_publisher.cpp shellcmd.h +stpmgrd_CFLAGS = $(DBGFLAGS) $(AM_CFLAGS) $(CFLAGS_COMMON) $(CFLAGS_SAI) $(LIBNL_CFLAGS) $(CFLAGS_ASAN) +stpmgrd_CPPFLAGS = $(DBGFLAGS) $(AM_CFLAGS) $(CFLAGS_COMMON) $(CFLAGS_SAI) $(CFLAGS_ASAN) +stpmgrd_LDADD = $(LDFLAGS_ASAN) $(COMMON_LIBS) $(SAIMETA_LIBS) + + if GCOV_ENABLED vlanmgrd_SOURCES += ../gcovpreload/gcovpreload.cpp teammgrd_SOURCES += ../gcovpreload/gcovpreload.cpp @@ -116,6 +122,7 @@ natmgrd_SOURCES += ../gcovpreload/gcovpreload.cpp coppmgrd_SOURCES += ../gcovpreload/gcovpreload.cpp tunnelmgrd_SOURCES += ../gcovpreload/gcovpreload.cpp macsecmgrd_SOURCES += ../gcovpreload/gcovpreload.cpp +stpmgrd_SOURCES += ../gcovpreload/gcovpreload.cpp endif if ASAN_ENABLED @@ -133,5 +140,6 @@ coppmgrd_SOURCES += $(top_srcdir)/lib/asan.cpp tunnelmgrd_SOURCES += $(top_srcdir)/lib/asan.cpp macsecmgrd_SOURCES += $(top_srcdir)/lib/asan.cpp fabricmgrd_SOURCES += $(top_srcdir)/lib/asan.cpp +stpmgrd_SOURCES += $(top_srcdir)/lib/asan.cpp endif diff --git a/cfgmgr/stpmgr.cpp b/cfgmgr/stpmgr.cpp new file mode 100644 index 0000000000..a75aaaaaa7 --- /dev/null +++ b/cfgmgr/stpmgr.cpp @@ -0,0 +1,1124 @@ +/* + * Copyright 2019 Broadcom. The term "Broadcom" refers to Broadcom Inc. and/or + * its subsidiaries. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "exec.h" +#include "stpmgr.h" +//#include "logger.h" +#include "tokenize.h" +#include "warm_restart.h" +#include "stp_ipc.h" + +#include +#include +#include +#include + +#include +#include +#include + +using namespace std; +using namespace swss; + +StpMgr::StpMgr(DBConnector *confDb, DBConnector *applDb, DBConnector *statDb, + const vector &tables) : + Orch(tables), + m_cfgStpGlobalTable(confDb, CFG_STP_GLOBAL_TABLE_NAME), + m_cfgStpVlanTable(confDb, CFG_STP_VLAN_TABLE_NAME), + m_cfgStpVlanPortTable(confDb, CFG_STP_VLAN_PORT_TABLE_NAME), + m_cfgStpPortTable(confDb, CFG_STP_PORT_TABLE_NAME), + m_cfgLagMemberTable(confDb, CFG_LAG_MEMBER_TABLE_NAME), + m_cfgVlanMemberTable(confDb, CFG_VLAN_MEMBER_TABLE_NAME), + 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) +{ + SWSS_LOG_ENTER(); + l2ProtoEnabled = L2_NONE; + + stpGlobalTask = stpVlanTask = stpVlanPortTask = stpPortTask = 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"); + if (ret != 0) { + SWSS_LOG_ERROR("ebtables add failed %d", ret); + } + SWSS_LOG_NOTICE("StpMgr constructor DONE"); +} + +void StpMgr::doTask(Consumer &consumer) +{ + auto table = consumer.getTableName(); + + SWSS_LOG_NOTICE("Get task from table %s", table.c_str()); + + if (table == CFG_STP_GLOBAL_TABLE_NAME) { + doStpGlobalTask(consumer); + } else if (table == CFG_STP_VLAN_TABLE_NAME) { + doStpVlanTask(consumer); + } else if (table == CFG_STP_VLAN_PORT_TABLE_NAME) { + doStpVlanPortTask(consumer); + } else if (table == CFG_STP_PORT_TABLE_NAME) { + doStpPortTask(consumer); + } else if (table == CFG_LAG_MEMBER_TABLE_NAME) { + doLagMemUpdateTask(consumer); + } else if (table == STATE_VLAN_MEMBER_TABLE_NAME) { + doVlanMemUpdateTask(consumer); + } else { + SWSS_LOG_ERROR("Invalid table %s", table.c_str()); + } +} + +void StpMgr::doStpGlobalTask(Consumer &consumer) +{ + SWSS_LOG_ENTER(); + + if (stpGlobalTask == false) + stpGlobalTask = true; + + auto it = consumer.m_toSync.begin(); + while (it != consumer.m_toSync.end()) + { + STP_BRIDGE_CONFIG_MSG msg; + memset(&msg, 0, sizeof(STP_BRIDGE_CONFIG_MSG)); + + KeyOpFieldsValuesTuple t = it->second; + + string key = kfvKey(t); + string op = kfvOp(t); + + SWSS_LOG_NOTICE("STP global key %s op %s", key.c_str(), op.c_str()); + if (op == SET_COMMAND) + { + msg.opcode = STP_SET_COMMAND; + for (auto i : kfvFieldsValues(t)) + { + SWSS_LOG_NOTICE("Field: %s Val %s", fvField(i).c_str(), fvValue(i).c_str()); + if (fvField(i) == "mode") + { + if (fvValue(i) == "pvst") + { + if (l2ProtoEnabled == L2_NONE) + { + 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); + } + + l2ProtoEnabled = L2_PVSTP; + } + msg.stp_mode = L2_PVSTP; + } + else { + SWSS_LOG_ERROR("Error invalid mode %s", fvValue(i).c_str()); + } + } + else if (fvField(i) == "rootguard_timeout") + { + msg.rootguard_timeout = stoi(fvValue(i).c_str()); + } + } + + memcpy(msg.base_mac_addr, macAddress.getMac(), 6); + } + else if (op == DEL_COMMAND) + { + msg.opcode = STP_DEL_COMMAND; + l2ProtoEnabled = L2_NONE; + + //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); + } + + sendMsgStpd(STP_BRIDGE_CONFIG, sizeof(msg), (void *)&msg); + + it = consumer.m_toSync.erase(it); + } +} + +void StpMgr::doStpVlanTask(Consumer &consumer) +{ + SWSS_LOG_ENTER(); + + if (stpGlobalTask == false || (stpPortTask == false && !isStpPortEmpty())) + return; + + if (stpVlanTask == false) + stpVlanTask = true; + + auto it = consumer.m_toSync.begin(); + while (it != consumer.m_toSync.end()) + { + STP_VLAN_CONFIG_MSG *msg = NULL; + uint32_t len = 0; + bool stpEnable = false; + uint8_t newInstance = 0; + int instId, forwardDelay, helloTime, maxAge, priority, portCnt = 0; + instId = forwardDelay = helloTime = maxAge = priority = portCnt = 0; + + KeyOpFieldsValuesTuple t = it->second; + + string key = kfvKey(t); + string op = kfvOp(t); + + string vlanKey = key.substr(4); // Remove Vlan prefix + int vlan_id = stoi(vlanKey.c_str()); + + SWSS_LOG_INFO("STP vlan key %s op %s", key.c_str(), op.c_str()); + if (op == SET_COMMAND) + { + if (l2ProtoEnabled == L2_NONE || !isVlanStateOk(key)) + { + // Wait till STP is configured + it++; + continue; + } + + for (auto i : kfvFieldsValues(t)) + { + SWSS_LOG_DEBUG("Field: %s Val: %s", fvField(i).c_str(), fvValue(i).c_str()); + + if (fvField(i) == "enabled") + { + stpEnable = (fvValue(i) == "true") ? true : false; + } + else if (fvField(i) == "forward_delay") + { + forwardDelay = stoi(fvValue(i).c_str()); + } + else if (fvField(i) == "hello_time") + { + helloTime = stoi(fvValue(i).c_str()); + } + else if (fvField(i) == "max_age") + { + maxAge = stoi(fvValue(i).c_str()); + } + else if (fvField(i) == "priority") + { + priority = stoi(fvValue(i).c_str()); + } + } + } + else if (op == DEL_COMMAND) + { + stpEnable = false; + if (l2ProtoEnabled == L2_NONE) + { + it = consumer.m_toSync.erase(it); + continue; + } + } + + len = sizeof(STP_VLAN_CONFIG_MSG); + if (stpEnable == true) + { + vector port_list; + if (m_vlanInstMap[vlan_id] == INVALID_INSTANCE) + { + /* VLAN is being added to the instance. Get all members*/ + SWSS_LOG_NOTICE("enabling pvst for vlan id %d", vlan_id); + if (l2ProtoEnabled == L2_PVSTP) + { + newInstance = 1; + instId = allocL2Instance(vlan_id); + if (instId == -1) + { + SWSS_LOG_ERROR("Couldnt allocate instance to VLAN %d", vlan_id); + it = consumer.m_toSync.erase(it); + continue; + } + + portCnt = getAllVlanMem(key, port_list); + SWSS_LOG_NOTICE("Port count %d", portCnt); + } + + len += (uint32_t)(portCnt * sizeof(PORT_ATTR)); + } + + 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; + msg->inst_id = m_vlanInstMap[vlan_id]; + msg->forward_delay = forwardDelay; + msg->hello_time = helloTime; + msg->max_age = maxAge; + msg->priority = priority; + msg->count = portCnt; + + if(msg->count) + { + int i = 0; + PORT_ATTR *attr = msg->port_list; + for (auto p = port_list.begin(); p != port_list.end(); p++) + { + attr[i].mode = p->mode; + attr[i].enabled = p->enabled; + strncpy(attr[i].intf_name, p->intf_name, IFNAMSIZ); + attr[i].intf_name[IFNAMSIZ-1] = '\0'; + SWSS_LOG_DEBUG("MemIntf: %s", p->intf_name); + i++; + } + } + } + else + { + if (m_vlanInstMap[vlan_id] == INVALID_INSTANCE) + { + // 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); + if (!msg) + { + SWSS_LOG_ERROR("mem failed for vlan %d", vlan_id); + return; + } + + msg->opcode = STP_DEL_COMMAND; + msg->inst_id = m_vlanInstMap[vlan_id]; + + deallocL2Instance(vlan_id); + } + + sendMsgStpd(STP_VLAN_CONFIG, len, (void *)msg); + if (msg) + free(msg); + + it = consumer.m_toSync.erase(it); + } +} + +void StpMgr::processStpVlanPortAttr(const string op, uint32_t vlan_id, const string intfName, + vector&tupEntry) +{ + STP_VLAN_PORT_CONFIG_MSG msg; + memset(&msg, 0, sizeof(STP_VLAN_PORT_CONFIG_MSG)); + + msg.vlan_id = vlan_id; + msg.inst_id = m_vlanInstMap[vlan_id]; + strncpy(msg.intf_name, intfName.c_str(), IFNAMSIZ); + msg.intf_name[IFNAMSIZ-1] = '\0'; + + 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; + } + + sendMsgStpd(STP_VLAN_PORT_CONFIG, sizeof(msg), (void *)&msg); +} + +void StpMgr::doStpVlanPortTask(Consumer &consumer) +{ + SWSS_LOG_ENTER(); + + if (stpGlobalTask == false || stpVlanTask == false || stpPortTask == false) + return; + + if (stpVlanPortTask == false) + stpVlanPortTask = true; + + auto it = consumer.m_toSync.begin(); + while (it != consumer.m_toSync.end()) + { + STP_VLAN_PORT_CONFIG_MSG msg; + memset(&msg, 0, sizeof(STP_VLAN_PORT_CONFIG_MSG)); + + KeyOpFieldsValuesTuple t = it->second; + + string key = kfvKey(t); + string op = kfvOp(t); + + string vlanKey = key.substr(4); // Remove VLAN keyword + size_t found = vlanKey.find(CONFIGDB_KEY_SEPARATOR); + + int vlan_id; + string intfName; + if (found != string::npos) + { + vlan_id = stoi(vlanKey.substr(0, found)); + intfName = vlanKey.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 vlan intf key:%s op:%s", key.c_str(), op.c_str()); + + if (op == SET_COMMAND) + { + if ((l2ProtoEnabled == L2_NONE) || (m_vlanInstMap[vlan_id] == INVALID_INSTANCE)) + { + // Wait till STP/VLAN is configured + it++; + continue; + } + } + else + { + if (l2ProtoEnabled == L2_NONE || (m_vlanInstMap[vlan_id] == INVALID_INSTANCE)) + { + it = consumer.m_toSync.erase(it); + continue; + } + } + + if (isLagEmpty(intfName)) + { + // Lag has no member. Process when first member is added/deleted + it = consumer.m_toSync.erase(it); + continue; + } + + processStpVlanPortAttr(op, vlan_id, intfName, kfvFieldsValues(t)); + + it = consumer.m_toSync.erase(it); + } +} + +void StpMgr::processStpPortAttr(const string op, vector&tupEntry, const string intfName) +{ + STP_PORT_CONFIG_MSG *msg; + uint32_t len = 0; + int vlanCnt = 0; + vector vlan_list; + + if (op == SET_COMMAND) + vlanCnt = getAllPortVlan(intfName, vlan_list); + + len = (uint32_t)(sizeof(STP_PORT_CONFIG_MSG) + (vlanCnt * sizeof(VLAN_ATTR))); + msg = (STP_PORT_CONFIG_MSG *)calloc(1, len); + if (!msg) + { + SWSS_LOG_ERROR("mem failed for %s", intfName.c_str()); + return; + } + + strncpy(msg->intf_name, intfName.c_str(), IFNAMSIZ); + msg->intf_name[IFNAMSIZ-1] = '\0'; + msg->count = vlanCnt; + SWSS_LOG_INFO("Vlan count %d", vlanCnt); + + if(msg->count) + { + int i = 0; +#pragma GCC diagnostic push +#pragma GCC diagnostic ignored "-Waddress-of-packed-member" + VLAN_ATTR *attr = msg->vlan_list; +#pragma GCC diagnostic pop + for (auto p = vlan_list.begin(); p != vlan_list.end(); p++) + { + attr[i].inst_id = p->inst_id; + attr[i].mode = p->mode; + SWSS_LOG_DEBUG("Inst:%d Mode:%d", p->inst_id, p->mode); + i++; + } + } + + 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) == "enabled") + { + msg->enabled = (fvValue(i) == "true") ? 1 : 0; + } + else if (fvField(i) == "root_guard") + { + msg->root_guard = (fvValue(i) == "true") ? 1 : 0; + } + else if (fvField(i) == "bpdu_guard") + { + msg->bpdu_guard = (fvValue(i) == "true") ? 1 : 0; + } + else if (fvField(i) == "bpdu_guard_do_disable") + { + msg->bpdu_guard_do_disable = (fvValue(i) == "true") ? 1 : 0; + } + else 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 (fvField(i) == "portfast") + { + msg->portfast = (fvValue(i) == "true") ? 1 : 0; + } + else if (fvField(i) == "uplink_fast") + { + msg->uplink_fast = (fvValue(i) == "true") ? 1 : 0; + } + } + } + else if (op == DEL_COMMAND) + { + msg->opcode = STP_DEL_COMMAND; + msg->enabled = 0; + } + + sendMsgStpd(STP_PORT_CONFIG, len, (void *)msg); + if (msg) + free(msg); +} + +void StpMgr::doStpPortTask(Consumer &consumer) +{ + SWSS_LOG_ENTER(); + + if (stpGlobalTask == false) + return; + + if (stpPortTask == false) + stpPortTask = true; + + auto it = consumer.m_toSync.begin(); + while (it != consumer.m_toSync.end()) + { + KeyOpFieldsValuesTuple t = it->second; + + string key = kfvKey(t); + string op = kfvOp(t); + + if (isLagEmpty(key)) + { + it = consumer.m_toSync.erase(it); + continue; + } + + if (op == SET_COMMAND) + { + if (l2ProtoEnabled == L2_NONE) + { + // Wait till STP is configured + it++; + continue; + } + } + else + { + if (l2ProtoEnabled == L2_NONE) + { + it = consumer.m_toSync.erase(it); + continue; + } + } + + SWSS_LOG_INFO("STP port key:%s op:%s", key.c_str(), op.c_str()); + processStpPortAttr(op, kfvFieldsValues(t), key); + + it = consumer.m_toSync.erase(it); + } +} + +void StpMgr::doVlanMemUpdateTask(Consumer &consumer) +{ + SWSS_LOG_ENTER(); + + auto it = consumer.m_toSync.begin(); + while (it != consumer.m_toSync.end()) + { + STP_VLAN_MEM_CONFIG_MSG msg; + memset(&msg, 0, sizeof(STP_VLAN_MEM_CONFIG_MSG)); + + KeyOpFieldsValuesTuple t = it->second; + + auto key = kfvKey(t); + auto op = kfvOp(t); + + string vlanKey = key.substr(4); // Remove Vlan prefix + size_t found = vlanKey.find(CONFIGDB_KEY_SEPARATOR); + + int vlan_id; + string intfName; + if (found != string::npos) + { + vlan_id = stoi(vlanKey.substr(0, found)); + intfName = vlanKey.substr(found+1); + } + else + { + SWSS_LOG_ERROR("Invalid key format. No member port is presented: %s", kfvKey(t).c_str()); + 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)) + { + int8_t tagging_mode = TAGGED_MODE; + + if (op == SET_COMMAND) + { + tagging_mode = getVlanMemMode(key); + if (tagging_mode == INVALID_MODE) + { + SWSS_LOG_ERROR("invalid mode %s", key.c_str()); + it = consumer.m_toSync.erase(it); + continue; + } + + SWSS_LOG_DEBUG("mode %d key %s", tagging_mode, key.c_str()); + + msg.enabled = isStpEnabled(intfName); + + vector stpVlanPortEntry; + if (m_cfgStpVlanPortTable.get(key, stpVlanPortEntry)) + { + for (auto entry : stpVlanPortEntry) + { + if (entry.first == "priority") + msg.priority = stoi(entry.second); + else if (entry.first == "path_cost") + msg.path_cost = stoi(entry.second); + } + } + } + + msg.opcode = (op == SET_COMMAND) ? STP_SET_COMMAND : STP_DEL_COMMAND; + msg.vlan_id = vlan_id; + msg.inst_id = m_vlanInstMap[vlan_id]; + msg.mode = tagging_mode; + msg.priority = -1; + msg.path_cost = 0; + + strncpy(msg.intf_name, intfName.c_str(), IFNAMSIZ); + msg.intf_name[IFNAMSIZ-1] = '\0'; + + sendMsgStpd(STP_VLAN_MEM_CONFIG, sizeof(msg), (void *)&msg); + } + + it = consumer.m_toSync.erase(it); + } +} + +void StpMgr::doLagMemUpdateTask(Consumer &consumer) +{ + SWSS_LOG_ENTER(); + + auto it = consumer.m_toSync.begin(); + while (it != consumer.m_toSync.end()) + { + KeyOpFieldsValuesTuple t = it->second; + bool notifyStpd = false; + + auto key = kfvKey(t); + auto op = kfvOp(t); + + string po_name; + string po_mem; + size_t found = key.find(CONFIGDB_KEY_SEPARATOR); + + if (found != string::npos) + { + po_name = key.substr(0, found); + po_mem = key.substr(found+1); + } + else + { + SWSS_LOG_ERROR("Invalid key format %s", key.c_str()); + it = consumer.m_toSync.erase(it); + continue; + } + + if (op == SET_COMMAND) + { + if (!isLagStateOk(po_name)) + { + it++; + continue; + } + + auto elm = m_lagMap.find(po_name); + if (elm == m_lagMap.end()) + { + // First Member added to the LAG + m_lagMap[po_name] = 1; + notifyStpd = true; + } + else + { + elm->second++; + } + } + else if (op == DEL_COMMAND) + { + auto elm = m_lagMap.find(po_name); + if (elm != m_lagMap.end()) + { + elm->second--; + + if (elm->second == 0) + { + // Last Member deleted from the LAG + m_lagMap.erase(po_name); + //notifyStpd = true; + } + } + else + SWSS_LOG_ERROR("PO not found %s", po_name.c_str()); + } + + if (notifyStpd && l2ProtoEnabled != L2_NONE) + { + vector vlan_list; + vector tupEntry; + + if (m_cfgStpPortTable.get(po_name, tupEntry)) + { + //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++) + { + vector vlanPortTup; + + string vlanPortKey = "Vlan" + to_string(p->vlan_id) + "|" + po_name; + if (m_cfgStpVlanPortTable.get(vlanPortKey, vlanPortTup)) + processStpVlanPortAttr(op, p->vlan_id, po_name, vlanPortTup); + } + } + } + + SWSS_LOG_DEBUG("LagMap"); + 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); + } +} + +void StpMgr::ipcInitStpd() +{ + int ret; + struct sockaddr_un addr; + + unlink(STPMGRD_SOCK_NAME); + // create socket + stpd_fd = socket(AF_UNIX, SOCK_DGRAM, 0); + if (!stpd_fd) { + SWSS_LOG_ERROR("socket error %s", strerror(errno)); + return; + } + + // setup socket address structure + bzero(&addr, sizeof(addr)); + addr.sun_family = AF_UNIX; + strncpy(addr.sun_path, STPMGRD_SOCK_NAME, sizeof(addr.sun_path)-1); + + ret = (int)bind(stpd_fd, (struct sockaddr *)&addr, sizeof(struct sockaddr_un)); + if (ret == -1) + { + SWSS_LOG_ERROR("ipc bind error %s", strerror(errno)); + close(stpd_fd); + return; + } +} + +int StpMgr::allocL2Instance(uint32_t vlan_id) +{ + int idx = 0; + + if (!IS_INST_ID_AVAILABLE()) + { + SWSS_LOG_ERROR("No instance available"); + return -1; + } + + if (l2ProtoEnabled == L2_PVSTP) + { + GET_FIRST_FREE_INST_ID(idx); + } + else + { + SWSS_LOG_ERROR("invalid proto %d for vlan %d", l2ProtoEnabled, vlan_id); + return -1; + } + + //Set VLAN to Instance mapping + m_vlanInstMap[vlan_id] = idx; + SWSS_LOG_INFO("Allocated Id: %d Vlan %d", m_vlanInstMap[vlan_id], vlan_id); + + return idx; +} + +void StpMgr::deallocL2Instance(uint32_t vlan_id) +{ + int idx = 0; + + if (l2ProtoEnabled == L2_PVSTP) + { + idx = m_vlanInstMap[vlan_id]; + FREE_INST_ID(idx); + } + else + { + SWSS_LOG_ERROR("invalid proto %d for vlan %d", l2ProtoEnabled, vlan_id); + } + + m_vlanInstMap[vlan_id] = INVALID_INSTANCE; + SWSS_LOG_INFO("Deallocated Id: %d Vlan %d", m_vlanInstMap[vlan_id], vlan_id); +} + + +int StpMgr::getAllVlanMem(const string &vlanKey, vector&port_list) +{ + PORT_ATTR port_id; + vector vmEntry; + + vector vmKeys; + m_stateVlanMemberTable.getKeys(vmKeys); + + SWSS_LOG_INFO("VLAN Key: %s", vlanKey.c_str()); + for (auto key : vmKeys) + { + size_t found = key.find(CONFIGDB_KEY_SEPARATOR); //split VLAN and interface + + string vlanName; + string intfName; + if (found != string::npos) + { + vlanName = key.substr(0, found); + intfName = key.substr(found+1); + } + else + { + SWSS_LOG_ERROR("Invalid Key: %s", key.c_str()); + continue; + } + + if (vlanKey == vlanName && !isLagEmpty(intfName)) + { + port_id.mode = getVlanMemMode(key); + if (port_id.mode == INVALID_MODE) + { + SWSS_LOG_ERROR("invalid mode %s", key.c_str()); + continue; + } + + port_id.enabled = isStpEnabled(intfName); + strncpy(port_id.intf_name, intfName.c_str(), IFNAMSIZ); + port_id.intf_name[IFNAMSIZ-1] = '\0'; + port_list.push_back(port_id); + SWSS_LOG_DEBUG("MemIntf: %s", intfName.c_str()); + } + } + + return (int)port_list.size(); +} + +int StpMgr::getAllPortVlan(const string &intfKey, vector&vlan_list) +{ + VLAN_ATTR vlan; + vector vmEntry; + + vector vmKeys; + m_stateVlanMemberTable.getKeys(vmKeys); + + SWSS_LOG_INFO("Intf Key: %s", intfKey.c_str()); + for (auto key : vmKeys) + { + string vlanKey = key.substr(4); // Remove Vlan prefix + size_t found = vlanKey.find(CONFIGDB_KEY_SEPARATOR); //split VLAN and interface + SWSS_LOG_DEBUG("Vlan mem Key: %s", key.c_str()); + + int vlan_id; + string intfName; + if (found != string::npos) + { + vlan_id = stoi(vlanKey.substr(0, found)); + intfName = vlanKey.substr(found+1); + + if (intfName == intfKey) + { + if (m_vlanInstMap[vlan_id] != INVALID_INSTANCE) + { + vlan.mode = getVlanMemMode(key); + if (vlan.mode == INVALID_MODE) + { + 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); + SWSS_LOG_DEBUG("Matched vlan key: %s intf key %s", intfName.c_str(), intfKey.c_str()); + } + } + } + } + + return (int)vlan_list.size(); +} + +// Send Message to STPd +int StpMgr::sendMsgStpd(STP_MSG_TYPE msgType, uint32_t msgLen, void *data) +{ + STP_IPC_MSG *tx_msg; + size_t len = 0; + struct sockaddr_un addr; + int rc; + + len = msgLen + (offsetof(struct STP_IPC_MSG, data)); + SWSS_LOG_NOTICE("STPd tx_msg len %d msglen %d", (int)len, msgLen); + + tx_msg = (STP_IPC_MSG *)calloc(1, len); + if (tx_msg == NULL) + { + SWSS_LOG_ERROR("tx_msg mem alloc error\n"); + return -1; + } + + tx_msg->msg_type = msgType; + tx_msg->msg_len = msgLen; + memcpy(tx_msg->data, data, msgLen); + + bzero(&addr, sizeof(addr)); + addr.sun_family = AF_UNIX; + strncpy(addr.sun_path, STPD_SOCK_NAME, sizeof(addr.sun_path)-1); + + rc = (int)sendto(stpd_fd, (void*)tx_msg, len, 0, (struct sockaddr *)&addr, sizeof(addr)); + if (rc == -1) + { + perror("sendto"); + SWSS_LOG_ERROR("tx_msg send error\n"); + } + else + { + SWSS_LOG_INFO("tx_msg sent %d", rc); + } + + free(tx_msg); + return rc; +} + +bool StpMgr::isPortInitDone(DBConnector *app_db) +{ + bool portInit = 0; + long cnt = 0; + + while(!portInit) { + Table portTable(app_db, APP_PORT_TABLE_NAME); + std::vector tuples; + portInit = portTable.get("PortInitDone", tuples); + + if(portInit) + break; + sleep(1); + cnt++; + } + SWSS_LOG_NOTICE("PORT_INIT_DONE : %d %ld", portInit, cnt); + return portInit; +} + +bool StpMgr::isVlanStateOk(const string &alias) +{ + vector temp; + + if (!alias.compare(0, strlen(VLAN_PREFIX), VLAN_PREFIX)) + { + if (m_stateVlanTable.get(alias, temp)) + { + SWSS_LOG_DEBUG("%s is ready", alias.c_str()); + return true; + } + } + SWSS_LOG_DEBUG("%s is not ready", alias.c_str()); + return false; +} + +bool StpMgr::isLagStateOk(const string &alias) +{ + vector temp; + + if (m_stateLagTable.get(alias, temp)) + { + SWSS_LOG_DEBUG("%s is ready", alias.c_str()); + return true; + } + + SWSS_LOG_DEBUG("%s is not ready", alias.c_str()); + return false; +} + +bool StpMgr::isLagEmpty(const string &key) +{ + size_t po_find = key.find("PortChannel"); + if (po_find != string::npos) + { + // If Lag, check if members present + auto elm = m_lagMap.find(key); + if (elm == m_lagMap.end()) + { + // Lag has no member + SWSS_LOG_DEBUG("%s empty", key.c_str()); + return true; + } + SWSS_LOG_DEBUG("%s not empty", key.c_str()); + } + // Else: Interface not PO + + return false; +} + +bool StpMgr::isStpPortEmpty() +{ + vector portKeys; + m_cfgStpPortTable.getKeys(portKeys); + + if (portKeys.empty()) + { + SWSS_LOG_NOTICE("stp port empty"); + return true; + } + + SWSS_LOG_NOTICE("stp port not empty"); + return false; +} + +bool StpMgr::isStpEnabled(const string &intf_name) +{ + vector temp; + + if (m_cfgStpPortTable.get(intf_name, temp)) + { + for (auto entry : temp) + { + if (entry.first == "enabled" && entry.second == "true") + { + SWSS_LOG_NOTICE("STP enabled on %s", intf_name.c_str()); + return true; + } + } + } + + SWSS_LOG_NOTICE("STP NOT enabled on %s", intf_name.c_str()); + return false; +} + +int8_t StpMgr::getVlanMemMode(const string &key) +{ + int8_t mode = -1; + vector vmEntry; + + if (m_cfgVlanMemberTable.get(key, vmEntry)) + { + for (auto entry : vmEntry) + { + if (entry.first == "tagging_mode") + mode = (entry.second == "untagged") ? UNTAGGED_MODE : TAGGED_MODE; + SWSS_LOG_INFO("mode %d for %s", mode, key.c_str()); + } + } + else + SWSS_LOG_ERROR("config vlan_member table fetch failed %s", key.c_str()); + + return mode; +} + +uint16_t StpMgr::getStpMaxInstances(void) +{ + vector vmEntry; + uint16_t max_delay = 60; + string key; + + key = "GLOBAL"; + + while(max_delay) + { + if (m_stateStpTable.get(key, vmEntry)) + { + for (auto entry : vmEntry) + { + if (entry.first == "max_stp_inst") + { + max_stp_instances = (uint16_t)stoi(entry.second.c_str()); + SWSS_LOG_NOTICE("max stp instance %d count %d", max_stp_instances, (60-max_delay)); + } + } + break; + } + sleep(1); + max_delay--; + } + + if(max_stp_instances == 0) + { + max_stp_instances = STP_DEFAULT_MAX_INSTANCES; + SWSS_LOG_NOTICE("set default max stp instance %d", max_stp_instances); + } + + return max_stp_instances; +} diff --git a/cfgmgr/stpmgr.h b/cfgmgr/stpmgr.h new file mode 100644 index 0000000000..0a990e0890 --- /dev/null +++ b/cfgmgr/stpmgr.h @@ -0,0 +1,127 @@ +/* + * Copyright 2019 Broadcom. The term "Broadcom" refers to Broadcom Inc. and/or + * its subsidiaries. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef __STPMGR__ +#define __STPMGR__ + + +#include +#include +#include +#include +#include +#include +#include +#include + +#include "dbconnector.h" +#include "netmsg.h" +#include "orch.h" +#include "producerstatetable.h" +#include +#include +#include + +#define STPMGRD_SOCK_NAME "/var/run/stpmgrd.sock" + +#define TAGGED_MODE 1 +#define UNTAGGED_MODE 0 +#define INVALID_MODE -1 + +#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 GET_FIRST_FREE_INST_ID(_idx) \ + while (_idx < (int)l2InstPool.size() && l2InstPool.test(_idx)) ++_idx; \ + l2InstPool.set(_idx) + +#define FREE_INST_ID(_idx) l2InstPool.reset(_idx) + +#define FREE_ALL_INST_ID() l2InstPool.reset() + +#define IS_INST_ID_AVAILABLE() (l2InstPool.count() < max_stp_instances) ? true : false + + +namespace swss { + +class StpMgr : public Orch +{ +public: + StpMgr(DBConnector *cfgDb, DBConnector *appDb, DBConnector *stateDb, + const std::vector &tables); + + using Orch::doTask; + void ipcInitStpd(); + int sendMsgStpd(STP_MSG_TYPE msgType, uint32_t msgLen, void *data); + MacAddress macAddress; + bool isPortInitDone(DBConnector *app_db); + uint16_t getStpMaxInstances(void); + +private: + Table m_cfgStpGlobalTable; + Table m_cfgStpVlanTable; + Table m_cfgStpVlanPortTable; + Table m_cfgStpPortTable; + Table m_cfgLagMemberTable; + Table m_cfgVlanMemberTable; + Table m_stateVlanTable; + Table m_stateVlanMemberTable; + Table m_stateLagTable; + Table m_stateStpTable; + + std::bitset l2InstPool; + int stpd_fd; + enum L2_PROTO_MODE l2ProtoEnabled; + int m_vlanInstMap[MAX_VLANS]; + bool portCfgDone; + uint16_t max_stp_instances; + std::map m_lagMap; + + bool stpGlobalTask; + bool stpVlanTask; + bool stpVlanPortTask; + bool stpPortTask; + + void doTask(Consumer &consumer); + void doStpGlobalTask(Consumer &consumer); + void doStpVlanTask(Consumer &consumer); + void doStpVlanPortTask(Consumer &consumer); + void doStpPortTask(Consumer &consumer); + void doVlanMemUpdateTask(Consumer &consumer); + void doLagMemUpdateTask(Consumer &consumer); + + bool isVlanStateOk(const std::string &alias); + bool isLagStateOk(const std::string &alias); + bool isStpPortEmpty(); + bool isStpEnabled(const std::string &intf_name); + int getAllVlanMem(const std::string &vlanKey, std::vector&port_list); + int getAllPortVlan(const std::string &intfKey, std::vector&vlan_list); + int8_t getVlanMemMode(const std::string &key); + int allocL2Instance(uint32_t vlan_id); + void deallocL2Instance(uint32_t vlan_id); + bool isLagEmpty(const std::string &key); + 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); +}; + +} +#endif diff --git a/cfgmgr/stpmgrd.cpp b/cfgmgr/stpmgrd.cpp new file mode 100644 index 0000000000..2de37761cb --- /dev/null +++ b/cfgmgr/stpmgrd.cpp @@ -0,0 +1,142 @@ +/* + * Copyright 2019 Broadcom. The term "Broadcom" refers to Broadcom Inc. and/or + * its subsidiaries. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include +#include + +#include "stpmgr.h" +#include "netdispatcher.h" +#include "netlink.h" +#include "select.h" +#include "warm_restart.h" + +using namespace std; +using namespace swss; + +int gBatchSize = 0; +bool gSwssRecord = false; +bool gLogRotate = false; +ofstream gRecordOfs; +string gRecordFile; +bool gResponsePublisherRecord = false; +bool gResponsePublisherLogRotate = false; +ofstream gResponsePublisherRecordOfs; +string gResponsePublisherRecordFile; + +#define SELECT_TIMEOUT 1000 + +int main(int argc, char **argv) +{ + Logger::linkToDbNative("stpmgrd"); + SWSS_LOG_ENTER(); + + SWSS_LOG_NOTICE("--- Starting stpmgrd ---"); + + if (fopen("/stpmgrd_dbg_reload", "r")) + { + Logger::setMinPrio(Logger::SWSS_DEBUG); + } + + try + { + DBConnector conf_db(CONFIG_DB, DBConnector::DEFAULT_UNIXSOCKET, 0); + DBConnector app_db(APPL_DB, DBConnector::DEFAULT_UNIXSOCKET, 0); + DBConnector state_db(STATE_DB, DBConnector::DEFAULT_UNIXSOCKET, 0); + + WarmStart::initialize("stpmgrd", "stpd"); + WarmStart::checkWarmStart("stpmgrd", "stpd"); + + // Config DB Tables + TableConnector conf_stp_global_table(&conf_db, CFG_STP_GLOBAL_TABLE_NAME); + 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); + + // 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 + }; + + + StpMgr stpmgr(&conf_db, &app_db, &state_db, tables); + + // Open a Unix Domain Socket with STPd + 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)); + msg.max_stp_instances = stpmgr.getStpMaxInstances(); + stpmgr.sendMsgStpd(STP_INIT_READY, sizeof(msg), (void *)&msg); + + // Get Base MAC + Table table(&conf_db, "DEVICE_METADATA"); + std::vector ovalues; + table.get("localhost", ovalues); + auto it = std::find_if( ovalues.begin(), ovalues.end(), [](const FieldValueTuple& t){ return t.first == "mac";} ); + if ( it == ovalues.end() ) { + throw runtime_error("couldn't find MAC address of the device from config DB"); + } + stpmgr.macAddress = MacAddress(it->second); + + vector cfgOrchList = {&stpmgr}; + + Select s; + for (Orch *o: cfgOrchList) + { + s.addSelectables(o->getSelectables()); + } + + while (true) + { + Selectable *sel; + int ret; + + ret = s.select(&sel, SELECT_TIMEOUT); + if (ret == Select::ERROR) + { + SWSS_LOG_NOTICE("Error: %s!", strerror(errno)); + continue; + } + if (ret == Select::TIMEOUT) + { + stpmgr.doTask(); + continue; + } + + auto *c = (Executor *)sel; + c->execute(); + } + } + catch (const exception &e) + { + SWSS_LOG_ERROR("Runtime error: %s", e.what()); + } + + return -1; +} diff --git a/configure.ac b/configure.ac index 231f1e1c58..63a792c191 100644 --- a/configure.ac +++ b/configure.ac @@ -18,6 +18,11 @@ AC_CHECK_LIB([team], [team_alloc], [AC_MSG_WARN([libteam is not installed.]) AM_CONDITIONAL(HAVE_LIBTEAM, false)]) +AC_CHECK_FILE([/usr/include/stp_ipc.h], + AM_CONDITIONAL(HAVE_STP, true), + [AC_MSG_WARN([stp is not installed.]) + AM_CONDITIONAL(HAVE_STP, false)]) + PKG_CHECK_MODULES([JANSSON], [jansson]) AC_CHECK_LIB([sai], [sai_object_type_query], diff --git a/orchagent/Makefile.am b/orchagent/Makefile.am index 44714f62a1..5e24c7a6a6 100644 --- a/orchagent/Makefile.am +++ b/orchagent/Makefile.am @@ -116,7 +116,8 @@ orchagent_SOURCES = \ dash/dashaclgroupmgr.cpp \ dash/dashtagmgr.cpp \ dash/pbutils.cpp \ - twamporch.cpp + twamporch.cpp \ + stporch.cpp orchagent_SOURCES += flex_counter/flex_counter_manager.cpp flex_counter/flex_counter_stat_manager.cpp flex_counter/flow_counter_handler.cpp flex_counter/flowcounterrouteorch.cpp orchagent_SOURCES += debug_counter/debug_counter.cpp debug_counter/drop_counter.cpp diff --git a/orchagent/orchdaemon.cpp b/orchagent/orchdaemon.cpp index 047263c93a..30f0ceaa75 100644 --- a/orchagent/orchdaemon.cpp +++ b/orchagent/orchdaemon.cpp @@ -64,6 +64,7 @@ FlowCounterRouteOrch *gFlowCounterRouteOrch; DebugCounterOrch *gDebugCounterOrch; MonitorOrch *gMonitorOrch; TunnelDecapOrch *gTunneldecapOrch; +StpOrch *gStpOrch; bool gIsNatSupported = false; event_handle_t g_events_handle; @@ -166,6 +167,14 @@ bool OrchDaemon::init() gFlowCounterRouteOrch = new FlowCounterRouteOrch(m_configDb, route_pattern_tables); gDirectory.set(gFlowCounterRouteOrch); + SWSS_LOG_NOTICE("starting STP orch class object\n"); + vector stp_tables = { + APP_STP_VLAN_INSTANCE_TABLE_NAME, + APP_STP_PORT_STATE_TABLE_NAME, + APP_STP_FASTAGEING_FLUSH_TABLE_NAME + }; + gStpOrch = new StpOrch(m_applDb, m_stateDb, stp_tables); + vector vnet_tables = { APP_VNET_RT_TABLE_NAME, APP_VNET_RT_TUNNEL_TABLE_NAME @@ -417,7 +426,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, gRouteOrch, gCoppOrch, gQosOrch, wm_orch, gPolicerOrch, gTunneldecapOrch, sflow_orch, gDebugCounterOrch, gMacsecOrch, bgp_global_state_orch, gBfdOrch, gSrv6Orch, mux_orch, mux_cb_orch, gMonitorOrch}; + m_orchList = { gSwitchOrch, gCrmOrch, gPortsOrch, gBufferOrch, gFlowCounterRouteOrch, gIntfsOrch, gNeighOrch, gNhgMapOrch, gNhgOrch, gCbfNhgOrch, gRouteOrch, gCoppOrch, gQosOrch, wm_orch, gPolicerOrch, gTunneldecapOrch, sflow_orch, gDebugCounterOrch, gMacsecOrch, bgp_global_state_orch, gBfdOrch, gSrv6Orch, mux_orch, mux_cb_orch, gMonitorOrch, gStpOrch}; bool initialize_dtel = false; if (platform == BFN_PLATFORM_SUBSTRING || platform == VS_PLATFORM_SUBSTRING) diff --git a/orchagent/orchdaemon.h b/orchagent/orchdaemon.h index 2473848bf5..7c07e3dd92 100644 --- a/orchagent/orchdaemon.h +++ b/orchagent/orchdaemon.h @@ -51,6 +51,7 @@ #include "dash/dashorch.h" #include "dash/dashrouteorch.h" #include "dash/dashvnetorch.h" +#include "stporch.h" #include using namespace swss; diff --git a/orchagent/port.h b/orchagent/port.h index 0ae9b97b67..0e27b13dbd 100644 --- a/orchagent/port.h +++ b/orchagent/port.h @@ -74,6 +74,8 @@ struct SystemLagInfo int32_t spa_id = 0; }; +typedef std::map stp_port_ids_t; + class Port { public: @@ -143,6 +145,8 @@ class Port MacAddress m_mac; sai_object_id_t m_bridge_port_id = 0; // TODO: port could have multiple bridge port IDs sai_object_id_t m_bridge_port_admin_state = 0; // TODO: port could have multiple bridge port IDs + stp_port_ids_t m_stp_port_ids; //STP Port object ids for each STP instance + sai_int16_t m_stp_id = -1; //STP instance for the VLAN sai_vlan_id_t m_port_vlan_id = DEFAULT_PORT_VLAN_ID; // Port VLAN ID sai_object_id_t m_rif_id = 0; sai_object_id_t m_vr_id = 0; diff --git a/orchagent/portsorch.cpp b/orchagent/portsorch.cpp index 14b6d17847..61d7db0199 100644 --- a/orchagent/portsorch.cpp +++ b/orchagent/portsorch.cpp @@ -7,6 +7,7 @@ #include "directory.h" #include "subintf.h" #include "notifications.h" +#include "stporch.h" #include #include @@ -62,6 +63,7 @@ extern int32_t gVoqMySwitchId; extern string gMyHostName; extern string gMyAsicName; extern event_handle_t g_events_handle; +extern StpOrch *gStpOrch; // defines ------------------------------------------------------------------------------------------------------------ @@ -6179,6 +6181,8 @@ bool PortsOrch::removeBridgePort(Port &port) return false; } + gStpOrch->removeStpPorts(port); + //Flush the FDB entires corresponding to the port gFdbOrch->flushFDBEntries(port.m_bridge_port_id, SAI_NULL_OBJECT_ID); SWSS_LOG_INFO("Flush FDB entries for port %s", port.m_alias.c_str()); @@ -6319,6 +6323,12 @@ bool PortsOrch::removeVlan(Port vlan) SWSS_LOG_ERROR("Failed to remove VLAN %d host interface", vlan.m_vlan_info.vlan_id); return false; } + + /* If STP instance is associated with VLAN remove VLAN from STP before deletion */ + if(vlan.m_stp_id != -1) + { + gStpOrch->removeVlanFromStpInstance(vlan.m_alias, 0); + } sai_status_t status = sai_vlan_api->remove_vlan(vlan.m_vlan_info.vlan_oid); if (status != SAI_STATUS_SUCCESS) diff --git a/orchagent/saihelper.cpp b/orchagent/saihelper.cpp index 1265364d97..930b2a6894 100644 --- a/orchagent/saihelper.cpp +++ b/orchagent/saihelper.cpp @@ -85,6 +85,7 @@ sai_dash_vip_api_t* sai_dash_vip_api; sai_dash_direction_lookup_api_t* sai_dash_direction_lookup_api; sai_twamp_api_t* sai_twamp_api; sai_tam_api_t* sai_tam_api; +sai_stp_api_t* sai_stp_api; extern sai_object_id_t gSwitchId; extern bool gTraditionalFlexCounter; @@ -232,6 +233,7 @@ void initSaiApi() sai_api_query((sai_api_t)SAI_API_DASH_DIRECTION_LOOKUP, (void**)&sai_dash_direction_lookup_api); sai_api_query(SAI_API_TWAMP, (void **)&sai_twamp_api); sai_api_query(SAI_API_TAM, (void **)&sai_tam_api); + sai_api_query(SAI_API_STP, (void **)&sai_stp_api); sai_log_set(SAI_API_SWITCH, SAI_LOG_LEVEL_NOTICE); sai_log_set(SAI_API_BRIDGE, SAI_LOG_LEVEL_NOTICE); diff --git a/orchagent/stporch.cpp b/orchagent/stporch.cpp new file mode 100644 index 0000000000..45132399f4 --- /dev/null +++ b/orchagent/stporch.cpp @@ -0,0 +1,576 @@ +#include +#include +#include "portsorch.h" +#include "logger.h" +#include "fdborch.h" +#include "stporch.h" + +extern sai_stp_api_t *sai_stp_api; +extern sai_vlan_api_t *sai_vlan_api; +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) : + Orch(db, tableNames) +{ + SWSS_LOG_ENTER(); + + sai_attribute_t attr; + sai_status_t status; + + 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) + { + SWSS_LOG_NOTICE("Failed to get default STP instance and max STP instances , rv:%d", status); + } + + m_defaultStpId = attrs[0].value.oid; + updateMaxStpInstance(attrs[1].value.u32); +}; + + +sai_object_id_t StpOrch::getStpInstanceOid(sai_uint16_t stp_instance) +{ + std::map::iterator it; + + it = m_stpInstToOid.find(stp_instance); + if (it == m_stpInstToOid.end()) + { + return SAI_NULL_OBJECT_ID; + } + + return it->second; +} + +sai_object_id_t StpOrch::addStpInstance(sai_uint16_t stp_instance) +{ + sai_object_id_t stp_oid = 0; + sai_attribute_t attr; + + 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_NOTICE("Added STP instance:%hu oid:%lx", stp_instance, stp_oid); + return stp_oid; +} + +bool StpOrch::removeStpInstance(sai_uint16_t stp_instance) +{ + sai_object_id_t stp_oid; + + stp_oid = getStpInstanceOid(stp_instance); + if (stp_oid == SAI_NULL_OBJECT_ID) + { + SWSS_LOG_ERROR("Failed to find oid for STP instance %u", stp_instance); + return false; + } + + /* Remove all STP ports before deleting the STP instance */ + auto portList = gPortsOrch->getAllPorts(); + for (auto &it: portList) + { + auto &port = it.second; + if (port.m_type == Port::PHY || port.m_type == Port::LAG) + { + if(port.m_stp_port_ids.find(stp_instance) == port.m_stp_port_ids.end()) + continue; + + removeStpPort(port, stp_instance); + } + } + + sai_status_t status = sai_stp_api->remove_stp(stp_oid); + if (status != SAI_STATUS_SUCCESS) + { + SWSS_LOG_ERROR("Failed to remove STP instance %u oid %lx status %u", stp_instance, stp_oid, status); + return false; + } + + m_stpInstToOid.erase(stp_instance); + SWSS_LOG_NOTICE("Removed STP instance:%hu oid:%lx", stp_instance, stp_oid); + return true; +} + +bool StpOrch::addVlanToStpInstance(string vlan_alias, sai_uint16_t stp_instance) +{ + SWSS_LOG_ENTER(); + + Port vlan; + sai_object_id_t stp_oid; + sai_attribute_t attr; + + if (!gPortsOrch->getPort(vlan_alias, vlan)) + { + SWSS_LOG_INFO("Failed to locate VLAN %s", vlan_alias.c_str()); + return false; + } + + stp_oid = getStpInstanceOid(stp_instance); + if (stp_oid == SAI_NULL_OBJECT_ID) + { + stp_oid = addStpInstance(stp_instance); + if(stp_oid == SAI_NULL_OBJECT_ID) + { + SWSS_LOG_NOTICE("stp_oid is NULL OBJECT ID"); + return false; + } + } + + attr.id = SAI_VLAN_ATTR_STP_INSTANCE; + attr.value.oid = stp_oid; + + 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 STP instance :%hu and stp oid:%lu to VLAN %s status %u",stp_instance, stp_oid, vlan_alias.c_str(), status); + return false; + } + + vlan.m_stp_id = stp_instance; + gPortsOrch->setPort(vlan_alias, vlan); + SWSS_LOG_NOTICE("Add VLAN %s to STP instance:%hu m_stp_id:%d", vlan_alias.c_str(), stp_instance, vlan.m_stp_id); + return true; +} + +bool StpOrch::removeVlanFromStpInstance(string vlan_alias, sai_uint16_t stp_instance) +{ + SWSS_LOG_ENTER(); + + Port vlan; + sai_attribute_t attr; + + if (!gPortsOrch->getPort(vlan_alias, vlan)) + { + SWSS_LOG_INFO("Failed to locate VLAN %s", vlan_alias.c_str()); + return false; + } + + attr.id = SAI_VLAN_ATTR_STP_INSTANCE; + attr.value.oid = m_defaultStpId; + + 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 remove VLAN %s to STP instance:%d status %u", vlan_alias.c_str(), vlan.m_stp_id, status); + } + + SWSS_LOG_NOTICE("Remove %s from instance:%d add instance:%lx", vlan_alias.c_str(), vlan.m_stp_id, m_defaultStpId); + + removeStpInstance(vlan.m_stp_id); + vlan.m_stp_id = -1; + gPortsOrch->setPort(vlan_alias, vlan); + return true; +} + +/* If STP Port exists return else create a new STP Port */ +sai_object_id_t StpOrch::addStpPort(Port &port, sai_uint16_t stp_instance) +{ + sai_object_id_t stp_port_id = SAI_NULL_OBJECT_ID; + sai_object_id_t stp_id = SAI_NULL_OBJECT_ID; + sai_attribute_t attr[3]; + + if(port.m_stp_port_ids.find(stp_instance) != port.m_stp_port_ids.end()) + { + return port.m_stp_port_ids[stp_instance]; + } + + if(port.m_bridge_port_id == SAI_NULL_OBJECT_ID) + { + gPortsOrch->addBridgePort(port); + + if(port.m_bridge_port_id == SAI_NULL_OBJECT_ID) + { + SWSS_LOG_ERROR("Failed to add STP port %s invalid bridge port id STP instance %d", port.m_alias.c_str(), stp_instance); + return SAI_NULL_OBJECT_ID; + } + } + + 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) + { + stp_id = addStpInstance(stp_instance); + if(stp_id == SAI_NULL_OBJECT_ID) + { + SWSS_LOG_ERROR("Failed to add STP instance %d for port %s", stp_instance, port.m_alias.c_str()); + return SAI_NULL_OBJECT_ID; + } + } + + 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; + + sai_status_t status = sai_stp_api->create_stp_port(&stp_port_id, gSwitchId, 3, attr); + if (status != SAI_STATUS_SUCCESS) + { + SWSS_LOG_ERROR("Failed to add STP port %s instance %d status %u", port.m_alias.c_str(), stp_instance, status); + return SAI_NULL_OBJECT_ID; + } + + SWSS_LOG_NOTICE("Add STP port %s instance %d oid %lx size %lu", port.m_alias.c_str(), stp_instance, stp_port_id, port.m_stp_port_ids.size()); + + port.m_stp_port_ids[stp_instance] = stp_port_id; + gPortsOrch->setPort(port.m_alias, port); + return stp_port_id; +} + +bool StpOrch::removeStpPort(Port &port, sai_uint16_t stp_instance) +{ + if(port.m_stp_port_ids.find(stp_instance) == port.m_stp_port_ids.end()) + { + /* Deletion could have already happened as part of other flows, so ignore this msg*/ + return true; + } + + 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 %lx status %x", port.m_alias.c_str(), stp_instance, + port.m_stp_port_ids[stp_instance], status); + return false; + } + + SWSS_LOG_NOTICE("Remove STP port %s instance %d oid %lx size %lu", 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); + return true; +} + +bool StpOrch::removeStpPorts(Port &port) +{ + if(port.m_stp_port_ids.empty()) + return true; + + for(auto stp_port_id: port.m_stp_port_ids) + { + uint16_t stp_instance = stp_port_id.first; + sai_object_id_t stp_port_oid = stp_port_id.second; + + if(stp_port_oid == SAI_NULL_OBJECT_ID) + { + SWSS_LOG_ERROR("Failed to find STP port %s instance %d", port.m_alias.c_str(), stp_instance); + continue; + } + + sai_status_t status = sai_stp_api->remove_stp_port(stp_port_oid); + if (status != SAI_STATUS_SUCCESS) + { + SWSS_LOG_ERROR("Failed to remove STP port %s instance %d oid %lx status %x", port.m_alias.c_str(), stp_instance, stp_port_oid, status); + } + else + { + SWSS_LOG_NOTICE("Remove STP port %s instance %d oid %lx", port.m_alias.c_str(), stp_instance, stp_port_oid); + } + } + + port.m_stp_port_ids.clear(); + gPortsOrch->setPort(port.m_alias, port); + return true; +} + +sai_stp_port_state_t StpOrch::getStpSaiState(sai_uint8_t stp_state) +{ + sai_stp_port_state_t state = SAI_STP_PORT_STATE_BLOCKING; + + switch(stp_state) + { + case STP_STATE_DISABLED: + case STP_STATE_BLOCKING: + case STP_STATE_LISTENING: + state = SAI_STP_PORT_STATE_BLOCKING; + break; + + case STP_STATE_LEARNING: + state = SAI_STP_PORT_STATE_LEARNING; + break; + + case STP_STATE_FORWARDING: + state = SAI_STP_PORT_STATE_FORWARDING; + break; + } + return state; +} + +bool StpOrch::updateStpPortState(Port &port, sai_uint16_t stp_instance, sai_uint8_t stp_state) +{ + sai_attribute_t attr[1]; + sai_object_id_t stp_port_oid; + + stp_port_oid = addStpPort(port, stp_instance); + + SWSS_LOG_NOTICE("in updateStpPortState"); + if (stp_port_oid == SAI_NULL_OBJECT_ID) + { + SWSS_LOG_ERROR("Failed to get STP port oid port %s instance %d state %d ", port.m_alias.c_str(), stp_instance, stp_state); + return true; + } + + attr[0].id = SAI_STP_PORT_ATTR_STATE; + attr[0].value.u32 = getStpSaiState(stp_state); + + sai_status_t status = sai_stp_api->set_stp_port_attribute(stp_port_oid, attr); + if (status != SAI_STATUS_SUCCESS) + { + 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_NOTICE("Set STP port state %s instance %d state %d ", port.m_alias.c_str(), stp_instance, stp_state); + + return true; +} + +bool StpOrch::stpVlanFdbFlush(string vlan_alias) +{ + SWSS_LOG_ENTER(); + + Port vlanPort; + if (vlan_alias.empty()) + { + SWSS_LOG_ERROR("Receive wrong vlan to flush fdb!"); + return false; + } + if (!gPortsOrch->getPort(vlan_alias, vlanPort)) + { + SWSS_LOG_ERROR("Failed to get VLAN port from VLAN %s", vlan_alias.c_str()); + return false; + } + + if (vlanPort.m_vlan_info.vlan_oid == SAI_NULL_OBJECT_ID) + { + SWSS_LOG_ERROR("NULL VLAN object for VLAN %s", vlan_alias.c_str()); + return false; + } + gFdbOrch->flushFDBEntries(SAI_NULL_OBJECT_ID, vlanPort.m_vlan_info.vlan_oid); + SWSS_LOG_NOTICE("Set STP FDB flush vlan %s ", vlan_alias.c_str()); + return true; +} + +bool StpOrch::updateMaxStpInstance(uint32_t max_stp_instances) +{ + SWSS_LOG_NOTICE("Max STP instances %d", (max_stp_instances - 1)); + + vector tuples; + FieldValueTuple tuple("max_stp_inst", to_string(max_stp_instances - 1)); + tuples.push_back(tuple); + m_stpTable->set("GLOBAL", tuples); + + return true; +} + +void StpOrch::doStpTask(Consumer &consumer) +{ + SWSS_LOG_ENTER(); + + auto it = consumer.m_toSync.begin(); + while (it != consumer.m_toSync.end()) + { + auto &t = it->second; + + SWSS_LOG_NOTICE("In doStpTask"); + string vlan_alias = kfvKey(t); + string op = kfvOp(t); + + if (op == SET_COMMAND) + { + string stp_instance; + uint16_t instance = STP_INVALID_INSTANCE; + + for (auto i : kfvFieldsValues(t)) + { + if (fvField(i) == "stp_instance") + { + stp_instance = fvValue(i); + instance = (uint16_t)std::stoi(stp_instance); + } + } + + if(instance == STP_INVALID_INSTANCE) + { + SWSS_LOG_ERROR("No instance found for VLAN %s", vlan_alias.c_str()); + } + else + { + if(!addVlanToStpInstance(vlan_alias, instance)) + { + it++; + continue; + } + } + } + else if (op == DEL_COMMAND) + { + if(!removeVlanFromStpInstance(vlan_alias, 0)) + { + it++; + continue; + } + } + else + { + SWSS_LOG_ERROR("Unknown operation type %s", op.c_str()); + } + it = consumer.m_toSync.erase(it); + } +} + +void StpOrch::doStpPortStateTask(Consumer &consumer) +{ + SWSS_LOG_ENTER(); + + auto it = consumer.m_toSync.begin(); + while (it != consumer.m_toSync.end()) + { + auto &t = it->second; + string key = kfvKey(t); + size_t found = key.find(':'); + SWSS_LOG_NOTICE("in doSTpPortState"); + /* Return if the format of key is wrong */ + if (found == string::npos) + { + SWSS_LOG_ERROR("Failed to parse %s", key.c_str()); + return; + } + string port_alias = key.substr(0, found); + string stp_instance = key.substr(found+1); + uint16_t instance = (uint16_t)std::stoi(stp_instance); + Port port; + + if (!gPortsOrch->getPort(port_alias, port)) + { + SWSS_LOG_ERROR("Failed to get port for %s alias", port_alias.c_str()); + return; + } + + string op = kfvOp(t); + + if (op == SET_COMMAND) + { + string stp_state; + uint8_t state = STP_STATE_INVALID; + + for (auto i : kfvFieldsValues(t)) + { + if (fvField(i) == "state") + { + stp_state = fvValue(i); + state = (uint8_t)std::stoi(stp_state); + } + } + + if(state == STP_STATE_INVALID) + { + SWSS_LOG_ERROR("No stp state found for instance %u port %s", instance, port_alias.c_str()); + } + else + { + if(!updateStpPortState(port, instance, state)) + { + it++; + continue; + } + } + } + else if (op == DEL_COMMAND) + { + if(!removeStpPort(port, instance)) + { + it++; + continue; + } + } + else + { + SWSS_LOG_ERROR("Unknown operation type %s", op.c_str()); + } + it = consumer.m_toSync.erase(it); + } +} + +void StpOrch::doStpFastageTask(Consumer &consumer) +{ + SWSS_LOG_ENTER(); + + auto it = consumer.m_toSync.begin(); + while (it != consumer.m_toSync.end()) + { + auto &t = it->second; + + string vlan_alias = kfvKey(t); + + string op = kfvOp(t); + + if (op == SET_COMMAND) + { + string state; + for (auto i : kfvFieldsValues(t)) + { + if (fvField(i) == "state") + state = fvValue(i); + } + + if(state.compare("true") == 0) + { + stpVlanFdbFlush(vlan_alias); + } + } + else if (op == DEL_COMMAND) + { + //no op + } + else + { + SWSS_LOG_ERROR("Unknown operation type %s", op.c_str()); + } + it = consumer.m_toSync.erase(it); + } +} + +void StpOrch::doTask(Consumer &consumer) +{ + SWSS_LOG_ENTER(); + + if (!gPortsOrch->allPortsReady()) + { + SWSS_LOG_NOTICE("doTask Return all ports not ready"); + return; + } + + string table_name = consumer.getTableName(); + if (table_name == APP_STP_VLAN_INSTANCE_TABLE_NAME) + { + doStpTask(consumer); + } + else if (table_name == APP_STP_PORT_STATE_TABLE_NAME) + { + doStpPortStateTask(consumer); + } + else if (table_name == APP_STP_FASTAGEING_FLUSH_TABLE_NAME) + { + doStpFastageTask(consumer); + } +} diff --git a/orchagent/stporch.h b/orchagent/stporch.h new file mode 100644 index 0000000000..60df8a0213 --- /dev/null +++ b/orchagent/stporch.h @@ -0,0 +1,53 @@ +#ifndef SWSS_STPORCH_H +#define SWSS_STPORCH_H + +#include +#include +#include "orch.h" + +#define STP_INVALID_INSTANCE 0xFFFF + +typedef enum _stp_state +{ + STP_STATE_DISABLED = 0, + STP_STATE_BLOCKING = 1, + STP_STATE_LISTENING = 2, + STP_STATE_LEARNING = 3, + STP_STATE_FORWARDING = 4, + STP_STATE_INVALID = 5 +}stp_state; + + +class StpOrch : public Orch +{ +public: + StpOrch(DBConnector *db, DBConnector *stateDb, vector &tableNames); + bool stpVlanFdbFlush(string vlan_alias); + bool updateMaxStpInstance(uint32_t max_stp_instance); + bool removeStpPorts(Port &port); + bool removeVlanFromStpInstance(string vlan, sai_uint16_t stp_instance); + bool stpVlanPortFdbFlush(string port_alias, string vlan_alias); + +private: + unique_ptr
m_stpTable; + std::map m_stpInstToOid;//Mapping from STP instance id to corresponding object id + sai_object_id_t m_defaultStpId; + + void doStpTask(Consumer &consumer); + void doStpPortStateTask(Consumer &consumer); + void doStpFastageTask(Consumer &consumer); + void doStpVlanIntfFlushTask(Consumer &consumer); + + sai_object_id_t addStpInstance(sai_uint16_t stp_instance); + bool removeStpInstance(sai_uint16_t stp_instance); + bool addVlanToStpInstance(string vlan, sai_uint16_t stp_instance); + sai_object_id_t getStpInstanceOid(sai_uint16_t stp_instance); + + sai_object_id_t addStpPort(Port &port, sai_uint16_t stp_instance); + bool removeStpPort(Port &port, sai_uint16_t stp_instance); + sai_stp_port_state_t getStpSaiState(sai_uint8_t stp_state); + bool updateStpPortState(Port &port, sai_uint16_t stp_instance, sai_uint8_t stp_state); + + void doTask(Consumer& consumer); +}; +#endif /* SWSS_STPORCH_H */ diff --git a/swssconfig/sample/00-copp.config.json b/swssconfig/sample/00-copp.config.json new file mode 100644 index 0000000000..e2c660891c --- /dev/null +++ b/swssconfig/sample/00-copp.config.json @@ -0,0 +1,111 @@ +[ + { + "COPP_TABLE:default": { + "queue": "0", + "meter_type":"packets", + "mode":"sr_tcm", + "cir":"600", + "cbs":"600", + "red_action":"drop" + }, + "OP": "SET" + }, + { + "COPP_TABLE:trap.group.bfd.lacp.udld.lldp": { + "trap_ids": "bfd,bfdv6,lacp,udld,lldp", + "trap_action":"trap", + "trap_priority":"7", + "queue": "7", + "meter_type":"packets", + "mode":"sr_tcm", + "cir":"6000", + "cbs":"6000", + "red_action":"drop" + }, + "OP": "SET" + }, + { + "COPP_TABLE:trap.group.stp.pvrst": { + "trap_ids": "stp,pvrst", + "trap_action":"trap", + "trap_priority":"6", + "queue": "6", + "meter_type":"packets", + "mode":"sr_tcm", + "cir":"16000", + "cbs":"16000", + "red_action":"drop" + }, + "OP": "SET" + }, + { + "COPP_TABLE:trap.group.vrrp": { + "trap_ids": "vrrp", + "trap_action":"trap", + "trap_priority":"5", + "queue": "5", + "meter_type":"packets", + "mode":"sr_tcm", + "cir":"6000", + "cbs":"6000", + "red_action":"drop" + }, + "OP": "SET" + }, + { + "COPP_TABLE:trap.group.bgp": { + "trap_ids": "bgp,bgpv6", + "trap_action":"trap", + "trap_priority":"4", + "queue": "4", + "meter_type":"packets", + "mode":"sr_tcm", + "cir":"10000", + "cbs":"10000", + "red_action":"drop" + }, + "OP": "SET" + }, + { + "COPP_TABLE:trap.group.arp": { + "trap_ids": "arp_req,arp_resp,neigh_discovery", + "trap_action":"copy", + "trap_priority":"3", + "queue": "3", + "meter_type":"packets", + "mode":"sr_tcm", + "cir":"6000", + "cbs":"6000", + "red_action":"drop" + }, + "OP": "SET" + }, + { + "COPP_TABLE:trap.group.ip2me.dhcp": { + "trap_ids": "ip2me,dhcp,dhcpv6", + "trap_action":"trap", + "trap_priority":"2", + "queue": "2", + "meter_type":"packets", + "mode":"sr_tcm", + "cir":"6000", + "cbs":"6000", + "red_action":"drop" + }, + "OP": "SET" + }, + { + "COPP_TABLE:trap.group.l3mtu": { + "trap_ids": "l3_mtu_error", + "trap_action":"trap", + "trap_priority":"1", + "queue": "1", + "meter_type":"packets", + "mode":"sr_tcm", + "cir":"600", + "cbs":"600", + "red_action":"drop" + }, + "OP": "SET" + } +] diff --git a/tests/mock_tests/Makefile.am b/tests/mock_tests/Makefile.am index c3a305b1eb..8a052e6e8c 100644 --- a/tests/mock_tests/Makefile.am +++ b/tests/mock_tests/Makefile.am @@ -136,7 +136,8 @@ tests_SOURCES = aclorch_ut.cpp \ $(top_srcdir)/warmrestart/warmRestartAssist.cpp \ $(top_srcdir)/orchagent/dash/pbutils.cpp \ $(top_srcdir)/cfgmgr/coppmgr.cpp \ - $(top_srcdir)/orchagent/twamporch.cpp + $(top_srcdir)/orchagent/twamporch.cpp \ + $(top_srcdir)/orchagent/stporch.cpp tests_SOURCES += $(FLEX_CTR_DIR)/flex_counter_manager.cpp $(FLEX_CTR_DIR)/flex_counter_stat_manager.cpp $(FLEX_CTR_DIR)/flow_counter_handler.cpp $(FLEX_CTR_DIR)/flowcounterrouteorch.cpp tests_SOURCES += $(DEBUG_CTR_DIR)/debug_counter.cpp $(DEBUG_CTR_DIR)/drop_counter.cpp