diff --git a/doc/swss-schema.md b/doc/swss-schema.md index 898b3ea5a69e..c93c05fee1ed 100644 --- a/doc/swss-schema.md +++ b/doc/swss-schema.md @@ -169,6 +169,24 @@ and reflects the LAG ports into the redis under: `LAG_TABLE::port` neigh = 12HEXDIG ; mac address of the neighbor family = "IPv4" / "IPv6" ; address family +--------------------------------------------- +###FDB_TABLE + + ; Stores FDB entries which were inserted into SAI state manually + ; Notes: + ; - only unicast FDB entries supported + ; - only Vlan interfaces are supported + key = FDB_TABLE:"Vlan"vlanid:mac_address ; mac address will be inserted to FDB for the vlan interface + port = ifName ; interface where the entry is bound to + type = "static" / "dynamic" ; type of the entry + + Example: + 127.0.0.1:6379> hgetall FDB_TABLE:Vlan2:52-54-00-25-06-E9 + 1) "port" + 2) "Ethernet0" + 3) "type" + 4) "static" + --------------------------------------------- ###QUEUE_TABLE diff --git a/orchagent/fdborch.cpp b/orchagent/fdborch.cpp index f0f353a82b4b..7124e2b00bc9 100644 --- a/orchagent/fdborch.cpp +++ b/orchagent/fdborch.cpp @@ -1,7 +1,9 @@ #include #include +#include #include "logger.h" +#include "tokenize.h" #include "fdborch.h" extern sai_fdb_api_t *sai_fdb_api; @@ -19,15 +21,21 @@ void FdbOrch::update(sai_fdb_event_t type, const sai_fdb_entry_t* entry, sai_obj case SAI_FDB_EVENT_LEARNED: if (!m_portsOrch->getPort(portOid, update.port)) { - SWSS_LOG_ERROR("Failed to get port for %lu OID\n", portOid); + SWSS_LOG_ERROR("Failed to get port for %lu OID", portOid); return; } update.add = true; + + (void)m_entries.insert(update.entry); + SWSS_LOG_DEBUG("FdbOrch notification: mac %s was inserted into vlan %d", update.entry.mac.to_string().c_str(), entry->vlan_id); break; case SAI_FDB_EVENT_AGED: case SAI_FDB_EVENT_FLUSHED: update.add = false; + + (void)m_entries.erase(update.entry); + SWSS_LOG_DEBUG("FdbOrch notification: mac %s was removed from vlan %d", update.entry.mac.to_string().c_str(), entry->vlan_id); break; } @@ -51,15 +59,247 @@ bool FdbOrch::getPort(const MacAddress& mac, uint16_t vlan, Port& port) sai_status_t status = sai_fdb_api->get_fdb_entry_attribute(&entry, 1, &attr); if (status != SAI_STATUS_SUCCESS) { - SWSS_LOG_INFO("Failed to get port for FDB entry OID\n"); + SWSS_LOG_ERROR("Failed to get port for FDB entry OID"); return false; } if (!m_portsOrch->getPort(attr.value.oid, port)) { - SWSS_LOG_ERROR("Failed to get port for %lu OID\n", attr.value.oid); + SWSS_LOG_ERROR("Failed to get port for %lu OID", attr.value.oid); + return false; + } + + return true; +} + +void FdbOrch::doTask(Consumer& consumer) +{ + SWSS_LOG_ENTER(); + + auto it = consumer.m_toSync.begin(); + while (it != consumer.m_toSync.end()) + { + KeyOpFieldsValuesTuple t = it->second; + + string key = kfvKey(t); + string op = kfvOp(t); + + FdbEntry entry; + if (!splitKey(key, entry)) + { + it = consumer.m_toSync.erase(it); + continue; + } + + if (op == SET_COMMAND) + { + string port; + string type; + bool port_defined = false; + bool type_defined = false; + + for (auto i : kfvFieldsValues(t)) + { + if (fvField(i) == "port") + { + port = fvValue(i); + port_defined = true; + } + + if (fvField(i) == "type") + { + type = fvValue(i); + type_defined = true; + } + } + + if (!port_defined) + { + SWSS_LOG_ERROR("FDB entry with key:'%s' must have 'port' attribute", key.c_str()); + it = consumer.m_toSync.erase(it); + continue; + } + + if (!type_defined) + { + SWSS_LOG_ERROR("FDB entry with key:'%s' must have 'type' attribute", key.c_str()); + it = consumer.m_toSync.erase(it); + continue; + } + + // check that type either static or dynamic + if (type != "static" && type != "dynamic") + { + SWSS_LOG_ERROR("FDB entry with key: '%s' has type '%s'. But allowed only types: 'static' or 'dynamic'", + key.c_str(), type.c_str()); + it = consumer.m_toSync.erase(it); + continue; + } + + if (addFdbEntry(entry, port, type)) + it = consumer.m_toSync.erase(it); + else + it++; + + // Remove AppDb entry if FdbEntry type == 'dynamic' + if (type == "dynamic") + m_table.del(key); + } + else if (op == DEL_COMMAND) + { + if (removeFdbEntry(entry)) + it = consumer.m_toSync.erase(it); + else + it++; + + } + else + { + SWSS_LOG_ERROR("Unknown operation type %s", op.c_str()); + it = consumer.m_toSync.erase(it); + } + } +} + +bool FdbOrch::addFdbEntry(const FdbEntry& entry, const string& port_name, const string& type) +{ + SWSS_LOG_ENTER(); + + SWSS_LOG_DEBUG("mac=%s, vlan=%d. port_name %s. type %s", + entry.mac.to_string().c_str(), entry.vlan, port_name.c_str(), type.c_str()); + + if (m_entries.count(entry) != 0) // we already have such entries + { + // FIXME: should we check that the entry are moving to another port? + // FIXME: should we check that the entry are changing its type? + SWSS_LOG_ERROR("FDB entry already exists. mac=%s vlan=%d", entry.mac.to_string().c_str(), entry.vlan); + return true; + } + + sai_status_t status; + + sai_fdb_entry_t fdb_entry; + memcpy(fdb_entry.mac_address, entry.mac.getMac(), sizeof(sai_mac_t)); + fdb_entry.vlan_id = entry.vlan; + + Port port; + if (!m_portsOrch->getPort(port_name, port)) + { + SWSS_LOG_ERROR("Failed to get port id for %s", port_name.c_str()); + return true; + } + + sai_attribute_t attr; + vector attrs; + + attr.id = SAI_FDB_ENTRY_ATTR_TYPE; + attr.value.s32 = (type == "dynamic") ? SAI_FDB_ENTRY_DYNAMIC : SAI_FDB_ENTRY_STATIC; + attrs.push_back(attr); + + attr.id = SAI_FDB_ENTRY_ATTR_PORT_ID; + attr.value.oid = port.m_port_id; + attrs.push_back(attr); + + attr.id = SAI_FDB_ENTRY_ATTR_PACKET_ACTION; + attr.value.s32 = SAI_PACKET_ACTION_FORWARD; + attrs.push_back(attr); + + status = sai_fdb_api->create_fdb_entry(&fdb_entry, attrs.size(), attrs.data()); + if (status != SAI_STATUS_SUCCESS) + { + SWSS_LOG_ERROR("Failed to add FDB entry. mac=%s, vlan=%d. port_name %s. type %s", + entry.mac.to_string().c_str(), entry.vlan, port_name.c_str(), type.c_str()); + return true; + } + + (void)m_entries.insert(entry); + + return true; +} + +bool FdbOrch::removeFdbEntry(const FdbEntry& entry) +{ + SWSS_LOG_ENTER(); + + SWSS_LOG_DEBUG("mac=%s, vlan=%d", entry.mac.to_string().c_str(), entry.vlan); + + if (m_entries.count(entry) == 0) + { + SWSS_LOG_ERROR("FDB entry isn't found. mac=%s vlan=%d", entry.mac.to_string().c_str(), entry.vlan); + return true; + } + + sai_status_t status; + sai_fdb_entry_t fdb_entry; + memcpy(fdb_entry.mac_address, entry.mac.getMac(), sizeof(sai_mac_t)); + fdb_entry.vlan_id = entry.vlan; + + status = sai_fdb_api->remove_fdb_entry(&fdb_entry); + if (status != SAI_STATUS_SUCCESS) + { + SWSS_LOG_ERROR("Failed to remove FDB entry. mac=%s, vlan=%d", + entry.mac.to_string().c_str(), entry.vlan); + return true; + } + + (void)m_entries.erase(entry); + + return true; +} + +bool FdbOrch::splitKey(const string& key, FdbEntry& entry) +{ + SWSS_LOG_ENTER(); + + string mac_address_str; + string vlan_str; + + auto fields = tokenize(key, ':'); + + if (fields.size() < 2 || fields.size() > 2) + { + SWSS_LOG_ERROR("Failed to parse key: %s", key.c_str()); + return false; + } + + vlan_str = fields[0]; + mac_address_str = fields[1]; + + if (vlan_str.length() <= 4) // "Vlan" + { + SWSS_LOG_ERROR("Failed to extract vlan interface name from the key: %s", key.c_str()); + return false; + } + + uint8_t mac_array[6]; + if (!MacAddress::parseMacString(mac_address_str, mac_array)) + { + SWSS_LOG_ERROR("Failed to parse mac address: %s in key: %s", mac_address_str.c_str(), key.c_str()); + return false; + } + + if (mac_array[0] & 0x01) + { + SWSS_LOG_ERROR("Mac address %s in key %s should be unicast", mac_address_str.c_str(), key.c_str()); + return false; + } + + entry.mac = MacAddress(mac_array); + + Port port; + if (!m_portsOrch->getPort(vlan_str, port)) + { + SWSS_LOG_ERROR("Failed to get port for %s", vlan_str.c_str()); + return false; + } + + if (port.m_type != Port::VLAN) + { + SWSS_LOG_ERROR("Port %s from key %s must be a vlan port", vlan_str.c_str(), key.c_str()); return false; } + entry.vlan = port.m_vlan_id; + return true; } diff --git a/orchagent/fdborch.h b/orchagent/fdborch.h index 77d864a0ce9a..822dd172b587 100644 --- a/orchagent/fdborch.h +++ b/orchagent/fdborch.h @@ -8,7 +8,12 @@ struct FdbEntry { MacAddress mac; - uint16_t vlan; + sai_vlan_id_t vlan; + + bool operator<(const FdbEntry& other) const + { + return tie(mac, vlan) < tie(other.mac, other.vlan); + } }; struct FdbUpdate @@ -18,11 +23,13 @@ struct FdbUpdate bool add; }; -class FdbOrch: public Subject +class FdbOrch: public Orch, public Subject { public: - FdbOrch(PortsOrch *port) : - m_portsOrch(port) + FdbOrch(DBConnector *db, string tableName, PortsOrch *port) : + Orch(db, tableName), + m_portsOrch(port), + m_table(Table(m_db, tableName)) { } @@ -31,6 +38,14 @@ class FdbOrch: public Subject private: PortsOrch *m_portsOrch; + set m_entries; + Table m_table; + + void doTask(Consumer& consumer); + + bool addFdbEntry(const FdbEntry&, const string&, const string&); + bool removeFdbEntry(const FdbEntry&); + bool splitKey(const string&, FdbEntry&); }; #endif /* SWSS_FDBORCH_H */ diff --git a/orchagent/orchdaemon.cpp b/orchagent/orchdaemon.cpp index 785031edb1d5..493e037e3368 100644 --- a/orchagent/orchdaemon.cpp +++ b/orchagent/orchdaemon.cpp @@ -41,7 +41,7 @@ bool OrchDaemon::init() }; gPortsOrch = new PortsOrch(m_applDb, ports_tables); - gFdbOrch = new FdbOrch(gPortsOrch); + gFdbOrch = new FdbOrch(m_applDb, APP_FDB_TABLE_NAME, gPortsOrch); IntfsOrch *intfs_orch = new IntfsOrch(m_applDb, APP_INTF_TABLE_NAME); NeighOrch *neigh_orch = new NeighOrch(m_applDb, APP_NEIGH_TABLE_NAME, intfs_orch); RouteOrch *route_orch = new RouteOrch(m_applDb, APP_ROUTE_TABLE_NAME, neigh_orch); @@ -79,7 +79,7 @@ bool OrchDaemon::init() }; AclOrch *acl_orch = new AclOrch(m_applDb, acl_tables, gPortsOrch, mirror_orch); - m_orchList = { gPortsOrch, intfs_orch, neigh_orch, route_orch, copp_orch, tunnel_decap_orch, qos_orch, buffer_orch, mirror_orch, acl_orch}; + m_orchList = { gPortsOrch, intfs_orch, neigh_orch, route_orch, copp_orch, tunnel_decap_orch, qos_orch, buffer_orch, mirror_orch, acl_orch, gFdbOrch}; m_select = new Select(); return true;