Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[DASH] add PA Validation orch #3357

Open
wants to merge 2 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions orchagent/Makefile.am
Original file line number Diff line number Diff line change
Expand Up @@ -113,6 +113,7 @@ orchagent_SOURCES = \
dash/dashaclorch.cpp \
dash/dashaclgroupmgr.cpp \
dash/dashtagmgr.cpp \
dash/dashpavalidationorch.cpp \
dash/pbutils.cpp \
twamporch.cpp

Expand Down
364 changes: 364 additions & 0 deletions orchagent/dash/dashpavalidationorch.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,364 @@
#include "dashpavalidationorch.h"

#include "dashvnetorch.h"
#include "crmorch.h"

#include "taskworker.h"
#include "pbutils.h"
#include "saihelper.h"
#include "swssnet.h"

#include <cinttypes>
#include <boost/functional/hash.hpp>

extern sai_dash_pa_validation_api_t* sai_dash_pa_validation_api;
extern size_t gMaxBulkSize;
extern CrmOrch *gCrmOrch;
extern sai_object_id_t gSwitchId;

using namespace std;
using namespace swss;

size_t IpAddressHash::operator()(swss::IpAddress addr) const
{
size_t seed = 0;
const auto &inner = addr.getIp();
boost::hash_combine(seed, inner.family);
if (inner.family == AF_INET)
{
boost::hash_combine(seed, inner.ip_addr.ipv4_addr);
}
else if (inner.family == AF_INET6)
{
boost::hash_combine(seed, inner.ip_addr.ipv6_addr);
}
return seed;
}

DashPaValidationOrch::DashPaValidationOrch(swss::DBConnector *db, swss::ZmqServer *zmqServer):
vnetOrch_(nullptr),
ZmqOrch(db, {APP_DASH_PA_VALIDATION_TABLE_NAME}, zmqServer)
{
SWSS_LOG_ENTER();
}

void DashPaValidationOrch::setVnetOrch(const DashVnetOrch *vnetOrch)
{
vnetOrch_ = vnetOrch;
}

bool DashPaValidationOrch::parseVni(const std::string& key, uint32_t& vni) {
std::istringstream iss(key);

iss >> vni;

return !iss.fail() && iss.eof();
}

bool DashPaValidationOrch::entryRefCountInc(const PaValidationEntry& entry)
{
SWSS_LOG_ENTER();

bool new_entry = false;

auto vni = vni_map_.find(entry.vni);
if (vni == vni_map_.end())
{
new_entry = true;
vni_map_[entry.vni] = {{entry.address, 1}};
} else {
auto& vni_addresses = vni->second;
auto address = vni_addresses.find(entry.address);
if (address == vni_addresses.end())
{
new_entry = true;
vni_addresses[entry.address] = 1;
} else {
vni_addresses[entry.address]++;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can this condition happen? PA validation entry addition for same {VNI, IpAddress}??

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes, as part of DASH_VNET_MAPPING_TABLE processing:

auto it = pa_refcount_table_.find(pa_ref_key);

}

SWSS_LOG_DEBUG("PA Validation entry (%u, %s) refcount is increased to %" PRIu64, entry.vni, entry.address.to_string().c_str(), vni_addresses[entry.address]);
}

return new_entry;
}

bool DashPaValidationOrch::entryRefCountDec(const PaValidationEntry& entry)
{
SWSS_LOG_ENTER();

auto vni = vni_map_.find(entry.vni);
if (vni == vni_map_.end())
{
SWSS_LOG_WARN("PA Validaion entry VNI %u IP %s is removed or not created yet", entry.vni, entry.address.to_string().c_str());
return false;
}

auto& vni_addresses = vni->second;
auto address = vni_addresses.find(entry.address);
if (address == vni_addresses.end())
{
SWSS_LOG_WARN("PA Validaion entry VNI %u IP %s is removed or not created yet", entry.vni, entry.address.to_string().c_str());
return false;
}

SWSS_LOG_DEBUG("PA Validation entry (%u, %s) refcount is decreased to %" PRIu64, entry.vni, entry.address.to_string().c_str(), address->second - 1);

if (address->second == 1)
{
vni_addresses.erase(address);
if (vni_addresses.empty())
{
vni_map_.erase(entry.vni);
}

return true;
} else {
address->second--;
return false;
}
}

bool DashPaValidationOrch::fetchVniAddresses(uint32_t vni, PaValidationEntryAddresses& addresses) const
{
SWSS_LOG_ENTER();

auto it = vni_map_.find(vni);
if (it == vni_map_.end())
{
return true;
}

const auto& vni_addresses = it->second;

addresses.reserve(vni_addresses.size());
transform(vni_addresses.begin(), vni_addresses.end(), std::back_inserter(addresses),
[](const auto& kv) { return kv.first; });

return false;
}

