From acb5c7e9368decdf63b9ac422801c098c5783f92 Mon Sep 17 00:00:00 2001 From: Pankaj Jain Date: Tue, 28 Apr 2020 14:06:35 -0700 Subject: [PATCH] [Orchagent]: FdbOrch changes for EVPN VXLAN Signed-off-by: Pankaj Jain --- orchagent/fdborch.cpp | 1010 ++++++++++++++++++++++++++++++++------ orchagent/fdborch.h | 62 ++- orchagent/orchdaemon.cpp | 8 +- tests/test_evpn_fdb.py | 680 +++++++++++++++++++++++++ 4 files changed, 1611 insertions(+), 149 deletions(-) create mode 100644 tests/test_evpn_fdb.py diff --git a/orchagent/fdborch.cpp b/orchagent/fdborch.cpp index 9a54ba98f1b..1bd6a171fd4 100644 --- a/orchagent/fdborch.cpp +++ b/orchagent/fdborch.cpp @@ -11,23 +11,30 @@ #include "crmorch.h" #include "notifier.h" #include "sai_serialize.h" +#include "vxlanorch.h" +#include "directory.h" extern sai_fdb_api_t *sai_fdb_api; extern sai_object_id_t gSwitchId; extern PortsOrch* gPortsOrch; extern CrmOrch * gCrmOrch; +extern Directory gDirectory; -const int fdborch_pri = 20; +const int FdbOrch::fdborch_pri = 20; -FdbOrch::FdbOrch(TableConnector applDbConnector, TableConnector stateDbConnector, PortsOrch *port) : - Orch(applDbConnector.first, applDbConnector.second, fdborch_pri), +FdbOrch::FdbOrch(DBConnector* applDbConnector, vector appFdbTables, TableConnector stateDbFdbConnector, PortsOrch *port) : + Orch(applDbConnector, appFdbTables), m_portsOrch(port), - m_table(applDbConnector.first, applDbConnector.second), - m_fdbStateTable(stateDbConnector.first, stateDbConnector.second) + m_fdbStateTable(stateDbFdbConnector.first, stateDbFdbConnector.second) { + for(auto it: appFdbTables) + { + m_appTables.push_back(new Table(applDbConnector, it.first)); + } + m_portsOrch->attach(this); - m_flushNotificationsConsumer = new NotificationConsumer(applDbConnector.first, "FLUSHFDBREQUEST"); + m_flushNotificationsConsumer = new NotificationConsumer(applDbConnector, "FLUSHFDBREQUEST"); auto flushNotifier = new Notifier(m_flushNotificationsConsumer, this, "FLUSHFDBREQUEST"); Orch::addExecutor(flushNotifier); @@ -57,6 +64,8 @@ bool FdbOrch::bake() bool FdbOrch::storeFdbEntryState(const FdbUpdate& update) { const FdbEntry& entry = update.entry; + FdbData fdbdata; + FdbData oldFdbData; const Port& port = update.port; const MacAddress& mac = entry.mac; string portName = port.m_alias; @@ -73,28 +82,55 @@ bool FdbOrch::storeFdbEntryState(const FdbUpdate& update) if (update.add) { - auto inserted = m_entries.insert(entry); + bool mac_move = false; + auto it = m_entries.find(entry); + if (it != m_entries.end()) + { + /* This block is specifically added for MAC_MOVE event + and not expected to be executed for LEARN event + */ + if (port.m_bridge_port_id == it->second.bridge_port_id) + { + SWSS_LOG_INFO("FdbOrch notification: mac %s is duplicate", entry.mac.to_string().c_str()); + return false; + } + mac_move = true; + oldFdbData = it->second; + } + + fdbdata.bridge_port_id = update.port.m_bridge_port_id; + fdbdata.type = update.type; + fdbdata.origin = FDB_ORIGIN_LEARN; + fdbdata.remote_ip = ""; + fdbdata.esi = ""; + fdbdata.vni = 0; + m_entries[entry] = fdbdata; SWSS_LOG_DEBUG("FdbOrch notification: mac %s was inserted into bv_id 0x%" PRIx64, entry.mac.to_string().c_str(), entry.bv_id); - - if (!inserted.second) - { - SWSS_LOG_INFO("FdbOrch notification: mac %s is duplicate", entry.mac.to_string().c_str()); - return false; - } + SWSS_LOG_DEBUG("m_entries size=%lu mac=%s port=0x%" PRIx64, + m_entries.size(), entry.mac.to_string().c_str(), m_entries[entry].bridge_port_id); // Write to StateDb std::vector fvs; fvs.push_back(FieldValueTuple("port", portName)); - fvs.push_back(FieldValueTuple("type", "dynamic")); + fvs.push_back(FieldValueTuple("type", update.type)); m_fdbStateTable.set(key, fvs); - gCrmOrch->incCrmResUsedCounter(CrmResourceType::CRM_FDB_ENTRY); + if (!mac_move) + { + gCrmOrch->incCrmResUsedCounter(CrmResourceType::CRM_FDB_ENTRY); + } return true; } else { + auto it= m_entries.find(entry); + if(it != m_entries.end()) + { + oldFdbData = it->second; + } + size_t erased = m_entries.erase(entry); SWSS_LOG_DEBUG("FdbOrch notification: mac %s was removed from bv_id 0x%" PRIx64, entry.mac.to_string().c_str(), entry.bv_id); @@ -103,8 +139,11 @@ bool FdbOrch::storeFdbEntryState(const FdbUpdate& update) return false; } - // Remove in StateDb - m_fdbStateTable.del(key); + if (oldFdbData.origin != FDB_ORIGIN_VXLAN_ADVERTIZED) + { + // Remove in StateDb for non advertised mac addresses + m_fdbStateTable.del(key); + } gCrmOrch->decCrmResUsedCounter(CrmResourceType::CRM_FDB_ENTRY); return true; @@ -118,85 +157,243 @@ void FdbOrch::update(sai_fdb_event_t type, const sai_fdb_entry_t* entry, sai_obj FdbUpdate update; update.entry.mac = entry->mac_address; update.entry.bv_id = entry->bv_id; + update.type = "dynamic"; + Port vlan; switch (type) { case SAI_FDB_EVENT_LEARNED: + { + SWSS_LOG_INFO("Received LEARN event for bvid=0x%" PRIx64 "mac=%s port=0x%" PRIx64, entry->bv_id, update.entry.mac.to_string().c_str(), bridge_port_id); + + if (!m_portsOrch->getPort(entry->bv_id, vlan)) + { + SWSS_LOG_ERROR("FdbOrch LEARN notification: Failed to locate vlan port from bv_id 0x%" PRIx64, entry->bv_id); + return; + } + if (!m_portsOrch->getPortByBridgePortId(bridge_port_id, update.port)) { - SWSS_LOG_ERROR("Failed to get port by bridge port ID 0x%" PRIx64, bridge_port_id); + SWSS_LOG_ERROR("FdbOrch LEARN notification: Failed to get port by bridge port ID 0x%" PRIx64, bridge_port_id); return; } // we already have such entries - if (m_entries.find(update.entry) != m_entries.end()) + auto existing_entry = m_entries.find(update.entry); + if (existing_entry != m_entries.end()) { - SWSS_LOG_INFO("FdbOrch notification: mac %s is already in bv_id 0x%" PRIx64, - update.entry.mac.to_string().c_str(), entry->bv_id); + SWSS_LOG_INFO("FdbOrch LEARN notification: mac %s is already in bv_id 0x%" + PRIx64 "existing-bp 0x%" PRIx64 "new-bp:0x%" PRIx64, + update.entry.mac.to_string().c_str(), entry->bv_id, existing_entry->second.bridge_port_id, bridge_port_id); break; } update.add = true; + update.type = "dynamic"; + update.port.m_fdb_count++; + m_portsOrch->setPort(update.port.m_alias, update.port); + vlan.m_fdb_count++; + m_portsOrch->setPort(vlan.m_alias, vlan); + storeFdbEntryState(update); + notify(SUBJECT_TYPE_FDB_CHANGE, &update); - for (auto observer: m_observers) + break; + } + case SAI_FDB_EVENT_AGED: + { + SWSS_LOG_INFO("Received AGE event for bvid=%lx mac=%s port=%lx", entry->bv_id, update.entry.mac.to_string().c_str(), bridge_port_id); + + if (!m_portsOrch->getPort(entry->bv_id, vlan)) { - observer->update(SUBJECT_TYPE_FDB_CHANGE, &update); + SWSS_LOG_NOTICE("FdbOrch AGE notification: Failed to locate vlan port from bv_id 0x%lx", entry->bv_id); } - break; + auto existing_entry = m_entries.find(update.entry); + // we don't have such entries + if (existing_entry == m_entries.end()) + { + SWSS_LOG_INFO("FdbOrch AGE notification: mac %s is not present in bv_id 0x%lx bp 0x%lx", + update.entry.mac.to_string().c_str(), entry->bv_id, bridge_port_id); + break; + } + + if (!m_portsOrch->getPortByBridgePortId(bridge_port_id, update.port)) + { + SWSS_LOG_NOTICE("FdbOrch AGE notification: Failed to get port by bridge port ID 0x%lx", bridge_port_id); + } + + if (existing_entry->second.bridge_port_id != bridge_port_id) + { + SWSS_LOG_NOTICE("FdbOrch AGE notification: Stale aging event received for mac-bv_id %s-0x%lx with bp=0x%lx existing bp=0x%lx", update.entry.mac.to_string().c_str(), entry->bv_id, bridge_port_id, existing_entry->second.bridge_port_id); + // We need to get the port for bridge-port in existing fdb + if (!m_portsOrch->getPortByBridgePortId(existing_entry->second.bridge_port_id, update.port)) + { + SWSS_LOG_NOTICE("FdbOrch AGE notification: Failed to get port by bridge port ID 0x%lx", existing_entry->second.bridge_port_id); + } + // dont return, let it delete just to bring SONiC and SAI in sync + // return; + } + + if (existing_entry->second.type == "static") + { + update.type = "static"; + + if (vlan.m_members.find(update.port.m_alias) == vlan.m_members.end()) + { + saved_fdb_entries[update.port.m_alias].push_back({existing_entry->first.mac, + vlan.m_vlan_info.vlan_id, update.type, + existing_entry->second.origin, + existing_entry->second.remote_ip, + existing_entry->second.esi, + existing_entry->second.vni}); + } + else + { + /*port added back to vlan before we receive delete + notification for flush from SAI. Re-add entry to SAI + */ + sai_attribute_t attr; + vector attrs; + + attr.id = SAI_FDB_ENTRY_ATTR_TYPE; + attr.value.s32 = SAI_FDB_ENTRY_TYPE_STATIC; + attrs.push_back(attr); + attr.id = SAI_FDB_ENTRY_ATTR_BRIDGE_PORT_ID; + attr.value.oid = bridge_port_id; + attrs.push_back(attr); + auto status = sai_fdb_api->create_fdb_entry(entry, (uint32_t)attrs.size(), attrs.data()); + if (status != SAI_STATUS_SUCCESS) + { + SWSS_LOG_ERROR("Failed to create FDB %s on %s, rv:%d", + existing_entry->first.mac.to_string().c_str(), update.port.m_alias.c_str(), status); + return; + } + return; + } + } - case SAI_FDB_EVENT_AGED: - case SAI_FDB_EVENT_MOVE: update.add = false; + if (!update.port.m_alias.empty()) + { + update.port.m_fdb_count--; + m_portsOrch->setPort(update.port.m_alias, update.port); + } + if (!vlan.m_alias.empty()) + { + vlan.m_fdb_count--; + m_portsOrch->setPort(vlan.m_alias, vlan); + } + storeFdbEntryState(update); - for (auto observer: m_observers) + notify(SUBJECT_TYPE_FDB_CHANGE, &update); + + notifyTunnelOrch(update.port); + break; + } + case SAI_FDB_EVENT_MOVE: + { + Port port_old; + auto existing_entry = m_entries.find(update.entry); + + SWSS_LOG_INFO("Received MOVE event for bvid=%lx mac=%s port=%lx", entry->bv_id, update.entry.mac.to_string().c_str(), bridge_port_id); + + if (!m_portsOrch->getPort(entry->bv_id, vlan)) { - observer->update(SUBJECT_TYPE_FDB_CHANGE, &update); + SWSS_LOG_ERROR("FdbOrch MOVE notification: Failed to locate vlan port from bv_id 0x%lx", entry->bv_id); + return; } - break; + if (!m_portsOrch->getPortByBridgePortId(bridge_port_id, update.port)) + { + SWSS_LOG_ERROR("FdbOrch MOVE notification: Failed to get port by bridge port ID 0x%lx", bridge_port_id); + return; + } + // We should already have such entry + if (existing_entry == m_entries.end()) + { + SWSS_LOG_WARN("FdbOrch MOVE notification: mac %s is not found in bv_id 0x%lx", + update.entry.mac.to_string().c_str(), entry->bv_id); + } + else if (!m_portsOrch->getPortByBridgePortId(existing_entry->second.bridge_port_id, port_old)) + { + SWSS_LOG_ERROR("FdbOrch MOVE notification: Failed to get port by bridge port ID 0x%lx", existing_entry->second.bridge_port_id); + return; + } + + update.add = true; + + if (!port_old.m_alias.empty()) + { + port_old.m_fdb_count--; + m_portsOrch->setPort(port_old.m_alias, port_old); + } + update.port.m_fdb_count++; + m_portsOrch->setPort(update.port.m_alias, update.port); + + storeFdbEntryState(update); + + notify(SUBJECT_TYPE_FDB_CHANGE, &update); + + notifyTunnelOrch(port_old); + + break; + } case SAI_FDB_EVENT_FLUSHED: - if (bridge_port_id == SAI_NULL_OBJECT_ID && entry->bv_id == SAI_NULL_OBJECT_ID) + SWSS_LOG_INFO("Received FLUSH event for bvid=%lx mac=%s port=%lx", entry->bv_id, update.entry.mac.to_string().c_str(), bridge_port_id); + for (auto itr = m_entries.begin(); itr != m_entries.end();) { - for (auto itr = m_entries.begin(); itr != m_entries.end();) + if ((itr->second.type == "static") || (itr->second.origin == FDB_ORIGIN_VXLAN_ADVERTIZED)) { - /* - TODO: here should only delete the dynamic fdb entries, - but unfortunately in structure FdbEntry currently have - no member to indicate the fdb entry type, - if there is static mac added, here will have issue. - */ - update.entry.mac = itr->mac; - update.entry.bv_id = itr->bv_id; - update.add = false; itr++; + continue; + } - storeFdbEntryState(update); + if (((bridge_port_id == SAI_NULL_OBJECT_ID) && (entry->bv_id == SAI_NULL_OBJECT_ID)) // Flush all DYNAMIC + || ((bridge_port_id == itr->second.bridge_port_id) && (entry->bv_id == SAI_NULL_OBJECT_ID)) // flush all DYN on a port + || ((bridge_port_id == SAI_NULL_OBJECT_ID) && (entry->bv_id == itr->first.bv_id))) // flush all DYN on a vlan + { - SWSS_LOG_DEBUG("FdbOrch notification: mac %s was removed", update.entry.mac.to_string().c_str()); + if (!m_portsOrch->getPortByBridgePortId(itr->second.bridge_port_id, update.port)) + { + SWSS_LOG_ERROR("FdbOrch FLUSH notification: Failed to get port by bridge port ID 0x%lx", itr->second.bridge_port_id); + } + + if (!m_portsOrch->getPort(itr->first.bv_id, vlan)) + { + SWSS_LOG_NOTICE("FdbOrch FLUSH notification: Failed to locate vlan port from bv_id 0x%lx", itr->first.bv_id); + } + + update.entry.mac = itr->first.mac; + update.entry.bv_id = itr->first.bv_id; + update.add = false; + itr++; - for (auto observer: m_observers) + if (!update.port.m_alias.empty()) { - observer->update(SUBJECT_TYPE_FDB_CHANGE, &update); + update.port.m_fdb_count--; + m_portsOrch->setPort(update.port.m_alias, update.port); } + if (!vlan.m_alias.empty()) + { + vlan.m_fdb_count--; + m_portsOrch->setPort(vlan.m_alias, vlan); + } + + /* This will invalidate the current iterator hence itr++ is done before */ + storeFdbEntryState(update); + + SWSS_LOG_DEBUG("FdbOrch FLUSH notification: mac %s was removed", update.entry.mac.to_string().c_str()); + + notify(SUBJECT_TYPE_FDB_CHANGE, &update); + notifyTunnelOrch(update.port); + } + else + { + itr++; } - } - else if (bridge_port_id && entry->bv_id == SAI_NULL_OBJECT_ID) - { - /*this is a placeholder for flush port fdb case, not supported yet.*/ - SWSS_LOG_ERROR("FdbOrch notification: not supported flush port fdb action, port_id = 0x%" PRIx64 ", bv_id = 0x%" PRIx64 ".", bridge_port_id, entry->bv_id); - } - else if (bridge_port_id == SAI_NULL_OBJECT_ID && entry->bv_id != SAI_NULL_OBJECT_ID) - { - /*this is a placeholder for flush vlan fdb case, not supported yet.*/ - SWSS_LOG_ERROR("FdbOrch notification: not supported flush vlan fdb action, port_id = 0x%" PRIx64 ", bv_id = 0x%" PRIx64 ".", bridge_port_id, entry->bv_id); - } - else - { - SWSS_LOG_ERROR("FdbOrch notification: not supported flush fdb action, port_id = 0x%" PRIx64 ", bv_id = 0x%" PRIx64 ".", bridge_port_id, entry->bv_id); } break; } @@ -226,6 +423,7 @@ void FdbOrch::update(SubjectType type, void *cntx) bool FdbOrch::getPort(const MacAddress& mac, uint16_t vlan, Port& port) { + sai_object_id_t bridge_port_id; SWSS_LOG_ENTER(); if (!m_portsOrch->getVlanByVlanId(vlan, port)) @@ -234,23 +432,20 @@ bool FdbOrch::getPort(const MacAddress& mac, uint16_t vlan, Port& port) return false; } - sai_fdb_entry_t entry; - entry.switch_id = gSwitchId; - memcpy(entry.mac_address, mac.getMac(), sizeof(sai_mac_t)); + FdbEntry entry; entry.bv_id = port.m_vlan_info.vlan_oid; - - sai_attribute_t attr; - attr.id = SAI_FDB_ENTRY_ATTR_BRIDGE_PORT_ID; - - sai_status_t status = sai_fdb_api->get_fdb_entry_attribute(&entry, 1, &attr); - if (status != SAI_STATUS_SUCCESS) + entry.mac = mac; + auto it= m_entries.find(entry); + if(it != m_entries.end()) + { + bridge_port_id = it->second.bridge_port_id; + } + else { - SWSS_LOG_ERROR("Failed to get bridge port ID for FDB entry %s, rv:%d", - mac.to_string().c_str(), status); return false; } - if (!m_portsOrch->getPortByBridgePortId(attr.value.oid, port)) + if (!m_portsOrch->getPortByBridgePortId(bridge_port_id, port)) { SWSS_LOG_ERROR("Failed to get port by bridge port ID 0x%" PRIx64, attr.value.oid); return false; @@ -268,6 +463,15 @@ void FdbOrch::doTask(Consumer& consumer) return; } + FdbOrigin origin = FDB_ORIGIN_PROVISIONED; + + string table_name = consumer.getTableName(); + if(table_name == APP_VXLAN_FDB_TABLE_NAME) + { + origin = FDB_ORIGIN_VXLAN_ADVERTIZED; + } + + auto it = consumer.m_toSync.begin(); while (it != consumer.m_toSync.end()) { @@ -281,7 +485,24 @@ void FdbOrch::doTask(Consumer& consumer) if (!m_portsOrch->getPort(keys[0], vlan)) { SWSS_LOG_INFO("Failed to locate %s", keys[0].c_str()); - it++; + if(op == DEL_COMMAND) + { + /* Delete if it is in saved_fdb_entry */ + unsigned short vlan_id; + try { + vlan_id = (unsigned short) stoi(keys[0].substr(4)); + } catch(exception &e) { + it = consumer.m_toSync.erase(it); + continue; + } + deleteFdbEntryFromSavedFDB(MacAddress(keys[1]), vlan_id, origin); + + it = consumer.m_toSync.erase(it); + } + else + { + it++; + } continue; } @@ -291,8 +512,12 @@ void FdbOrch::doTask(Consumer& consumer) if (op == SET_COMMAND) { - string port; - string type; + string port = ""; + string type = "dynamic"; + string remote_ip = ""; + string esi = ""; + unsigned int vni = 0; + string sticky = ""; for (auto i : kfvFieldsValues(t)) { @@ -305,27 +530,67 @@ void FdbOrch::doTask(Consumer& consumer) { type = fvValue(i); } + + if(origin == FDB_ORIGIN_VXLAN_ADVERTIZED) + { + if (fvField(i) == "remote_vtep") + { + remote_ip = fvValue(i); + // Creating an IpAddress object to validate if remote_ip is valid + // if invalid it will throw the exception and we will ignore the + // event + try { + IpAddress valid_ip = IpAddress(remote_ip); + (void)valid_ip; // To avoid g++ warning + } catch(exception &e) { + SWSS_LOG_NOTICE("Invalid IP address in remote MAC %s", remote_ip.c_str()); + remote_ip = ""; + break; + } + } + + if (fvField(i) == "esi") + { + esi = fvValue(i); + } + + if (fvField(i) == "vni") + { + try { + vni = (unsigned int) stoi(fvValue(i)); + } catch(exception &e) { + SWSS_LOG_INFO("Invalid VNI in remote MAC %s", fvValue(i).c_str()); + vni = 0; + break; + } + } + } } /* FDB type is either dynamic or static */ assert(type == "dynamic" || type == "static"); - if (addFdbEntry(entry, port, type)) + if(origin == FDB_ORIGIN_VXLAN_ADVERTIZED) + { + VxlanTunnelOrch* tunnel_orch = gDirectory.get(); + + if(!remote_ip.length()) + { + it = consumer.m_toSync.erase(it); + continue; + } + port = tunnel_orch->getTunnelPortName(remote_ip); + } + + + if (addFdbEntry(entry, port, type, origin, remote_ip, vni, esi)) it = consumer.m_toSync.erase(it); else it++; - - /* Remove corresponding APP_DB entry if type is 'dynamic' */ - // FIXME: The modification of table is not thread safe. - // Uncomment this after this issue is fixed. - // if (type == "dynamic") - // { - // m_table.del(kfvKey(t)); - // } } else if (op == DEL_COMMAND) { - if (removeFdbEntry(entry)) + if (removeFdbEntry(entry, origin)) it = consumer.m_toSync.erase(it); else it++; @@ -348,7 +613,6 @@ void FdbOrch::doTask(NotificationConsumer& consumer) return; } - sai_status_t status; std::string op; std::string data; std::vector values; @@ -359,28 +623,17 @@ void FdbOrch::doTask(NotificationConsumer& consumer) { if (op == "ALL") { - /* - * so far only support flush all the FDB entris - * flush per port and flush per vlan will be added later. - */ - status = sai_fdb_api->flush_fdb_entries(gSwitchId, 0, NULL); - if (status != SAI_STATUS_SUCCESS) - { - SWSS_LOG_ERROR("Flush fdb failed, return code %x", status); - } - + flushFdbAll(0); return; } else if (op == "PORT") { - /*place holder for flush port fdb*/ - SWSS_LOG_ERROR("Received unsupported flush port fdb request"); + flushFdbByPort(data, 0); return; } else if (op == "VLAN") { - /*place holder for flush vlan fdb*/ - SWSS_LOG_ERROR("Received unsupported flush vlan fdb request"); + flushFdbByVlan(data, 0); return; } else @@ -418,14 +671,29 @@ void FdbOrch::doTask(NotificationConsumer& consumer) void FdbOrch::updateVlanMember(const VlanMemberUpdate& update) { + Port port; + + string port_name = update.member.m_alias; + string vlan_name = update.vlan.m_alias; + SWSS_LOG_ENTER(); + if (!m_portsOrch->getPort(port_name, port)) + { + SWSS_LOG_ERROR("could not locate port from alias %s", port_name.c_str()); + fdb_dbg_cnt.common.fail_get_port++; + return; + } + if (!update.add) { - return; // we need additions only + if(port.m_type != Port::TUNNEL) + { + flushFdbByPortVlan(port_name, vlan_name, 1); + } + return; } - string port_name = update.member.m_alias; auto fdb_list = std::move(saved_fdb_entries[port_name]); if(!fdb_list.empty()) { @@ -433,93 +701,329 @@ void FdbOrch::updateVlanMember(const VlanMemberUpdate& update) { // try to insert an FDB entry. If the FDB entry is not ready to be inserted yet, // it would be added back to the saved_fdb_entries structure by addFDBEntry() - (void)addFdbEntry(fdb.entry, port_name, fdb.type); + if(fdb.vlanId == update.vlan.m_vlan_info.vlan_id) + { + FdbEntry entry; + entry.mac = fdb.mac; + entry.bv_id = update.vlan.m_vlan_info.vlan_oid; + (void)addFdbEntry(entry, port_name, fdb.type, fdb.origin, + fdb.remote_ip, fdb.vni, fdb.esi); + } + else + { + saved_fdb_entries[port_name].push_back(fdb); + } } } } -bool FdbOrch::addFdbEntry(const FdbEntry& entry, const string& port_name, const string& type) +bool FdbOrch::addFdbEntry(const FdbEntry& entry, const string& port_name, const string& type, FdbOrigin origin, const string& remote_ip, unsigned int vni, const string& esi) { - SWSS_LOG_ENTER(); + Port vlan; + Port port; - sai_fdb_entry_t fdb_entry; + SWSS_LOG_ENTER(); + SWSS_LOG_NOTICE("mac=%s bv_id=0x%lx port_name=%s type=%s origin=%d", entry.mac.to_string().c_str(), entry.bv_id, port_name.c_str(), type.c_str(), origin); - fdb_entry.switch_id = gSwitchId; - memcpy(fdb_entry.mac_address, entry.mac.getMac(), sizeof(sai_mac_t)); - fdb_entry.bv_id = entry.bv_id; + if (!m_portsOrch->getPort(entry.bv_id, vlan)) + { + SWSS_LOG_NOTICE("addFdbEntry: Failed to locate vlan port from bv_id 0x%lx", entry.bv_id); + return false; + } - Port port; /* Retry until port is created */ - if (!m_portsOrch->getPort(port_name, port)) + if (!m_portsOrch->getPort(port_name, port) || (port.m_bridge_port_id == SAI_NULL_OBJECT_ID)) { - SWSS_LOG_DEBUG("Saving a fdb entry until port %s becomes active", port_name.c_str()); - saved_fdb_entries[port_name].push_back({entry, type}); + SWSS_LOG_INFO("Saving a fdb entry until port %s becomes active", port_name.c_str()); + saved_fdb_entries[port_name].push_back({entry.mac, + vlan.m_vlan_info.vlan_id, type, origin, remote_ip, esi, vni}); return true; } - /* Retry until port is added to the VLAN */ - if (!port.m_bridge_port_id) + /* Retry until port is member of vlan*/ + if (vlan.m_members.find(port_name) == vlan.m_members.end()) { - SWSS_LOG_DEBUG("Saving a fdb entry until port %s has got a bridge port ID", port_name.c_str()); - saved_fdb_entries[port_name].push_back({entry, type}); + SWSS_LOG_INFO("Saving a fdb entry until port %s becomes vlan %s member", port_name.c_str(), vlan.m_alias.c_str()); + saved_fdb_entries[port_name].push_back({entry.mac, + vlan.m_vlan_info.vlan_id, type, origin, remote_ip, esi, vni}); return true; } + sai_status_t status; + sai_fdb_entry_t fdb_entry; + fdb_entry.switch_id = gSwitchId; + memcpy(fdb_entry.mac_address, entry.mac.getMac(), sizeof(sai_mac_t)); + fdb_entry.bv_id = entry.bv_id; + + Port oldPort; + string oldType; + FdbOrigin oldOrigin = FDB_ORIGIN_INVALID ; + bool macUpdate = false; + auto it = m_entries.find(entry); + if(it != m_entries.end()) + { + /* get existing port and type */ + oldType = it->second.type; + oldOrigin = it->second.origin; + + if (!m_portsOrch->getPortByBridgePortId(it->second.bridge_port_id, oldPort)) + { + SWSS_LOG_ERROR("Existing port 0x%lx details not found", it->second.bridge_port_id); + return false; + } + + if((oldOrigin == origin) && (oldType == type) && (port.m_bridge_port_id == it->second.bridge_port_id)) + { + /* Duplicate Mac */ + SWSS_LOG_NOTICE("FdbOrch: mac=%s %s port=%s type=%s origin=%d is duplicate", entry.mac.to_string().c_str(), vlan.m_alias.c_str(), port_name.c_str(), type.c_str(), origin); + return true; + } + else if(origin != oldOrigin) + { + /* Mac origin has changed */ + if((oldType == "static") && (oldOrigin == FDB_ORIGIN_PROVISIONED)) + { + /* old mac was static and provisioned, it can not be changed by Remote Mac */ + SWSS_LOG_NOTICE("Already existing static MAC:%s in Vlan:%d. " + "Received same MAC from peer:%s; " + "Peer mac ignored", + entry.mac.to_string().c_str(), vlan.m_vlan_info.vlan_id, + remote_ip.c_str()); + + return true; + } + else if((oldType == "static") && (oldOrigin == FDB_ORIGIN_VXLAN_ADVERTIZED) && (type == "dynamic")) + { + /* old mac was static and received from remote, it can not be changed by dynamic locally provisioned Mac */ + SWSS_LOG_NOTICE("Already existing static MAC:%s in Vlan:%d " + "from Peer:%s. Now same is provisioned as dynamic; " + "Provisioned dynamic mac is ignored", + entry.mac.to_string().c_str(), vlan.m_vlan_info.vlan_id, + it->second.remote_ip.c_str()); + return true; + } + else if(oldOrigin == FDB_ORIGIN_VXLAN_ADVERTIZED) + { + if((oldType == "static") && (type == "static")) + { + SWSS_LOG_WARN("You have just overwritten existing static MAC:%s " + "in Vlan:%d from Peer:%s, " + "If it is a mistake, it will result in inconsistent Traffic Forwarding", + entry.mac.to_string().c_str(), + vlan.m_vlan_info.vlan_id, + it->second.remote_ip.c_str()); + } + } + } + else /* (origin == oldOrigin) */ + { + /* Mac origin is same, all changes are allowed */ + /* Allowed + * Bridge-port is changed or/and + * Sticky bit from remote is modified or + * provisioned mac is converted from static<-->dynamic + */ + } + + macUpdate = true; + } + sai_attribute_t attr; vector attrs; attr.id = SAI_FDB_ENTRY_ATTR_TYPE; - attr.value.s32 = (type == "dynamic") ? SAI_FDB_ENTRY_TYPE_DYNAMIC : SAI_FDB_ENTRY_TYPE_STATIC; + if (origin == FDB_ORIGIN_VXLAN_ADVERTIZED) + { + attr.value.s32 = (type == "dynamic") ? SAI_FDB_ENTRY_TYPE_STATIC_MACMOVE : SAI_FDB_ENTRY_TYPE_STATIC; + } + else + { + attr.value.s32 = (type == "dynamic") ? SAI_FDB_ENTRY_TYPE_DYNAMIC : SAI_FDB_ENTRY_TYPE_STATIC; + } attrs.push_back(attr); attr.id = SAI_FDB_ENTRY_ATTR_BRIDGE_PORT_ID; attr.value.oid = port.m_bridge_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); - - if (m_entries.count(entry) != 0) // we already have such entries + if(origin == FDB_ORIGIN_VXLAN_ADVERTIZED) { - removeFdbEntry(entry); + IpAddress remote = IpAddress(remote_ip); + sai_ip_address_t ipaddr; + if(remote.isV4()) + { + ipaddr.addr_family = SAI_IP_ADDR_FAMILY_IPV4; + ipaddr.addr.ip4 = remote.getV4Addr(); + } + else + { + ipaddr.addr_family = SAI_IP_ADDR_FAMILY_IPV6; + memcpy(ipaddr.addr.ip6, remote.getV6Addr(), sizeof(ipaddr.addr.ip6)); + } + attr.id = SAI_FDB_ENTRY_ATTR_ENDPOINT_IP; + attr.value.ipaddr = ipaddr; + attrs.push_back(attr); + } + else if(macUpdate && (oldOrigin == FDB_ORIGIN_VXLAN_ADVERTIZED) && (origin != oldOrigin)) + { + /* origin is changed from Remote-advertized to Local-provisioned + * Remove the end-point ip attribute from fdb entry + */ + sai_ip_address_t ipaddr; + ipaddr.addr_family = SAI_IP_ADDR_FAMILY_IPV4; + ipaddr.addr.ip4 = 0; + attr.id = SAI_FDB_ENTRY_ATTR_ENDPOINT_IP; + attr.value.ipaddr = ipaddr; + attrs.push_back(attr); } - sai_status_t status = sai_fdb_api->create_fdb_entry(&fdb_entry, (uint32_t)attrs.size(), attrs.data()); - if (status != SAI_STATUS_SUCCESS) + string key = "Vlan" + to_string(vlan.m_vlan_info.vlan_id) + ":" + entry.mac.to_string(); + + + if(macUpdate) { - SWSS_LOG_ERROR("Failed to create %s FDB %s on %s, rv:%d", - type.c_str(), entry.mac.to_string().c_str(), port_name.c_str(), status); - return false; //FIXME: it should be based on status. Some could be retried, some not + SWSS_LOG_NOTICE("MAC-Update FDB %s in %s on from-%s:to-%s from-%s:to-%s origin-%d-to-%d", + entry.mac.to_string().c_str(), vlan.m_alias.c_str(), oldPort.m_alias.c_str(), + port_name.c_str(), oldType.c_str(), type.c_str(), oldOrigin, origin); + for(auto itr : attrs) + { + status = sai_fdb_api->set_fdb_entry_attribute(&fdb_entry, &itr); + if (status != SAI_STATUS_SUCCESS) + { + SWSS_LOG_ERROR("macUpdate-Failed for attr.id=0x%x for FDB %s in %s on %s, rv:%d", + itr.id, entry.mac.to_string().c_str(), vlan.m_alias.c_str(), port_name.c_str(), status); + return false; + } + } + if (oldPort.m_bridge_port_id != port.m_bridge_port_id) + { + oldPort.m_fdb_count--; + m_portsOrch->setPort(oldPort.m_alias, oldPort); + port.m_fdb_count++; + m_portsOrch->setPort(port.m_alias, port); + } } + else + { + SWSS_LOG_NOTICE("MAC-Create %s FDB %s in %s on %s", type.c_str(), entry.mac.to_string().c_str(), vlan.m_alias.c_str(), port_name.c_str()); - SWSS_LOG_NOTICE("Create %s FDB %s on %s", type.c_str(), entry.mac.to_string().c_str(), port_name.c_str()); + status = sai_fdb_api->create_fdb_entry(&fdb_entry, (uint32_t)attrs.size(), attrs.data()); + if (status != SAI_STATUS_SUCCESS) + { + SWSS_LOG_ERROR("Failed to create %s FDB %s in %s on %s, rv:%d", + type.c_str(), entry.mac.to_string().c_str(), + vlan.m_alias.c_str(), port_name.c_str(), status); + return false; //FIXME: it should be based on status. Some could be retried, some not + } + port.m_fdb_count++; + m_portsOrch->setPort(port.m_alias, port); + vlan.m_fdb_count++; + m_portsOrch->setPort(vlan.m_alias, vlan); + } + + FdbData fdbData; + fdbData.bridge_port_id = port.m_bridge_port_id; + fdbData.type = type; + fdbData.origin = origin; + fdbData.remote_ip = remote_ip; + fdbData.esi = esi; + fdbData.vni = vni; - (void) m_entries.insert(entry); + m_entries[entry] = fdbData; - gCrmOrch->incCrmResUsedCounter(CrmResourceType::CRM_FDB_ENTRY); + string key = "Vlan" + to_string(vlan.m_vlan_info.vlan_id) + ":" + entry.mac.to_string(); - FdbUpdate update = {entry, port, true}; - for (auto observer: m_observers) + if (origin != FDB_ORIGIN_VXLAN_ADVERTIZED) { - observer->update(SUBJECT_TYPE_FDB_CHANGE, &update); + /* State-DB is updated only for Local Mac addresses */ + // Write to StateDb + std::vector fvs; + fvs.push_back(FieldValueTuple("port", port_name)); + if (type == "dynamic_local") + fvs.push_back(FieldValueTuple("type", "dynamic")); + else + fvs.push_back(FieldValueTuple("type", type)); + m_fdbStateTable.set(key, fvs); + } + else if (macUpdate && (oldOrigin != FDB_ORIGIN_VXLAN_ADVERTIZED)) + { + /* origin is FDB_ORIGIN_ADVERTIZED and it is mac-update + * so delete from StateDb since we only keep local fdbs + * in state-db + */ + m_fdbStateTable.del(key); + } + + if(!macUpdate) + { + gCrmOrch->incCrmResUsedCounter(CrmResourceType::CRM_FDB_ENTRY); } + FdbUpdate update; + update.entry = entry; + update.port = port; + update.type = type; + update.add = true; + + notify(SUBJECT_TYPE_FDB_CHANGE, &update); + return true; } -bool FdbOrch::removeFdbEntry(const FdbEntry& entry) +bool FdbOrch::removeFdbEntry(const FdbEntry& entry, FdbOrigin origin) { + Port vlan; + Port port; + SWSS_LOG_ENTER(); - if (m_entries.count(entry) == 0) + SWSS_LOG_NOTICE("FdbOrch RemoveFDBEntry: mac=%s bv_id=0x%lx origin %d", entry.mac.to_string().c_str(), entry.bv_id, origin); + + if (!m_portsOrch->getPort(entry.bv_id, vlan)) + { + SWSS_LOG_NOTICE("FdbOrch notification: Failed to locate vlan port from bv_id 0x%lx", entry.bv_id); + return false; + } + + auto it= m_entries.find(entry); + if(it == m_entries.end()) { - SWSS_LOG_ERROR("FDB entry isn't found. mac=%s bv_id=0x%" PRIx64, entry.mac.to_string().c_str(), entry.bv_id); + SWSS_LOG_INFO("FdbOrch RemoveFDBEntry: FDB entry isn't found. mac=%s bv_id=0x%lx", entry.mac.to_string().c_str(), entry.bv_id); + + /* check whether the entry is in the saved fdb, if so delete it from there. */ + deleteFdbEntryFromSavedFDB(entry.mac, vlan.m_vlan_info.vlan_id, origin); return true; } + FdbData fdbData = it->second; + if (!m_portsOrch->getPortByBridgePortId(fdbData.bridge_port_id, port)) + { + SWSS_LOG_NOTICE("FdbOrch RemoveFDBEntry: Failed to locate port from bridge_port_id 0x%lx", fdbData.bridge_port_id); + return false; + } + + if(fdbData.origin != origin) + { + /* When mac is moved from remote to local + * BGP will delete the mac from vxlan_fdb_table + * but we should not delete this mac here since now + * mac in orchagent represents locally learnt + */ + SWSS_LOG_NOTICE("FdbOrch RemoveFDBEntry: mac=%s fdb origin is different; found_origin:%d delete_origin:%d", + entry.mac.to_string().c_str(), fdbData.origin, origin); + + /* We may still have the mac in saved-fdb probably due to unavailability + * of bridge-port. check whether the entry is in the saved fdb, + * if so delete it from there. */ + deleteFdbEntryFromSavedFDB(entry.mac, vlan.m_vlan_info.vlan_id, origin); + + return true; + } + + string key = "Vlan" + to_string(vlan.m_vlan_info.vlan_id) + ":" + entry.mac.to_string(); + sai_status_t status; sai_fdb_entry_t fdb_entry; fdb_entry.switch_id = gSwitchId; @@ -529,23 +1033,247 @@ bool FdbOrch::removeFdbEntry(const FdbEntry& entry) status = sai_fdb_api->remove_fdb_entry(&fdb_entry); if (status != SAI_STATUS_SUCCESS) { - SWSS_LOG_ERROR("Failed to remove FDB entry. mac=%s, bv_id=0x%" PRIx64, + SWSS_LOG_ERROR("FdbOrch RemoveFDBEntry: Failed to remove FDB entry. mac=%s, bv_id=0x%lx", entry.mac.to_string().c_str(), entry.bv_id); return true; //FIXME: it should be based on status. Some could be retried. some not } + SWSS_LOG_NOTICE("Removed mac=%s bv_id=0x%lx port:%s", + entry.mac.to_string().c_str(), entry.bv_id, port.m_alias.c_str()); + + port.m_fdb_count--; + m_portsOrch->setPort(port.m_alias, port); + vlan.m_fdb_count--; + m_portsOrch->setPort(vlan.m_alias, vlan); (void)m_entries.erase(entry); + // Remove in StateDb + if (fdbData.origin != FDB_ORIGIN_VXLAN_ADVERTIZED) + { + m_fdbStateTable.del(key); + } + gCrmOrch->decCrmResUsedCounter(CrmResourceType::CRM_FDB_ENTRY); + FdbUpdate update; + update.entry = entry; + update.port = port; + update.type = fdbData.type; + update.add = false; + + notify(SUBJECT_TYPE_FDB_CHANGE, &update); + + notifyTunnelOrch(update.port); + + return true; +} + +bool FdbOrch::flushFdbAll(bool flush_static) +{ + sai_status_t status; + sai_attribute_t port_attr; + + if (!flush_static) + { + port_attr.id = SAI_FDB_FLUSH_ATTR_ENTRY_TYPE; + port_attr.value.s32 = SAI_FDB_FLUSH_ENTRY_TYPE_DYNAMIC; + status = sai_fdb_api->flush_fdb_entries(gSwitchId, 1, &port_attr); + } + else + { + status = sai_fdb_api->flush_fdb_entries(gSwitchId, 0, NULL); + } + if (status != SAI_STATUS_SUCCESS) + { + SWSS_LOG_ERROR("Flush fdb failed, return code %x", status); + return false; + } + return true; +} + +bool FdbOrch::flushFdbByPort(const string &alias, bool flush_static) +{ + sai_status_t status; Port port; - m_portsOrch->getPortByBridgePortId(entry.bv_id, port); + sai_attribute_t port_attr[2]; - FdbUpdate update = {entry, port, false}; - for (auto observer: m_observers) + if (!m_portsOrch->getPort(alias, port)) { - observer->update(SUBJECT_TYPE_FDB_CHANGE, &update); + SWSS_LOG_ERROR("could not locate port from alias %s", alias.c_str()); + return false; + } + + if ((port.m_bridge_port_id == SAI_NULL_OBJECT_ID) || !port.m_fdb_count) + { + /* port is not an L2 port or no macs to flush */ + return true; + } + + SWSS_LOG_NOTICE("m_bridge_port_id 0x%lx flush_static %d m_fdb_count %u", port.m_bridge_port_id, flush_static, port.m_fdb_count); + + port_attr[0].id = SAI_FDB_FLUSH_ATTR_BRIDGE_PORT_ID; + port_attr[0].value.oid = port.m_bridge_port_id; + if (!flush_static) + { + port_attr[1].id = SAI_FDB_FLUSH_ATTR_ENTRY_TYPE; + port_attr[1].value.s32 = SAI_FDB_FLUSH_ENTRY_TYPE_DYNAMIC; + status = sai_fdb_api->flush_fdb_entries(gSwitchId, 2, port_attr); + } + else + { + status = sai_fdb_api->flush_fdb_entries(gSwitchId, 1, port_attr); + } + if (status != SAI_STATUS_SUCCESS) + { + SWSS_LOG_ERROR("Flush fdb failed, return code %x", status); + return false; + } + return true; +} + +bool FdbOrch::flushFdbByVlan(const string &alias, bool flush_static) +{ + sai_status_t status; + Port vlan; + sai_attribute_t vlan_attr[2]; + + if (!m_portsOrch->getPort(alias, vlan)) + { + SWSS_LOG_ERROR("could not locate vlan from alias %s", alias.c_str()); + return false; + } + SWSS_LOG_NOTICE("vlan_oid 0x%lx flush_static %d", vlan.m_vlan_info.vlan_oid, flush_static); + + vlan_attr[0].id = SAI_FDB_FLUSH_ATTR_BV_ID; + vlan_attr[0].value.oid = vlan.m_vlan_info.vlan_oid; + if (!flush_static) + { + vlan_attr[1].id = SAI_FDB_FLUSH_ATTR_ENTRY_TYPE; + vlan_attr[1].value.s32 = SAI_FDB_FLUSH_ENTRY_TYPE_DYNAMIC; + status = sai_fdb_api->flush_fdb_entries(gSwitchId, 2, vlan_attr); + } + else + { + status = sai_fdb_api->flush_fdb_entries(gSwitchId, 1, vlan_attr); + } + if (status != SAI_STATUS_SUCCESS) + { + SWSS_LOG_ERROR("Flush fdb failed, return code %x", status); + return false; } return true; } + +bool FdbOrch::flushFdbByPortVlan(const string &port_alias, const string &vlan_alias, bool flush_static) +{ + + sai_status_t status; + Port vlan; + Port port; + sai_attribute_t port_vlan_attr[3]; + + SWSS_LOG_NOTICE("port %s vlan %s", port_alias.c_str(), vlan_alias.c_str()); + + if (!m_portsOrch->getPort(port_alias, port)) + { + SWSS_LOG_ERROR("could not locate port from alias %s", port_alias.c_str()); + return false; + } + if (!m_portsOrch->getPort(vlan_alias, vlan)) + { + SWSS_LOG_NOTICE("FdbOrch notification: Failed to locate vlan %s", vlan_alias.c_str()); + return false; + } + + if ((port.m_bridge_port_id == SAI_NULL_OBJECT_ID) || !port.m_fdb_count) + { + /* port is not an L2 port or no macs to flush */ + return true; + } + + SWSS_LOG_NOTICE("vlan_oid 0x%lx m_bridge_port_id 0x%lx flush_static %d m_fdb_count %u", vlan.m_vlan_info.vlan_oid, port.m_bridge_port_id, flush_static, port.m_fdb_count); + + port_vlan_attr[0].id = SAI_FDB_FLUSH_ATTR_BV_ID; + port_vlan_attr[0].value.oid = vlan.m_vlan_info.vlan_oid; + port_vlan_attr[1].id = SAI_FDB_FLUSH_ATTR_BRIDGE_PORT_ID; + port_vlan_attr[1].value.oid = port.m_bridge_port_id; + if (!flush_static) + { + port_vlan_attr[2].id = SAI_FDB_FLUSH_ATTR_ENTRY_TYPE; + port_vlan_attr[2].value.s32 = SAI_FDB_FLUSH_ENTRY_TYPE_DYNAMIC; + status = sai_fdb_api->flush_fdb_entries(gSwitchId, 3, port_vlan_attr); + } + else + { + status = sai_fdb_api->flush_fdb_entries(gSwitchId, 2, port_vlan_attr); + } + if (status != SAI_STATUS_SUCCESS) + { + SWSS_LOG_ERROR("Flush fdb failed, return code %x", status); + return false; + } + + return true; +} + +void FdbOrch::deleteFdbEntryFromSavedFDB(const MacAddress &mac, + const unsigned short &vlanId, FdbOrigin origin, const string portName) +{ + bool found=false; + SavedFdbEntry entry; + entry.mac = mac; + entry.vlanId = vlanId; + entry.type = "static"; + /* Below members are unused during delete compare */ + entry.origin = origin; + + for (auto& itr: saved_fdb_entries) + { + if(portName.empty() || (portName == itr.first)) + { + auto iter = saved_fdb_entries[itr.first].begin(); + while(iter != saved_fdb_entries[itr.first].end()) + { + if (*iter == entry) + { + if(iter->origin == origin) + { + SWSS_LOG_NOTICE("FDB entry found in saved fdb. deleting..." + "mac=%s vlan_id=0x%x origin:%d port:%s", + mac.to_string().c_str(), vlanId, origin, + itr.first.c_str()); + saved_fdb_entries[itr.first].erase(iter); + + found=true; + break; + } + else + { + SWSS_LOG_NOTICE("FDB entry found in saved fdb, but Origin is " + "different mac=%s vlan_id=0x%x reqOrigin:%d " + "foundOrigin:%d port:%s, IGNORED", + mac.to_string().c_str(), vlanId, origin, + iter->origin, itr.first.c_str()); + } + } + iter++; + } + } + if(found) + break; + } +} + +// Notify Tunnel Orch when the number of MAC entries +void FdbOrch::notifyTunnelOrch(Port& port) +{ + VxlanTunnelOrch* tunnel_orch = gDirectory.get(); + + if((port.m_type != Port::TUNNEL) || + (port.m_fdb_count != 0)) + return; + + tunnel_orch->deleteTunnelPort(port); +} + diff --git a/orchagent/fdborch.h b/orchagent/fdborch.h index 3125b3112d9..eef5d349645 100644 --- a/orchagent/fdborch.h +++ b/orchagent/fdborch.h @@ -5,6 +5,14 @@ #include "observer.h" #include "portsorch.h" +enum FdbOrigin +{ + FDB_ORIGIN_INVALID = 0, + FDB_ORIGIN_LEARN = 1, + FDB_ORIGIN_PROVISIONED = 2, + FDB_ORIGIN_VXLAN_ADVERTIZED = 4 +}; + struct FdbEntry { MacAddress mac; @@ -14,19 +22,53 @@ struct FdbEntry { return tie(mac, bv_id) < tie(other.mac, other.bv_id); } + bool operator==(const FdbEntry& other) const + { + return tie(mac, bv_id) == tie(other.mac, other.bv_id); + } }; struct FdbUpdate { FdbEntry entry; Port port; + string type; bool add; }; +struct FdbData +{ + sai_object_id_t bridge_port_id; + string type; + FdbOrigin origin; + /** + {"dynamic", FDB_ORIGIN_LEARN} => dynamically learnt + {"dynamic", FDB_ORIGIN_PROVISIONED} => provisioned dynamic with swssconfig in APPDB + {"dynamic", FDB_ORIGIN_ADVERTIZED} => synced from remote device e.g. BGP MAC route + {"static", FDB_ORIGIN_LEARN} => Invalid + {"static", FDB_ORIGIN_PROVISIONED} => statically provisioned + {"static", FDB_ORIGIN_ADVERTIZED} => sticky synced from remote device + */ + + /* Remote FDB related info */ + string remote_ip; + string esi; + unsigned int vni; +}; + struct SavedFdbEntry { - FdbEntry entry; + MacAddress mac; + unsigned short vlanId; string type; + FdbOrigin origin; + string remote_ip; + string esi; + unsigned int vni; + bool operator==(const SavedFdbEntry& other) const + { + return tie(mac, vlanId) == tie(other.mac, other.vlanId); + } }; typedef unordered_map> fdb_entries_by_port_t; @@ -35,7 +77,7 @@ class FdbOrch: public Orch, public Subject, public Observer { public: - FdbOrch(TableConnector applDbConnector, TableConnector stateDbConnector, PortsOrch *port); + FdbOrch(DBConnector* applDbConnector, vector appFdbTables, TableConnector stateDbFdbConnector, PortsOrch *port); ~FdbOrch() { @@ -46,12 +88,19 @@ class FdbOrch: public Orch, public Subject, public Observer void update(sai_fdb_event_t, const sai_fdb_entry_t *, sai_object_id_t); void update(SubjectType type, void *cntx); bool getPort(const MacAddress&, uint16_t, Port&); + bool flushFdbByPortVlan(const string &, const string &, bool flush_static); + bool flushFdbByVlan(const string &, bool flush_static); + bool flushFdbByPort(const string &, bool flush_static); + bool flushFdbAll(bool flush_static); + bool removeFdbEntry(const FdbEntry& entry, FdbOrigin origin=FDB_ORIGIN_PROVISIONED); + + static const int fdborch_pri; private: PortsOrch *m_portsOrch; - set m_entries; + map m_entries; fdb_entries_by_port_t saved_fdb_entries; - Table m_table; + vector m_appTables; Table m_fdbStateTable; NotificationConsumer* m_flushNotificationsConsumer; NotificationConsumer* m_fdbNotificationConsumer; @@ -60,10 +109,11 @@ class FdbOrch: public Orch, public Subject, public Observer void doTask(NotificationConsumer& consumer); void updateVlanMember(const VlanMemberUpdate&); - bool addFdbEntry(const FdbEntry&, const string&, const string&); - bool removeFdbEntry(const FdbEntry&); + bool addFdbEntry(const FdbEntry&, const string&, const string&, FdbOrigin origin, const string& remote_ip="", unsigned int vni=0, const string& esi=""); + void deleteFdbEntryFromSavedFDB(const MacAddress &mac, const unsigned short &vlanId, FdbOrigin origin, const string portName=""); bool storeFdbEntryState(const FdbUpdate& update); + void notifyTunnelOrch(Port& port); }; #endif /* SWSS_FDBORCH_H */ diff --git a/orchagent/orchdaemon.cpp b/orchagent/orchdaemon.cpp index 26ff1f03011..329a1757a1b 100644 --- a/orchagent/orchdaemon.cpp +++ b/orchagent/orchdaemon.cpp @@ -84,11 +84,15 @@ bool OrchDaemon::init() { APP_LAG_MEMBER_TABLE_NAME, portsorch_base_pri } }; + vector app_fdb_tables = { + { APP_FDB_TABLE_NAME, FdbOrch::fdborch_pri}, + { APP_VXLAN_FDB_TABLE_NAME, FdbOrch::fdborch_pri} + }; + gCrmOrch = new CrmOrch(m_configDb, CFG_CRM_TABLE_NAME); gPortsOrch = new PortsOrch(m_applDb, ports_tables); - TableConnector applDbFdb(m_applDb, APP_FDB_TABLE_NAME); TableConnector stateDbFdb(m_stateDb, STATE_FDB_TABLE_NAME); - gFdbOrch = new FdbOrch(applDbFdb, stateDbFdb, gPortsOrch); + gFdbOrch = new FdbOrch(m_applDb, app_fdb_tables, stateDbFdb, gPortsOrch); vector vnet_tables = { APP_VNET_RT_TABLE_NAME, diff --git a/tests/test_evpn_fdb.py b/tests/test_evpn_fdb.py new file mode 100644 index 00000000000..469494a279e --- /dev/null +++ b/tests/test_evpn_fdb.py @@ -0,0 +1,680 @@ +from swsscommon import swsscommon +import os +import sys +import time +import json +import pytest +from distutils.version import StrictVersion + +def create_entry(tbl, key, pairs): + fvs = swsscommon.FieldValuePairs(pairs) + tbl.set(key, fvs) + + # FIXME: better to wait until DB create them + time.sleep(1) + +def create_entry_tbl(db, table, key, pairs): + tbl = swsscommon.Table(db, table) + create_entry(tbl, key, pairs) + +def create_entry_pst(db, table, key, pairs): + tbl = swsscommon.ProducerStateTable(db, table) + create_entry(tbl, key, pairs) + +def delete_entry_pst(db, table, key): + tbl = swsscommon.ProducerStateTable(db, table) + tbl._del(key) + +def how_many_entries_exist(db, table): + tbl = swsscommon.Table(db, table) + return len(tbl.getKeys()) + +def remove_mac(db, table, mac, vlan): + tbl = swsscommon.Table(db, table) + tbl._del("Vlan" + vlan + "|" + mac.lower()) + time.sleep(1) + +def delete_entry_tbl(db, table, key): + tbl = swsscommon.Table(db, table) + tbl._del(key) + time.sleep(1) + +def create_evpn_nvo(db, nvoname, tnlname): + #conf_db = swsscommon.DBConnector(swsscommon.CONFIG_DB, dvs.redis_sock, 0) + attrs = [ + ("source_vtep", tnlname), + ] + + # create the VXLAN tunnel Term entry in Config DB + create_entry_tbl( + db, + "EVPN_NVO", nvoname, + attrs, + ) + +def remove_evpn_nvo(db, nvoname): + #conf_db = swsscommon.DBConnector(swsscommon.CONFIG_DB, dvs.redis_sock, 0) + delete_entry_tbl(db,"EVPN_NVO", nvoname,) + +def create_vxlan_tunnel(db, name, src_ip): + #conf_db = swsscommon.DBConnector(swsscommon.CONFIG_DB, dvs.redis_sock, 0) + + attrs = [ + ("src_ip", src_ip), + ] + + # create the VXLAN tunnel Term entry in Config DB + create_entry_tbl( + db, + "VXLAN_TUNNEL", name, + attrs, + ) + +def remove_vxlan_tunnel(db, tnlname): + #conf_db = swsscommon.DBConnector(swsscommon.CONFIG_DB, dvs.redis_sock, 0) + + # create the VXLAN tunnel Term entry in Config DB + delete_entry_tbl( + db, + "VXLAN_TUNNEL", tnlname, + ) + +def create_vxlan_tunnel_map(db, tnlname, mapname, vni_id, vlan_id): + #conf_db = swsscommon.DBConnector(swsscommon.CONFIG_DB, dvs.redis_sock, 0) + + attrs = [ + ("vni", vni_id), + ("vlan", vlan_id), + ] + + # create the VXLAN tunnel Term entry in Config DB + create_entry_tbl( + db, + "VXLAN_TUNNEL_MAP", "%s|%s" % (tnlname, mapname), + attrs, + ) + +def remove_vxlan_tunnel_map(db, tnlname, mapname,vni_id, vlan_id): + #conf_db = swsscommon.DBConnector(swsscommon.CONFIG_DB, dvs.redis_sock, 0) + + attrs = [ + ("vni", vni_id), + ("vlan", vlan_id), + ] + + # create the VXLAN tunnel Term entry in Config DB + delete_entry_tbl( + db, + "VXLAN_TUNNEL_MAP", "%s|%s" % (tnlname, mapname), + ) + +def create_evpn_remote_vni(db, vlan_id, remotevtep, vnid): + #app_db = swsscommon.DBConnector(swsscommon.APPL_DB, dvs.redis_sock, 0) + create_entry_pst( + db, + "EVPN_REMOTE_VNI_TABLE", "%s:%s" % (vlan_id, remotevtep), + [ + ("vni", vnid), + ], + ) + +def remove_evpn_remote_vni(db, vlan_id, remotevtep ): + #app_db = swsscommon.DBConnector(swsscommon.APPL_DB, dvs.redis_sock, 0) + delete_entry_pst( + db, + "EVPN_REMOTE_VNI_TABLE", "%s:%s" % (vlan_id, remotevtep), + ) + +def get_vxlan_p2p_tunnel_bp(db, remote_ip): + tnl_id = None + bp = None + print("remote_ip = " + remote_ip) + attributes = [("SAI_TUNNEL_TERM_TABLE_ENTRY_ATTR_TYPE", "SAI_TUNNEL_TERM_TABLE_ENTRY_TYPE_P2P"), + ("SAI_TUNNEL_TERM_TABLE_ENTRY_ATTR_TUNNEL_TYPE", "SAI_TUNNEL_TYPE_VXLAN"), + ("SAI_TUNNEL_TERM_TABLE_ENTRY_ATTR_SRC_IP", remote_ip) + ] + tbl = swsscommon.Table(db, "ASIC_STATE:SAI_OBJECT_TYPE_TUNNEL_TERM_TABLE_ENTRY") + keys = tbl.getKeys() + for key in keys: + status, fvs = tbl.get(key) + assert status, "Error reading from table ASIC_STATE:SAI_OBJECT_TYPE_TUNNEL_TERM_TABLE_ENTRY" + attrs = dict(attributes) + num_match = 0 + for k, v in fvs: + print("attr:value="+str(k)+":"+str(v)) + if k in attrs and attrs[k] == v: + num_match += 1 + if k == "SAI_TUNNEL_TERM_TABLE_ENTRY_ATTR_ACTION_TUNNEL_ID": + tnl_id = v + if num_match == len(attributes): + break + else: + tnl_id = None + + print("tnl_id = "+str(tnl_id)) + if tnl_id != None: + tbl = swsscommon.Table(db, "ASIC_STATE:SAI_OBJECT_TYPE_BRIDGE_PORT") + keys = tbl.getKeys() + for key in keys: + status, fvs = tbl.get(key) + assert status, "Error reading from table ASIC_STATE:SAI_OBJECT_TYPE_BRIDGE_PORT" + for k, v in fvs: + print("attr:value="+str(k)+":"+str(v)) + if k == "SAI_BRIDGE_PORT_ATTR_TUNNEL_ID" and tnl_id == v: + bp = key + break + if bp != None: + break + else: + pass + print("bp = "+str(bp)) + return bp + + +def test_evpnFdb(dvs, testlog): + dvs.setup_db() + + dvs.runcmd("sonic-clear fdb all") + time.sleep(2) + + #Find switch_id + switch_id = dvs.getSwitchOid() + print("Switch_id="+str(switch_id)) + + vlan_before = how_many_entries_exist(dvs.adb, "ASIC_STATE:SAI_OBJECT_TYPE_VLAN") + + # create vlan + print("Creating Vlan3") + #dvs.runcmd("config vlan add 3") + dvs.create_vlan("3") + time.sleep(2) + + vlan_after = how_many_entries_exist(dvs.adb, "ASIC_STATE:SAI_OBJECT_TYPE_VLAN") + assert vlan_after - vlan_before == 1, "The Vlan3 wasn't created" + print("Vlan3 is created") + + # Find the vlan_oid to be used in DB communications + vlan_oid_3 = dvs.getVlanOid("3") + assert vlan_oid_3 is not None, "Could not find Vlan_oid" + print("Vlan-3 vlan_oid="+str(vlan_oid_3)) + + bp_before = how_many_entries_exist(dvs.adb, "ASIC_STATE:SAI_OBJECT_TYPE_BRIDGE_PORT") + vm_before = how_many_entries_exist(dvs.adb, "ASIC_STATE:SAI_OBJECT_TYPE_VLAN_MEMBER") + + print("Making Ethernet0 as a member of Vlan3") + #dvs.runcmd("config vlan member add 3 Ethernet0") + dvs.create_vlan_member("3", "Ethernet0") + time.sleep(2) + + # check that the vlan information was propagated + bp_after = how_many_entries_exist(dvs.adb, "ASIC_STATE:SAI_OBJECT_TYPE_BRIDGE_PORT") + vm_after = how_many_entries_exist(dvs.adb, "ASIC_STATE:SAI_OBJECT_TYPE_VLAN_MEMBER") + + assert bp_after - bp_before == 1, "The bridge port wasn't created" + assert vm_after - vm_before == 1, "The vlan member wasn't added" + print("Ethernet0 is a member of Vlan3") + + # Get mapping between interface name and its bridge port_id + iface_2_bridge_port_id = dvs.get_map_iface_bridge_port_id(dvs.adb) + + #create SIP side of tunnel + source_tnl_name = "source_vtep_name" + source_tnl_ip = "7.7.7.7" + create_vxlan_tunnel(dvs.cdb, source_tnl_name, source_tnl_ip) + time.sleep(1) + + nvo_name = "evpn_nvo" + create_evpn_nvo(dvs.cdb, nvo_name, source_tnl_name) + time.sleep(1) + + map_name_vlan_3 = "map_3_3" + create_vxlan_tunnel_map(dvs.cdb, source_tnl_name, map_name_vlan_3, "3", "Vlan3") + time.sleep(1) + + remote_ip_6 = "6.6.6.6" + create_evpn_remote_vni(dvs.pdb, "Vlan3", remote_ip_6, "3") + remote_ip_8 = "8.8.8.8" + create_evpn_remote_vni(dvs.pdb, "Vlan3", remote_ip_8, "3") + time.sleep(1) + + #UT-1 Evpn Mac add from remote when tunnels are already created + mac = "52:54:00:25:06:E9" + print("Creating Evpn FDB Vlan3:"+mac.lower()+":6.6.6.6 in APP-DB") + create_entry_pst( + dvs.pdb, + "VXLAN_FDB_TABLE", "Vlan3:"+mac.lower(), + [ + ("remote_vtep", remote_ip_6), + ("type", "dynamic"), + ("vni", "3") + ] + ) + time.sleep(1) + + tnl_bp_oid_6 = get_vxlan_p2p_tunnel_bp(dvs.adb, remote_ip_6) + tnl_bp_oid_8 = get_vxlan_p2p_tunnel_bp(dvs.adb, remote_ip_8) + + # check that the FDB entry is inserted into ASIC DB + ok, extra = dvs.is_fdb_entry_exists(dvs.adb, "ASIC_STATE:SAI_OBJECT_TYPE_FDB_ENTRY", + [("mac", mac), ("bvid", vlan_oid_3)], + [("SAI_FDB_ENTRY_ATTR_TYPE", "SAI_FDB_ENTRY_TYPE_STATIC_MACMOVE"), + ("SAI_FDB_ENTRY_ATTR_ENDPOINT_IP", remote_ip_6), + ("SAI_FDB_ENTRY_ATTR_BRIDGE_PORT_ID", str(tnl_bp_oid_6)), + ] + ) + assert ok == True, str(extra) + print("EVPN FDB Vlan3:"+mac.lower()+":"+remote_ip_6+" is created in ASIC-DB") + + time.sleep(1) + + #UT-2 Evpn Mac del from remote + mac = "52:54:00:25:06:E9" + print("Deleting Evpn FDB Vlan3:"+mac.lower()+":6.6.6.6 in APP-DB") + delete_entry_pst( + dvs.pdb, + "VXLAN_FDB_TABLE", "Vlan3:"+mac.lower() + ) + time.sleep(1) + + # check that the FDB entry is deleted from ASIC DB + ok, extra = dvs.is_fdb_entry_exists(dvs.adb, "ASIC_STATE:SAI_OBJECT_TYPE_FDB_ENTRY", + [("mac", mac), ("bvid", vlan_oid_3)], + [("SAI_FDB_ENTRY_ATTR_TYPE", "SAI_FDB_ENTRY_TYPE_STATIC_MACMOVE"), + ("SAI_FDB_ENTRY_ATTR_ENDPOINT_IP", remote_ip_6), + ("SAI_FDB_ENTRY_ATTR_BRIDGE_PORT_ID", str(tnl_bp_oid_6)), + ] + ) + assert ok == False, str(extra) + print("EVPN FDB Vlan3:"+mac.lower()+":"+remote_ip_6+" is deleted from ASIC-DB") + + time.sleep(1) + + #UT-3 Evpn Mac add from remote when local mac is already present + mac = "52:54:00:25:06:E9" + + print("Creating Local dynamic FDB Vlan3:"+mac.lower()+":Ethernet0 in APP-DB") + # Create Dynamic MAC entry in APP DB + create_entry_pst( + dvs.pdb, + "FDB_TABLE", "Vlan3:"+mac.lower(), + [ + ("port", "Ethernet0"), + ("type", "dynamic"), + ] + ) + + time.sleep(1) + #raw_input("Check ASIC_DB.........") + + # check that the FDB entry was added in ASIC DB + ok, extra = dvs.is_fdb_entry_exists(dvs.adb, "ASIC_STATE:SAI_OBJECT_TYPE_FDB_ENTRY", + [("mac", mac), ("bvid", vlan_oid_3)], + [("SAI_FDB_ENTRY_ATTR_TYPE", "SAI_FDB_ENTRY_TYPE_DYNAMIC"), + ("SAI_FDB_ENTRY_ATTR_BRIDGE_PORT_ID", iface_2_bridge_port_id["Ethernet0"])] + ) + assert ok, str(extra) + print("Dynamic FDB Vlan3:"+mac.lower()+":Ethernet0 is created in Asic-DB") + + # check that the FDB entry was added in STATE DB + mac1_found, extra = dvs.is_table_entry_exists(dvs.sdb, "FDB_TABLE", + "Vlan3:"+mac.lower(), + [("port", "Ethernet0"), + ("type", "dynamic"), + ] + ) + assert mac1_found, str(extra) + print("FDB Vlan3:"+mac+":Ethernet0 is created in STATE-DB") + + print("Creating Evpn FDB Vlan3:"+mac.lower()+":6.6.6.6 in APP-DB") + create_entry_pst( + dvs.pdb, + "VXLAN_FDB_TABLE", "Vlan3:"+mac.lower(), + [ + ("remote_vtep", remote_ip_6), + ("type", "dynamic"), + ("vni", "3") + ] + ) + time.sleep(1) + + #raw_input("Check ASIC_DB.........") + + # check that the FDB entry is inserted into ASIC DB + ok, extra = dvs.is_fdb_entry_exists(dvs.adb, "ASIC_STATE:SAI_OBJECT_TYPE_FDB_ENTRY", + [("mac", mac), ("bvid", str(dvs.getVlanOid("3")))], + [("SAI_FDB_ENTRY_ATTR_TYPE", "SAI_FDB_ENTRY_TYPE_STATIC_MACMOVE"), + ("SAI_FDB_ENTRY_ATTR_ENDPOINT_IP", remote_ip_6), + ("SAI_FDB_ENTRY_ATTR_BRIDGE_PORT_ID", str(tnl_bp_oid_6)), + ] + ) + assert ok, str(extra) + print("EVPN FDB Vlan3:"+mac.lower()+":"+remote_ip_6+" is created in ASIC-DB") + + # check that the Local FDB entry is deleted from STATE DB + mac1_found, extra = dvs.is_table_entry_exists(dvs.sdb, "FDB_TABLE", + "Vlan3:"+mac.lower(), + [("port", "Ethernet0"), + ("type", "dynamic"), + ] + ) + assert mac1_found == False, str(extra) + print("FDB Vlan3:"+mac+":Ethernet0 is deleted from STATE-DB") + + time.sleep(1) + + #UT-4 Evpn Sticky Mac add from remote + mac = "52:54:00:25:06:E9" + print("Creating Evpn Sticky FDB Vlan3:"+mac.lower()+":6.6.6.6 in APP-DB") + create_entry_pst( + dvs.pdb, + "VXLAN_FDB_TABLE", "Vlan3:"+mac.lower(), + [ + ("remote_vtep", remote_ip_6), + ("type", "static"), + ("vni", "3") + ] + ) + time.sleep(1) + + #raw_input("Check ASIC_DB.........") + + # check that the FDB entry is inserted into ASIC DB + ok, extra = dvs.is_fdb_entry_exists(dvs.adb, "ASIC_STATE:SAI_OBJECT_TYPE_FDB_ENTRY", + [("mac", mac), ("bvid", str(dvs.getVlanOid("3")))], + [("SAI_FDB_ENTRY_ATTR_TYPE", "SAI_FDB_ENTRY_TYPE_STATIC"), + ("SAI_FDB_ENTRY_ATTR_ENDPOINT_IP", remote_ip_6), + ("SAI_FDB_ENTRY_ATTR_BRIDGE_PORT_ID", str(tnl_bp_oid_6)), + ] + ) + assert ok, str(extra) + print("EVPN Sticky FDB Vlan3:"+mac.lower()+":"+remote_ip_6+" is created in ASIC-DB") + + #raw_input("Check ASIC_DB.........") + + #UT-5 create a Static FDB entry + mac = "52:54:00:25:06:E9" + print("Creating static FDB Vlan3:"+mac.lower()+":Ethernet0 in CONFIG-DB") + create_entry_tbl( + dvs.cdb, + "FDB", "Vlan3|"+mac.lower(), + [ + ("port", "Ethernet0"), + ] + ) + time.sleep(2) + + #raw_input("Check ASIC_DB.........") + + # check that the FDB entry was added in APP DB + mac1_found, extra = dvs.is_table_entry_exists(dvs.pdb, "FDB_TABLE", + "Vlan3:"+mac.lower(), + [("port", "Ethernet0"), + ("type", "static"), + ] + ) + assert mac1_found, str(extra) + print("Static FDB Vlan3:"+mac.lower()+"Ethernet0 is created in APP-DB") + + # check that the FDB entry is now inserted into ASIC DB + ok, extra = dvs.is_fdb_entry_exists(dvs.adb, "ASIC_STATE:SAI_OBJECT_TYPE_FDB_ENTRY", + [("mac", mac.upper()), ("bvid", vlan_oid_3)], + [("SAI_FDB_ENTRY_ATTR_TYPE", "SAI_FDB_ENTRY_TYPE_STATIC"), + ("SAI_FDB_ENTRY_ATTR_BRIDGE_PORT_ID", iface_2_bridge_port_id["Ethernet0"])] + ) + assert ok, str(extra) + print("Static FDB Vlan3:"+mac.lower()+":Ethernet0 is created in ASIC-DB") + + # check that the FDB entry was added in STATE DB + mac1_found, extra = dvs.is_table_entry_exists(dvs.sdb, "FDB_TABLE", + "Vlan3:"+mac.lower(), + [("port", "Ethernet0"), + ("type", "static"), + ] + ) + assert mac1_found, str(extra) + print("Static FDB Vlan3:"+mac.lower()+":Ethernet0 is created in STATE-DB") + + #raw_input("Check ASIC_DB.........") + + #UT-6 Evpn Mac del from remote when only local is present; local mac should not get affected + mac = "52:54:00:25:06:E9" + print("Deleting Evpn FDB Vlan3:"+mac.lower()+":6.6.6.6 in APP-DB") + delete_entry_pst( + dvs.pdb, + "VXLAN_FDB_TABLE", "Vlan3:"+mac.lower() + ) + time.sleep(1) + + #raw_input("Check ASIC_DB.........") + + # check that the existing local fdb entry is not deleted and still available in ASIC DB + ok, extra = dvs.is_fdb_entry_exists(dvs.adb, "ASIC_STATE:SAI_OBJECT_TYPE_FDB_ENTRY", + [("mac", mac), ("bvid", vlan_oid_3)], + [("SAI_FDB_ENTRY_ATTR_TYPE", "SAI_FDB_ENTRY_TYPE_STATIC"), + ("SAI_FDB_ENTRY_ATTR_BRIDGE_PORT_ID", iface_2_bridge_port_id["Ethernet0"]), + ] + ) + assert ok, str(extra) + print("Local FDB Vlan3:"+mac.lower()+":Ethernet0 is not deleted from ASIC-DB") + + # check that the local FDB entry is still available in STATE DB + mac1_found, extra = dvs.is_table_entry_exists(dvs.sdb, "FDB_TABLE", + "Vlan3:"+mac.lower(), + [("port", "Ethernet0"), + ("type", "static"), + ] + ) + assert mac1_found, str(extra) + print("Local FDB Vlan3:"+mac.lower()+":Ethernet0 is not deleted STATE-DB") + + time.sleep(1) + + #UT-7 Evpn Mac add from remote when local static mac is already available + mac = "52:54:00:25:06:E9" + print("Creating Evpn dynamic FDB Vlan3:"+mac.lower()+":6.6.6.6 in APP-DB") + create_entry_pst( + dvs.pdb, + "VXLAN_FDB_TABLE", "Vlan3:"+mac.lower(), + [ + ("remote_vtep", remote_ip_6), + ("type", "dynamic"), + ("vni", "3") + ] + ) + time.sleep(1) + + #raw_input("Check ASIC_DB.........") + + # check that the FDB entry is not inserted into ASIC DB + ok, extra = dvs.is_fdb_entry_exists(dvs.adb, "ASIC_STATE:SAI_OBJECT_TYPE_FDB_ENTRY", + [("mac", mac), ("bvid", vlan_oid_3)], + [("SAI_FDB_ENTRY_ATTR_TYPE", "SAI_FDB_ENTRY_TYPE_STATIC_MACMOVE"), + ("SAI_FDB_ENTRY_ATTR_ENDPOINT_IP", remote_ip_6), + ("SAI_FDB_ENTRY_ATTR_BRIDGE_PORT_ID", str(tnl_bp_oid_6)), + ] + ) + assert ok == False, str(extra) + print("EVPN dynamic FDB Vlan3:"+mac.lower()+":"+remote_ip_6+" is not created in ASIC-DB") + + time.sleep(1) + + print("Creating Evpn sticky FDB Vlan3:"+mac.lower()+":6.6.6.6 in APP-DB") + create_entry_pst( + dvs.pdb, + "VXLAN_FDB_TABLE", "Vlan3:"+mac.lower(), + [ + ("remote_vtep", remote_ip_6), + ("type", "static"), + ("vni", "3") + ] + ) + time.sleep(1) + + #raw_input("Check ASIC_DB.........") + + # check that the FDB entry is not inserted into ASIC DB + ok, extra = dvs.is_fdb_entry_exists(dvs.adb, "ASIC_STATE:SAI_OBJECT_TYPE_FDB_ENTRY", + [("mac", mac), ("bvid", vlan_oid_3)], + [("SAI_FDB_ENTRY_ATTR_TYPE", "SAI_FDB_ENTRY_TYPE_STATIC"), + ("SAI_FDB_ENTRY_ATTR_ENDPOINT_IP", remote_ip_6), + ("SAI_FDB_ENTRY_ATTR_BRIDGE_PORT_ID", str(tnl_bp_oid_6)), + ] + ) + assert ok == False, str(extra) + print("EVPN sticky FDB Vlan3:"+mac.lower()+":"+remote_ip_6+" is not created in ASIC-DB") + + # Delete Static FDB entry + mac = "52:54:00:25:06:E9" + print("Deleting static FDB Vlan3:"+mac.lower()+":Ethernet0 from CONFIG-DB") + delete_entry_tbl( + dvs.cdb, + "FDB", "Vlan3|"+mac.lower() + ) + time.sleep(2) + + #raw_input("Check ASIC_DB.........") + + # check that the FDB entry was deleted in APP DB + mac1_found, extra = dvs.is_table_entry_exists(dvs.pdb, "FDB_TABLE", + "Vlan3:"+mac.lower(), + [("port", "Ethernet0"), + ("type", "static"), + ] + ) + assert mac1_found == False, str(extra) + print("Static FDB Vlan3:"+mac.lower()+"Ethernet0 is deleted from APP-DB") + + # check that the FDB entry is deleted in ASIC DB + ok, extra = dvs.is_fdb_entry_exists(dvs.adb, "ASIC_STATE:SAI_OBJECT_TYPE_FDB_ENTRY", + [("mac", mac.upper()), ("bvid", vlan_oid_3)], + [("SAI_FDB_ENTRY_ATTR_TYPE", "SAI_FDB_ENTRY_TYPE_STATIC"), + ("SAI_FDB_ENTRY_ATTR_BRIDGE_PORT_ID", iface_2_bridge_port_id["Ethernet0"])] + ) + assert ok == False, str(extra) + print("Static FDB Vlan3:"+mac.lower()+":Ethernet0 is deleted in ASIC-DB") + + # check that the FDB entry was deleted from STATE DB + mac1_found, extra = dvs.is_table_entry_exists(dvs.sdb, "FDB_TABLE", + "Vlan3:"+mac.lower(), + [("port", "Ethernet0"), + ("type", "static"), + ] + ) + assert mac1_found == False, str(extra) + print("Static FDB Vlan3:"+mac.lower()+":Ethernet0 is deleted from STATE-DB") + + #raw_input("Check ASIC_DB.........") + + #UT-8 Evpn Mac add from remote when tunnels are already created + mac = "52:54:00:25:06:E9" + print("Creating Evpn FDB Vlan3:"+mac.lower()+":6.6.6.6 in APP-DB") + create_entry_pst( + dvs.pdb, + "VXLAN_FDB_TABLE", "Vlan3:"+mac.lower(), + [ + ("remote_vtep", remote_ip_6), + ("type", "dynamic"), + ("vni", "3") + ] + ) + time.sleep(1) + + #raw_input("Check ASIC_DB.........") + + # check that the FDB entry is inserted into ASIC DB + ok, extra = dvs.is_fdb_entry_exists(dvs.adb, "ASIC_STATE:SAI_OBJECT_TYPE_FDB_ENTRY", + [("mac", mac), ("bvid", vlan_oid_3)], + [("SAI_FDB_ENTRY_ATTR_TYPE", "SAI_FDB_ENTRY_TYPE_STATIC_MACMOVE"), + ("SAI_FDB_ENTRY_ATTR_ENDPOINT_IP", remote_ip_6), + ("SAI_FDB_ENTRY_ATTR_BRIDGE_PORT_ID", str(tnl_bp_oid_6)), + ] + ) + assert ok == True, str(extra) + print("EVPN FDB Vlan3:"+mac.lower()+":"+remote_ip_6+" is created in ASIC-DB") + + time.sleep(1) + + tnl_bp_oid_8 = get_vxlan_p2p_tunnel_bp(dvs.adb, remote_ip_8) + + print("Creating Evpn FDB Vlan3:"+mac.lower()+":8.8.8.8 in APP-DB") + create_entry_pst( + dvs.pdb, + "VXLAN_FDB_TABLE", "Vlan3:"+mac.lower(), + [ + ("remote_vtep", remote_ip_8), + ("type", "dynamic"), + ("vni", "3") + ] + ) + time.sleep(1) + + #raw_input("Check ASIC_DB.........") + + # check that the FDB entry is inserted into ASIC DB + ok, extra = dvs.is_fdb_entry_exists(dvs.adb, "ASIC_STATE:SAI_OBJECT_TYPE_FDB_ENTRY", + [("mac", mac), ("bvid", vlan_oid_3)], + [("SAI_FDB_ENTRY_ATTR_TYPE", "SAI_FDB_ENTRY_TYPE_STATIC_MACMOVE"), + ("SAI_FDB_ENTRY_ATTR_ENDPOINT_IP", remote_ip_8), + ("SAI_FDB_ENTRY_ATTR_BRIDGE_PORT_ID", str(tnl_bp_oid_8)), + ] + ) + assert ok == True, str(extra) + print("EVPN FDB Vlan3:"+mac.lower()+":"+remote_ip_8+" is created in ASIC-DB") + + time.sleep(1) + + #UT-9 Local mac move (delete and learn) when remote is already added + mac = "52:54:00:25:06:E9" + print("Deleting FDB Vlan3:52-54-00-25-06-E9:8.8.8.8 in ASIC-DB") + delete_entry_tbl(dvs.adb, "ASIC_STATE", "SAI_OBJECT_TYPE_FDB_ENTRY:{\"bvid\":\""+vlan_oid_3+"\",\"mac\":\""+mac+"\",\"switch_id\":\""+switch_id+"\"}") + + ntf = swsscommon.NotificationProducer(dvs.adb, "FDB_NOTIFICATIONS") + fvp = swsscommon.FieldValuePairs() + ntf_data = "[{\"fdb_entry\":\"{\\\"bvid\\\":\\\""+vlan_oid_3+"\\\",\\\"mac\\\":\\\""+mac+"\\\",\\\"switch_id\\\":\\\""+switch_id+"\\\"}\",\"fdb_event\":\"SAI_FDB_EVENT_AGED\",\"list\":[{\"id\":\"SAI_FDB_ENTRY_ATTR_BRIDGE_PORT_ID\",\"value\":\""+str(tnl_bp_oid_8)+"\"}]}]" + ntf.send("fdb_event", ntf_data, fvp) + + time.sleep(2) + + #raw_input("Check ASIC_DB.........") + + print("Creating FDB Vlan3:52-54-00-25-06-E9:Ethernet0 in ASIC-DB") + create_entry_tbl( + dvs.adb, + "ASIC_STATE", "SAI_OBJECT_TYPE_FDB_ENTRY:{\"bvid\":\""+vlan_oid_3+"\",\"mac\":\"52:54:00:25:06:E9\",\"switch_id\":\""+switch_id+"\"}", + [ + ("SAI_FDB_ENTRY_ATTR_TYPE", "SAI_FDB_ENTRY_TYPE_DYNAMIC"), + ("SAI_FDB_ENTRY_ATTR_BRIDGE_PORT_ID", iface_2_bridge_port_id["Ethernet0"]), + ] + ) + + ntf = swsscommon.NotificationProducer(dvs.adb, "FDB_NOTIFICATIONS") + fvp = swsscommon.FieldValuePairs() + ntf_data = "[{\"fdb_entry\":\"{\\\"bvid\\\":\\\""+vlan_oid_3+"\\\",\\\"mac\\\":\\\"52:54:00:25:06:E9\\\",\\\"switch_id\\\":\\\""+switch_id+"\\\"}\",\"fdb_event\":\"SAI_FDB_EVENT_LEARNED\",\"list\":[{\"id\":\"SAI_FDB_ENTRY_ATTR_BRIDGE_PORT_ID\",\"value\":\""+iface_2_bridge_port_id["Ethernet0"]+"\"}]}]" + ntf.send("fdb_event", ntf_data, fvp) + + time.sleep(2) + + #raw_input("Check ASIC_DB.........") + + # check that the FDB entry was added in ASIC DB + ok, extra = dvs.is_fdb_entry_exists(dvs.adb, "ASIC_STATE:SAI_OBJECT_TYPE_FDB_ENTRY", + [("mac", "52:54:00:25:06:E9"), ("bvid", vlan_oid_3)], + [("SAI_FDB_ENTRY_ATTR_TYPE", "SAI_FDB_ENTRY_TYPE_DYNAMIC"), + ("SAI_FDB_ENTRY_ATTR_BRIDGE_PORT_ID", iface_2_bridge_port_id["Ethernet0"])] + ) + assert ok, str(extra) + print("FDB Vlan3:52-54-00-25-06-E9:Ethernet0 is created in ASIC-DB") + + # check that the FDB entry was added in STATE DB + mac1_found, extra = dvs.is_table_entry_exists(dvs.sdb, "FDB_TABLE", + "Vlan3:52:54:00:25:06:e9", + [("port", "Ethernet0"), + ("type", "dynamic"), + ] + ) + assert mac1_found, str(extra) + print("FDB Vlan3:52-54-00-25-06-E9:Ethernet0 is created in STATE-DB") + + + dvs.remove_vlan_member("3", "Ethernet0") + dvs.remove_vlan("3")