vslib/VirtualSwitchSaiInterfaceFdb.cpp (163 lines of code) (raw):
#include "VirtualSwitchSaiInterface.h"
#include "SwitchStateBase.h"
#include "swss/logger.h"
#include "meta/sai_serialize.h"
using namespace saivs;
bool VirtualSwitchSaiInterface::doesFdbEntryNotMatchFlushAttr(
_In_ const std::string &str_fdb_entry,
_In_ SwitchState::AttrHash &fdb_attrs,
_In_ uint32_t attr_count,
_In_ const sai_attribute_t *attr_list)
{
SWSS_LOG_ENTER();
/*
* Since there may be combination of flush attributes
* that user requested to flush, so when one attribute
* is different, then this fdb entry should NOT be flushed.
*/
for (uint32_t idx = 0; idx < attr_count; ++idx)
{
const sai_attribute_t &attr = attr_list[idx];
switch (attr.id)
{
case SAI_FDB_FLUSH_ATTR_BRIDGE_PORT_ID:
// remove from list all entries not matching bridge port id
if (fdb_attrs.at("SAI_FDB_ENTRY_ATTR_BRIDGE_PORT_ID")->getAttr()->value.oid != attr.value.oid)
{
return true;
}
break;
case SAI_FDB_FLUSH_ATTR_BV_ID:
{
sai_fdb_entry_t fdb_entry;
sai_deserialize_fdb_entry(str_fdb_entry, fdb_entry);
// remove from list all entries not matching vlan id
if (fdb_entry.bv_id != attr.value.oid)
{
return true;
}
}
break;
case SAI_FDB_FLUSH_ATTR_ENTRY_TYPE:
switch (attr.value.s32)
{
case SAI_FDB_FLUSH_ENTRY_TYPE_ALL:
// if flush type is all, we accept both dynamic and static entries
break;
case SAI_FDB_FLUSH_ENTRY_TYPE_DYNAMIC:
if (fdb_attrs.at("SAI_FDB_ENTRY_ATTR_TYPE")->getAttr()->value.s32 != SAI_FDB_ENTRY_TYPE_DYNAMIC)
return true;
break;
case SAI_FDB_FLUSH_ENTRY_TYPE_STATIC:
if (fdb_attrs.at("SAI_FDB_ENTRY_ATTR_TYPE")->getAttr()->value.s32 != SAI_FDB_ENTRY_TYPE_STATIC)
return true;
break;
default:
SWSS_LOG_THROW("unknown SAI_FDB_FLUSH_ATTR_ENTRY_TYPE: %d", attr.value.s32);
}
break;
default:
SWSS_LOG_ERROR("flush attribute %d is not supported yet", attr.id);
return true;
}
}
// this fdb entry should be flushed
return false;
}
sai_status_t VirtualSwitchSaiInterface::flushFdbEntries(
_In_ sai_object_id_t switch_id,
_In_ uint32_t attr_count,
_In_ const sai_attribute_t *attr_list)
{
SWSS_LOG_ENTER();
/*
* There are 3 databases for fdb entries, one is in metadata and second is
* in g_fdb_info_set and third is local virtual switch. Second one holds
* all dynamic entries learned from interfaces. All learned entries are
* processed by metadata and propagated from info set to metadata. But
* there may be short period of time that those 3 sets will be out of sync
* until notifications learned/aged will be sent.
*
* After learning fdb event, fdb entry will be put into 3 places:
* - local db m_switchStateMap
* - metadata db, and
* - g_fdb_info_set (contains only learned entries)
*
* This call should clear m_switchStateMap and g_fdb_info_set, and
* metadata db should be cleared by flush notification handler.
*/
auto ss = m_switchStateMap.at(switch_id);
// TODO move some part to switch state
auto &fdbs = ss->m_objectHash.at(SAI_OBJECT_TYPE_FDB_ENTRY);
std::map<std::string, SwitchState::AttrHash> static_fdbs;
std::map<std::string, SwitchState::AttrHash> dynamic_fdbs;
for (auto it = fdbs.begin(); it != fdbs.end();)
{
if (VirtualSwitchSaiInterface::doesFdbEntryNotMatchFlushAttr(it->first, it->second, attr_count, attr_list))
{
/*
* If fdb entry does not match flush attributes, we will skip this entry.
*/
++it;
}
else
{
/*
* Entries to be flushed we need to split for 2 groups static and
* dynamic entries.
*/
sai_fdb_entry_type_t type = (sai_fdb_entry_type_t)it->second.at("SAI_FDB_ENTRY_ATTR_TYPE")->getAttr()->value.s32;
switch (type)
{
case SAI_FDB_ENTRY_TYPE_DYNAMIC:
dynamic_fdbs.insert(*it);
break;
case SAI_FDB_ENTRY_TYPE_STATIC:
static_fdbs.insert(*it);
break;
default:
SWSS_LOG_ERROR("unsupported fdb_entry type %d on %s", type, it->first.c_str());
break;
}
// update fdb info set
FdbInfo fi;
// If fdb entry has bv_id set to vlan object type then we can try to get vlan number from
// that object and populate vlan_id in fdb_info. If bv_id is bridge object type then vlan
// must be zero, since there can be only 1 (assuming) mac address on a given bridge.
sai_fdb_entry_t fdb_entry;
sai_deserialize_fdb_entry(it->first, fdb_entry);
fi.setFdbEntry(fdb_entry);
if (objectTypeQuery(fdb_entry.bv_id) == SAI_OBJECT_TYPE_VLAN)
{
sai_attribute_t attr;
attr.id = SAI_VLAN_ATTR_VLAN_ID;
sai_status_t status = get(SAI_OBJECT_TYPE_VLAN, fdb_entry.bv_id, 1, &attr);
if (status != SAI_STATUS_SUCCESS)
{
SWSS_LOG_ERROR("failed to get vlan_id for vlan object: %s",
sai_serialize_object_id(fdb_entry.bv_id).c_str());
return SAI_STATUS_FAILURE;
}
SWSS_LOG_INFO("got vlan id %d", attr.value.u16);
fi.setVlanId(attr.value.u16);
}
auto fit = ss->m_fdb_info_set.find(fi);
if (fit == ss->m_fdb_info_set.end())
{
// this may happen if vlan is invalid
SWSS_LOG_ERROR("failed to find fdb entry in info set: %s, learn for this MAC will be disabled", it->first.c_str());
}
else
{
ss->m_fdb_info_set.erase(fit);
}
/*
* Since we are using &on fdbs then this will also clear local
* data base.
*/
it = fdbs.erase(it);
}
}
/*
* We can have 3 attributes (so far) to flush by:
* - entry type
* - bridge port id
* - vlan id
*
* We are sending consolidated FLUSH event to not send each fdb entry 1 by
* 1 in that case we need to mark data in special way reusing current
* attributes.
*
* Since flush event is considered like regular fdb event, data inside will
* be special. Indication that this is flush event will be by actual event
* type and consolidation will marked as MAC address 00:00:00:00:00:00.
* In that case user receiving notification will know that flush event is
* consolidated event and not actual fdb entry.
*
* To indicate what entry type was flushed, entry_type field in fdb_entry
* will be populated. If no entry will be specified 2 notifications will be
* sent 1 for static entries and 1 for dynamic entries, or 1 notification
* with 2 data entries will be send.
*
* To indicate what vlan if was flushed, vlan_id field in fdb_entry will be
* populated with flushed vlan_id. If no vlan has been set, vlan_id will be
* zero.
*
* To indicate what bridge port id was flushed,
* SAI_FDB_ENTRY_ATTR_BRIDGE_PORT_ID attribute will be added with oid value
* set to that bridge port id, or if no bridge port id was specified then
* this attribute will be missing or oid will be set to SAI_NULL_OBJECT_ID.
*
* By those rules user receiving notification can figure out what flush
* event is handling.
*
* All other attributes in consolidated fdb event notification are
* irrelevant.
*/
SWSS_LOG_NOTICE("generating fdb flush notifications");
sai_fdb_event_notification_data_t data;
sai_attribute_t attrs[2];
memset(&data, 0, sizeof(data));
memset(attrs, 0, sizeof(attrs));
data.event_type = SAI_FDB_EVENT_FLUSHED;
data.fdb_entry.switch_id = switch_id;
data.attr_count = 1;
data.attr = attrs;
auto bpid = sai_metadata_get_attr_by_id(SAI_FDB_FLUSH_ATTR_BRIDGE_PORT_ID, attr_count, attr_list);
if (bpid != NULL)
{
data.attr_count = 2;
attrs[1].id = SAI_FDB_ENTRY_ATTR_BRIDGE_PORT_ID;
attrs[1].value.oid = bpid->value.oid;
}
auto vlanid = sai_metadata_get_attr_by_id(SAI_FDB_FLUSH_ATTR_BV_ID, attr_count, attr_list);
if (vlanid != NULL)
{
data.fdb_entry.bv_id = vlanid->value.oid;
}
if (static_fdbs.size())
{
SWSS_LOG_NOTICE("flushing %zu static entries", static_fdbs.size());
attrs[0].id = SAI_FDB_ENTRY_ATTR_TYPE;
attrs[0].value.s32 = SAI_FDB_ENTRY_TYPE_STATIC;
ss->send_fdb_event_notification(data);
}
if (dynamic_fdbs.size())
{
SWSS_LOG_NOTICE("flushing %zu dynamic entries", dynamic_fdbs.size());
attrs[0].id = SAI_FDB_ENTRY_ATTR_TYPE;
attrs[0].value.s32 = SAI_FDB_ENTRY_TYPE_DYNAMIC;
ss->send_fdb_event_notification(data);
}
// NOTE: we can add config entry to send notifications 1 by 1 as option to consolidated
return SAI_STATUS_SUCCESS;
}
void VirtualSwitchSaiInterface::ageFdbs()
{
SWSS_LOG_ENTER();
for (auto& it: m_switchStateMap)
{
it.second->processFdbEntriesForAging();
}
}