void DashPaValidationOrch::paValidationConfigProcess(const PaValidationEntryList& toCreate, const PaValidationEntryList& toRemove)
{
SWSS_LOG_ENTER();

auto task_status = paValidationConfigApply(toCreate, toRemove);
if (task_status != task_success)
{
parseHandleSaiStatusFailure(task_status);
}
}

task_process_status DashPaValidationOrch::paValidationConfigApply(const PaValidationEntryList& toCreate, const PaValidationEntryList& toRemove)
{
EntityBulker<sai_dash_pa_validation_api_t> bulker(sai_dash_pa_validation_api, gMaxBulkSize);
std::vector<sai_status_t> statuses;

SWSS_LOG_ENTER();

statuses.reserve(toCreate.size() + toRemove.size());
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

should we check PA validation CRM before calling bulk addition?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The CRM checks are done by the CRM Orch, while the Orchs that are adding/removing the resources just update the usage. The API of CRM Orch:

void incCrmResUsedCounter(CrmResourceType resource);
void decCrmResUsedCounter(CrmResourceType resource);
// Increment "used" counter for the ACL table/group CRM resources
void incCrmAclUsedCounter(CrmResourceType resource, sai_acl_stage_t stage, sai_acl_bind_point_type_t point);
// Decrement "used" counter for the ACL table/group CRM resources
void decCrmAclUsedCounter(CrmResourceType resource, sai_acl_stage_t stage, sai_acl_bind_point_type_t point, sai_object_id_t oid);
// Increment "used" counter for the per ACL table CRM resources (ACL entry/counter)
void incCrmAclTableUsedCounter(CrmResourceType resource, sai_object_id_t tableId);
// Decrement "used" counter for the per ACL table CRM resources (ACL entry/counter)
void decCrmAclTableUsedCounter(CrmResourceType resource, sai_object_id_t tableId);
// Increment "used" counter for the EXT table CRM resources
void incCrmExtTableUsedCounter(CrmResourceType resource, std::string table_name);
// Decrement "used" counter for the EXT table CRM resources
void decCrmExtTableUsedCounter(CrmResourceType resource, std::string table_name);
// Increment "used" counter for the per DASH ACL CRM resources (ACL group/rule)
void incCrmDashAclUsedCounter(CrmResourceType resource, sai_object_id_t groupId);
// Decrement "used" counter for the per DASH ACL CRM resources (ACL group/rule)
void decCrmDashAclUsedCounter(CrmResourceType resource, sai_object_id_t groupId);


for (const auto& entry: toCreate)
{
bool new_entry = entryRefCountInc(entry);
if (!new_entry)
{
// mark entry as created to omit error handling
statuses.emplace_back(SAI_STATUS_SUCCESS);
} else {
sai_attribute_t attr;
uint32_t attr_count = 1;

sai_pa_validation_entry_t sai_entry;
sai_entry.vnet_id = entry.vnet_oid;
sai_entry.switch_id = gSwitchId;
swss::copy(sai_entry.sip, entry.address);

attr.id = SAI_PA_VALIDATION_ENTRY_ATTR_ACTION;
attr.value.u32 = SAI_PA_VALIDATION_ENTRY_ACTION_PERMIT;

statuses.emplace_back();
bulker.create_entry(&statuses.back(), &sai_entry, attr_count, &attr);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

How do we decrement vni_map refcount if there is any failure in SAI addition?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The SAI failure leads to the abort(), so reverting the refcount/CRM right before that doesn't have a lot of value.

handleSaiFailure(true);

If you prefer to have it (for consistency/clarity) please let me know, and I'll add it.


gCrmOrch->incCrmResUsedCounter(entry.address.isV4() ? CrmResourceType::CRM_DASH_IPV4_PA_VALIDATION : CrmResourceType::CRM_DASH_IPV6_PA_VALIDATION);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Does this CRM_DASH_IPV4_PA_VALIDATION and CRM_DASH_IPV6_PA_VALIDATION include both outbound and inbound direction entries?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Currently, there is no distinction between inbound/outbound PA Validation entries on SAI/DASH level.

}
}

for (const auto& entry: toRemove)
{
bool last_ref = entryRefCountDec(entry);
if (!last_ref)
{
// mark entry as removed to omit error handling
statuses.emplace_back(SAI_STATUS_SUCCESS);
} else {
sai_pa_validation_entry_t sai_entry;
sai_entry.vnet_id = entry.vnet_oid;
sai_entry.switch_id = gSwitchId;
swss::copy(sai_entry.sip, entry.address);

statuses.emplace_back();
bulker.remove_entry(&statuses.back(), &sai_entry);

gCrmOrch->decCrmResUsedCounter(entry.address.isV4() ? CrmResourceType::CRM_DASH_IPV4_PA_VALIDATION : CrmResourceType::CRM_DASH_IPV6_PA_VALIDATION);
}
}

bulker.flush();

size_t i = 0;
for (; i < toCreate.size(); i++)
{
const auto& entry = toCreate[i];
const auto& status = statuses[i];

if (status == SAI_STATUS_SUCCESS)
{
continue;
}

SWSS_LOG_ERROR("Failed to create PA validation entry for VNI %u IP %s", entry.vni, entry.address.to_string().c_str());
task_process_status handle_status = handleSaiCreateStatus((sai_api_t) SAI_API_DASH_PA_VALIDATION, status);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

handle refcount here for any failures?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The same as above, if you prefer to revert the refcount/CRM before the abort(), let me know.

if (handle_status != task_success)
{
return handle_status;
}
}

for (;i < toRemove.size(); i++)
{
const auto& entry = toRemove[i];
const auto& status = statuses[i];

if (status == SAI_STATUS_SUCCESS)
{
continue;
}

SWSS_LOG_ERROR("Failed to remvoe PA validation entry for VNI %u IP %s", entry.vni, entry.address.to_string().c_str());
task_process_status handle_status = handleSaiCreateStatus((sai_api_t) SAI_API_DASH_PA_VALIDATION, status);
if (handle_status != task_success)
{
return handle_status;
}
}

return task_success;
}

