diff --git a/cfgmgr/portmgr.cpp b/cfgmgr/portmgr.cpp index 765198833a5d..94a1994310f7 100644 --- a/cfgmgr/portmgr.cpp +++ b/cfgmgr/portmgr.cpp @@ -55,6 +55,17 @@ bool PortMgr::setPortAdminStatus(const string &alias, const bool up) return true; } +bool PortMgr::setPortLearnMode(const string &alias, const string &learn_mode) +{ + // Set the port MAC learn mode in application database + vector fvs; + FieldValueTuple fv("learn_mode", learn_mode); + fvs.push_back(fv); + m_appPortTable.set(alias, fvs); + + return true; +} + bool PortMgr::isPortStateOk(const string &alias) { vector temp; @@ -91,7 +102,7 @@ void PortMgr::doTask(Consumer &consumer) continue; } - string admin_status, mtu; + string admin_status, mtu, learn_mode; bool configured = (m_portList.find(alias) != m_portList.end()); @@ -116,6 +127,10 @@ void PortMgr::doTask(Consumer &consumer) { admin_status = fvValue(i); } + else if (fvField(i) == "learn_mode") + { + learn_mode = fvValue(i); + } } if (!mtu.empty()) @@ -129,6 +144,12 @@ void PortMgr::doTask(Consumer &consumer) setPortAdminStatus(alias, admin_status == "up"); SWSS_LOG_NOTICE("Configure %s admin status to %s", alias.c_str(), admin_status.c_str()); } + + if (!learn_mode.empty()) + { + setPortLearnMode(alias, learn_mode); + SWSS_LOG_NOTICE("Configure %s MAC learn mode to %s", alias.c_str(), learn_mode.c_str()); + } } it = consumer.m_toSync.erase(it); diff --git a/cfgmgr/portmgr.h b/cfgmgr/portmgr.h index 971f00024c3a..a9724365a7f4 100644 --- a/cfgmgr/portmgr.h +++ b/cfgmgr/portmgr.h @@ -31,6 +31,7 @@ class PortMgr : public Orch void doTask(Consumer &consumer); bool setPortMtu(const std::string &alias, const std::string &mtu); bool setPortAdminStatus(const std::string &alias, const bool up); + bool setPortLearnMode(const std::string &alias, const std::string &learn_mode); bool isPortStateOk(const std::string &alias); }; diff --git a/cfgmgr/teammgr.cpp b/cfgmgr/teammgr.cpp index 2149e1ce5ff1..8071d44ada87 100644 --- a/cfgmgr/teammgr.cpp +++ b/cfgmgr/teammgr.cpp @@ -126,6 +126,7 @@ void TeamMgr::doLagTask(Consumer &consumer) bool fallback = false; string admin_status = DEFAULT_ADMIN_STATUS_STR; string mtu = DEFAULT_MTU_STR; + string learn_mode; for (auto i : kfvFieldsValues(t)) { @@ -153,6 +154,12 @@ void TeamMgr::doLagTask(Consumer &consumer) mtu = fvValue(i); SWSS_LOG_INFO("Get MTU %s", mtu.c_str()); } + else if (fvField(i) == "learn_mode") + { + learn_mode = fvValue(i); + SWSS_LOG_INFO("Get learn_mode %s", + learn_mode.c_str()); + } } if (m_lagList.find(alias) == m_lagList.end()) @@ -168,6 +175,11 @@ void TeamMgr::doLagTask(Consumer &consumer) setLagAdminStatus(alias, admin_status); setLagMtu(alias, mtu); + if (!learn_mode.empty()) + { + setLagLearnMode(alias, learn_mode); + SWSS_LOG_NOTICE("Configure %s MAC learn mode to %s", alias.c_str(), learn_mode.c_str()); + } } else if (op == DEL_COMMAND) { @@ -365,6 +377,17 @@ bool TeamMgr::setLagMtu(const string &alias, const string &mtu) return true; } +bool TeamMgr::setLagLearnMode(const string &alias, const string &learn_mode) +{ + // Set the port MAC learn mode in application database + vector fvs; + FieldValueTuple fv("learn_mode", learn_mode); + fvs.push_back(fv); + m_appLagTable.set(alias, fvs); + + return true; +} + task_process_status TeamMgr::addLag(const string &alias, int min_links, bool fallback) { SWSS_LOG_ENTER(); diff --git a/cfgmgr/teammgr.h b/cfgmgr/teammgr.h index e0e0410db1b9..db3b0338443a 100644 --- a/cfgmgr/teammgr.h +++ b/cfgmgr/teammgr.h @@ -44,6 +44,7 @@ class TeamMgr : public Orch bool setLagAdminStatus(const std::string &alias, const std::string &admin_status); bool setLagMtu(const std::string &alias, const std::string &mtu); + bool setLagLearnMode(const std::string &alias, const std::string &learn_mode); bool isPortEnslaved(const std::string &); bool findPortMaster(std::string &, const std::string &); diff --git a/orchagent/port.h b/orchagent/port.h index 8950985523f8..fac1cae2b60f 100644 --- a/orchagent/port.h +++ b/orchagent/port.h @@ -72,6 +72,7 @@ class Port int m_index = 0; // PHY_PORT: index uint32_t m_mtu = DEFAULT_MTU; uint32_t m_speed = 0; // Mbps + std::string m_learn_mode = "hardware"; bool m_autoneg = false; bool m_admin_state_up = false; sai_object_id_t m_port_id = 0; diff --git a/orchagent/portsorch.cpp b/orchagent/portsorch.cpp index 16ea7099e85d..586a81f755bb 100644 --- a/orchagent/portsorch.cpp +++ b/orchagent/portsorch.cpp @@ -60,6 +60,16 @@ static map pfc_asym_map = { "off", SAI_PORT_PRIORITY_FLOW_CONTROL_MODE_COMBINED } }; +static map learn_mode_map = +{ + { "drop", SAI_BRIDGE_PORT_FDB_LEARNING_MODE_DROP }, + { "disable", SAI_BRIDGE_PORT_FDB_LEARNING_MODE_DISABLE }, + { "hardware", SAI_BRIDGE_PORT_FDB_LEARNING_MODE_HW }, + { "cpu_trap", SAI_BRIDGE_PORT_FDB_LEARNING_MODE_CPU_TRAP}, + { "cpu_log", SAI_BRIDGE_PORT_FDB_LEARNING_MODE_CPU_LOG}, + { "notification", SAI_BRIDGE_PORT_FDB_LEARNING_MODE_FDB_NOTIFICATION} +}; + const vector portStatIds = { SAI_PORT_STAT_IF_IN_OCTETS, @@ -1671,6 +1681,7 @@ void PortsOrch::doPortTask(Consumer &consumer) string pfc_asym; uint32_t mtu = 0; uint32_t speed = 0; + string learn_mode; int an = -1; for (auto i : kfvFieldsValues(t)) @@ -1713,6 +1724,12 @@ void PortsOrch::doPortTask(Consumer &consumer) fec_mode = fvValue(i); } + /* Get port fdb learn mode*/ + if (fvField(i) == "learn_mode") + { + learn_mode = fvValue(i); + } + /* Set port asymmetric PFC */ if (fvField(i) == "pfc_asym") pfc_asym = fvValue(i); @@ -1988,6 +2005,32 @@ void PortsOrch::doPortTask(Consumer &consumer) } } + if (!learn_mode.empty() && (p.m_learn_mode != learn_mode)) + { + if (p.m_bridge_port_id != SAI_NULL_OBJECT_ID) + { + if(setBridgePortLearnMode(p, learn_mode)) + { + p.m_learn_mode = learn_mode; + m_portList[alias] = p; + SWSS_LOG_NOTICE("Set port %s learn mode to %s", alias.c_str(), learn_mode.c_str()); + } + else + { + SWSS_LOG_ERROR("Failed to set port %s learn mode to %s", alias.c_str(), learn_mode.c_str()); + it++; + continue; + } + } + else + { + p.m_learn_mode = learn_mode; + m_portList[alias] = p; + + SWSS_LOG_NOTICE("Saved to set port %s learn mode %s", alias.c_str(), learn_mode.c_str()); + } + } + if (pfc_asym != "") { if (setPortPfcAsym(p, pfc_asym)) @@ -2298,13 +2341,19 @@ void PortsOrch::doLagTask(Consumer &consumer) { // Retrieve attributes uint32_t mtu = 0; + string learn_mode; + for (auto i : kfvFieldsValues(t)) { if (fvField(i) == "mtu") { mtu = (uint32_t)stoul(fvValue(i)); } - if (fvField(i) == "oper_status") + else if (fvField(i) == "learn_mode") + { + learn_mode = fvValue(i); + } + else if (fvField(i) == "oper_status") { if (fvValue(i) == "down") { @@ -2344,6 +2393,32 @@ void PortsOrch::doLagTask(Consumer &consumer) gIntfsOrch->setRouterIntfsMtu(l); } } + + if (!learn_mode.empty() && (l.m_learn_mode != learn_mode)) + { + if (l.m_bridge_port_id != SAI_NULL_OBJECT_ID) + { + if(setBridgePortLearnMode(l, learn_mode)) + { + l.m_learn_mode = learn_mode; + m_portList[alias] = l; + SWSS_LOG_NOTICE("Set port %s learn mode to %s", alias.c_str(), learn_mode.c_str()); + } + else + { + SWSS_LOG_ERROR("Failed to set port %s learn mode to %s", alias.c_str(), learn_mode.c_str()); + it++; + continue; + } + } + else + { + l.m_learn_mode = learn_mode; + m_portList[alias] = l; + + SWSS_LOG_NOTICE("Saved to set port %s learn mode %s", alias.c_str(), learn_mode.c_str()); + } + } } it = consumer.m_toSync.erase(it); @@ -2758,7 +2833,15 @@ bool PortsOrch::addBridgePort(Port &port) /* And with hardware FDB learning mode set to HW (explicit default value) */ attr.id = SAI_BRIDGE_PORT_ATTR_FDB_LEARNING_MODE; - attr.value.s32 = SAI_BRIDGE_PORT_FDB_LEARNING_MODE_HW; + auto found = learn_mode_map.find(port.m_learn_mode); + if (found == learn_mode_map.end()) + { + attr.value.s32 = SAI_BRIDGE_PORT_FDB_LEARNING_MODE_HW; + } + else + { + attr.value.s32 = found->second; + } attrs.push_back(attr); sai_status_t status = sai_bridge_api->create_bridge_port(&port.m_bridge_port_id, gSwitchId, (uint32_t)attrs.size(), attrs.data()); @@ -2829,6 +2912,40 @@ bool PortsOrch::removeBridgePort(Port &port) return true; } +bool PortsOrch::setBridgePortLearnMode(Port &port, string learn_mode) +{ + SWSS_LOG_ENTER(); + + if (port.m_bridge_port_id == SAI_NULL_OBJECT_ID) + { + return true; + } + + auto found = learn_mode_map.find(learn_mode); + if (found == learn_mode_map.end()) + { + SWSS_LOG_ERROR("Incorrect MAC learn mode: %s", learn_mode.c_str()); + return false; + } + + /* Set bridge port learning mode */ + sai_attribute_t attr; + attr.id = SAI_BRIDGE_PORT_ATTR_FDB_LEARNING_MODE; + attr.value.s32 = found->second; + + sai_status_t status = sai_bridge_api->set_bridge_port_attribute(port.m_bridge_port_id, &attr); + if (status != SAI_STATUS_SUCCESS) + { + SWSS_LOG_ERROR("Failed to set bridge port %s learning mode, rv:%d", + port.m_alias.c_str(), status); + return false; + } + + SWSS_LOG_NOTICE("Set bridge port %s learning mode %s", port.m_alias.c_str(), learn_mode.c_str()); + + return true; +} + bool PortsOrch::addVlan(string vlan_alias) { SWSS_LOG_ENTER(); diff --git a/orchagent/portsorch.h b/orchagent/portsorch.h index 0fe3bad48021..6f314d6bffe7 100644 --- a/orchagent/portsorch.h +++ b/orchagent/portsorch.h @@ -133,9 +133,9 @@ class PortsOrch : public Orch, public Subject map, tuple> m_lanesAliasSpeedMap; map m_portList; map m_port_ref_count; - unordered_set m_pendingPortSet; + NotificationConsumer* m_portStatusNotificationConsumer; void doTask(Consumer &consumer); @@ -159,6 +159,7 @@ class PortsOrch : public Orch, public Subject bool addBridgePort(Port &port); bool removeBridgePort(Port &port); + bool setBridgePortLearnMode(Port &port, string learn_mode); bool addVlan(string vlan); bool removeVlan(Port vlan); diff --git a/tests/test_port_mac_learn.py b/tests/test_port_mac_learn.py new file mode 100644 index 000000000000..2abf4291ac78 --- /dev/null +++ b/tests/test_port_mac_learn.py @@ -0,0 +1,181 @@ +from swsscommon import swsscommon + +import time +import os + +class TestPortMacLearn(object): + def setup_db(self, dvs): + self.pdb = swsscommon.DBConnector(swsscommon.APPL_DB, dvs.redis_sock, 0) + self.adb = swsscommon.DBConnector(swsscommon.ASIC_DB, dvs.redis_sock, 0) + self.cdb = swsscommon.DBConnector(swsscommon.CONFIG_DB, dvs.redis_sock, 0) + self.cntdb = swsscommon.DBConnector(swsscommon.COUNTERS_DB, dvs.redis_sock, 0) + + def get_learn_mode_map(self): + learn_mode_map = { "drop": "SAI_BRIDGE_PORT_FDB_LEARNING_MODE_DROP", + "disable": "SAI_BRIDGE_PORT_FDB_LEARNING_MODE_DISABLE", + "hardware": "SAI_BRIDGE_PORT_FDB_LEARNING_MODE_HW", + "cpu_trap": "SAI_BRIDGE_PORT_FDB_LEARNING_MODE_CPU_TRAP", + "cpu_log": "SAI_BRIDGE_PORT_FDB_LEARNING_MODE_CPU_LOG", + "notification": "SAI_BRIDGE_PORT_FDB_LEARNING_MODE_FDB_NOTIFICATION"} + return learn_mode_map + + def get_port_oid(self, port_name): + port_map_tbl = swsscommon.Table(self.cntdb, 'COUNTERS_PORT_NAME_MAP') + for k in port_map_tbl.get('')[1]: + if k[0] == port_name: + return k[1] + return None + + def get_bridge_port_oid(self, port_oid): + tbl = swsscommon.Table(self.adb, "ASIC_STATE:SAI_OBJECT_TYPE_BRIDGE_PORT") + for key in tbl.getKeys(): + status, data = tbl.get(key) + assert status + values = dict(data) + if port_oid == values["SAI_BRIDGE_PORT_ATTR_PORT_ID"]: + return key + return None + + def check_learn_mode_in_appdb(self, table, interface, learn_mode): + (status, fvs) = table.get(interface) + assert status == True + for fv in fvs: + if fv[0] == "learn_mode": + if fv[1] == learn_mode: + return True + return False + + def check_learn_mode_in_asicdb(self, interface_oid, learn_mode): + # Get bridge port oid + bridge_port_oid = self.get_bridge_port_oid(interface_oid) + assert bridge_port_oid is not None + + tbl = swsscommon.Table(self.adb, "ASIC_STATE:SAI_OBJECT_TYPE_BRIDGE_PORT") + (status, fvs) = tbl.get(bridge_port_oid) + assert status == True + values = dict(fvs) + if values["SAI_BRIDGE_PORT_ATTR_FDB_LEARNING_MODE"] == learn_mode: + return True + else: + return False + + def test_PortMacLearnMode(self, dvs, testlog): + self.setup_db(dvs) + + # create vlan + tbl = swsscommon.Table(self.cdb, "VLAN") + fvs = swsscommon.FieldValuePairs([("vlanid", "2")]) + tbl.set("Vlan2", fvs) + time.sleep(1) + + # create vlan member entry in application db + tbl = swsscommon.Table(self.cdb, "VLAN_MEMBER") + fvs = swsscommon.FieldValuePairs([("tagging_mode", "untagged")]) + tbl.set("Vlan2|Ethernet8", fvs) + time.sleep(1) + + # get port oid + port_oid = self.get_port_oid("Ethernet8") + assert port_oid is not None + + # check asicdb before setting mac learn mode; The default learn_mode value is SAI_BRIDGE_PORT_FDB_LEARNING_MODE_HW. + status = self.check_learn_mode_in_asicdb(port_oid, "SAI_BRIDGE_PORT_FDB_LEARNING_MODE_HW") + assert status == True + + learn_mode_map = self.get_learn_mode_map() + for key, value in learn_mode_map.items(): + # set MAC learn mode to port + tbl = swsscommon.Table(self.cdb, "PORT") + fvs = swsscommon.FieldValuePairs([("learn_mode", key)]) + tbl.set("Ethernet8", fvs) + time.sleep(1) + + # check application database + tbl = swsscommon.Table(self.pdb, "PORT_TABLE") + status = self.check_learn_mode_in_appdb(tbl, "Ethernet8", key) + assert status == True + + # check ASIC bridge port database + status = self.check_learn_mode_in_asicdb(port_oid, value) + assert status == True + + # set default learn mode for Ethernet8 + tbl = swsscommon.Table(self.cdb, "PORT") + fvs = swsscommon.FieldValuePairs([("learn_mode", "hardware")]) + tbl.set("Ethernet8", fvs) + time.sleep(1) + + # remove vlan member + tbl = swsscommon.Table(self.cdb, "VLAN_MEMBER") + tbl._del("Vlan2|Ethernet8") + time.sleep(1) + + # remove vlan + tbl = swsscommon.Table(self.cdb, "VLAN") + tbl._del("Vlan2") + time.sleep(1) + + def test_PortchannelMacLearnMode(self, dvs, testlog): + self.setup_db(dvs) + + #create portchannel + tbl = swsscommon.Table(self.cdb, "PORTCHANNEL") + fvs = swsscommon.FieldValuePairs([("admin_status", "up"), + ("mtu", "9100")]) + tbl.set("PortChannel001", fvs) + time.sleep(1) + + # create vlan + tbl = swsscommon.Table(self.cdb, "VLAN") + fvs = swsscommon.FieldValuePairs([("vlanid", "3")]) + tbl.set("Vlan3", fvs) + time.sleep(1) + + # create vlan member entry in application db + tbl = swsscommon.Table(self.cdb, "VLAN_MEMBER") + fvs = swsscommon.FieldValuePairs([("tagging_mode", "untagged")]) + tbl.set("Vlan3|PortChannel001", fvs) + time.sleep(1) + + # get PortChannel oid; When sonic-swss pr885 is complete, you can get oid directly from COUNTERS_LAG_NAME_MAP, which would be better. + lag_tbl = swsscommon.Table(self.adb, "ASIC_STATE:SAI_OBJECT_TYPE_LAG") + lag_entries = lag_tbl.getKeys() + # At this point there should be only one lag in the system, which is PortChannel001. + assert len(lag_entries) == 1 + lag_oid = lag_entries[0] + + # check asicdb before setting mac learn mode; The default learn_mode value is SAI_BRIDGE_PORT_FDB_LEARNING_MODE_HW. + status = self.check_learn_mode_in_asicdb(lag_oid, "SAI_BRIDGE_PORT_FDB_LEARNING_MODE_HW") + assert status == True + + learn_mode_map = self.get_learn_mode_map() + for key, value in learn_mode_map.items(): + # set mac learn mode to PortChannel + tbl = swsscommon.Table(self.cdb, "PORTCHANNEL") + fvs = swsscommon.FieldValuePairs([("learn_mode", key)]) + tbl.set("PortChannel001", fvs) + time.sleep(1) + + # check application database + tbl = swsscommon.Table(self.pdb, "LAG_TABLE") + status = self.check_learn_mode_in_appdb(tbl, "PortChannel001", key) + assert status == True + + # check ASIC bridge port database + status = self.check_learn_mode_in_asicdb(lag_oid, value) + assert status == True + + # remove vlan member + tbl = swsscommon.Table(self.cdb, "VLAN_MEMBER") + tbl._del("Vlan3|PortChannel001") + time.sleep(1) + + # create vlan + tbl = swsscommon.Table(self.cdb, "VLAN") + tbl._del("Vlan3") + time.sleep(1) + + # remove PortChannel + tbl = swsscommon.Table(self.cdb, "PORTCHANNEL") + tbl._del("PortChannel001") + time.sleep(1)