task_process_status DashPaValidationOrch::addPaValidationEntries(const PaValidationEntryList& entries)
{
return paValidationConfigApply(entries, {});
}

task_process_status DashPaValidationOrch::removePaValidationEntries(const PaValidationEntryList& entries)
{
return paValidationConfigApply({}, entries);
}

void DashPaValidationOrch::doTask(ConsumerBase &consumer)
{
SWSS_LOG_ENTER();

const auto& tn = consumer.getTableName();

SWSS_LOG_INFO("Table name: %s", tn.c_str());

if (tn == APP_DASH_PA_VALIDATION_TABLE_NAME)
{
doTaskPaValidation(consumer);
}
else
{
SWSS_LOG_ERROR("Unknown table: %s", tn.c_str());
}
}

void DashPaValidationOrch::doTaskPaValidation(ConsumerBase &consumer)
{
SWSS_LOG_ENTER();

PaValidationEntryList toCreate, toRemove;

auto it = consumer.m_toSync.begin();
while (it != consumer.m_toSync.end())
{
auto tuple = it->second;
string key = kfvKey(tuple);
string op = kfvOp(tuple);

uint32_t vni;
if (!parseVni(key, vni))
{
SWSS_LOG_WARN("Failed to parse VNI from PA Validation key %s", key.c_str());
it = consumer.m_toSync.erase(it);
continue;
}

sai_object_id_t vnet_oid;
if (!vnetOrch_->getVnetByVni(vni, vnet_oid))
{
SWSS_LOG_INFO("VNET for VNI %u is not created yet or removed", vni);
it++;
continue;
}

if (op == SET_COMMAND)
{
dash::pa_validation::PaValidation pbEntry;
if (!parsePbMessage(kfvFieldsValues(tuple), pbEntry))
{
SWSS_LOG_WARN("Failed to parse protobuff messaage for PA Validation (vni: %u)", vni);
it = consumer.m_toSync.erase(it);
continue;
}

PaValidationEntryAddresses addresses;
if (!toAddresses(pbEntry, addresses))
{
SWSS_LOG_WARN("Failed to parse PA Validation (vni: %u) addresses", vni);
it = consumer.m_toSync.erase(it);
continue;
}

for (const auto addr : addresses)
{
toCreate.push_back(PaValidationEntry{vnet_oid, vni, addr});
}

it = consumer.m_toSync.erase(it);
}
else if (op == DEL_COMMAND)
{
PaValidationEntryAddresses addresses;
bool empty = fetchVniAddresses(vni, addresses);
if (empty)
{
SWSS_LOG_WARN("PA validation entries for VNI %u are already removed or not created yet", vni);
it = consumer.m_toSync.erase(it);
continue;
}

for (const auto addr : addresses)
{
toRemove.push_back(PaValidationEntry{vnet_oid, vni, addr});
}

it = consumer.m_toSync.erase(it);
}
}

paValidationConfigProcess(toCreate, toRemove);
}

bool DashPaValidationOrch::toAddresses(const dash::pa_validation::PaValidation& entry, PaValidationEntryAddresses &addresses)
{
addresses.reserve(entry.addresses().size());

for (const auto& addr : entry.addresses())
{
addresses.push_back(IpAddress(to_swss(addr)));
}

return true;
}
Loading
Loading