vslib/SwitchStateBaseMACsec.cpp (698 lines of code) (raw):
#include "MACsecAttr.h"
#include "SwitchStateBase.h"
#include "meta/sai_serialize.h"
#include "swss/exec.h"
#include "swss/logger.h"
#include <cinttypes>
#include <string>
#include <vector>
#include <regex>
#include <net/if.h>
#include <arpa/inet.h>
#include <byteswap.h>
#include <endian.h>
using namespace saivs;
#define SAI_VS_MACSEC_PREFIX "macsec_"
#define MACSEC_SYSTEM_IDENTIFIER (12)
#define MACSEC_PORT_IDENTIFIER (4)
#define MACSEC_SCI_LENGTH (MACSEC_SYSTEM_IDENTIFIER + MACSEC_PORT_IDENTIFIER)
#define SAI_METADATA_GET_ATTR_BY_ID(attr, attrId, attrCount, attrList) \
{ \
attr = sai_metadata_get_attr_by_id(attrId, attrCount, attrList); \
if (attr == NULL) \
{ \
SWSS_LOG_ERROR("attr " #attrId " was not passed"); \
return SAI_STATUS_FAILURE; \
} \
}
sai_status_t SwitchStateBase::setAclEntryMACsecFlowActive(
_In_ sai_object_id_t entryId,
_In_ const sai_attribute_t *attr)
{
SWSS_LOG_ENTER();
if (attr == nullptr || entryId == SAI_NULL_OBJECT_ID)
{
SWSS_LOG_ERROR("attr or entryId is null");
return SAI_STATUS_INVALID_PARAMETER;
}
if (attr->id != SAI_ACL_ENTRY_ATTR_ACTION_MACSEC_FLOW)
{
auto meta = sai_metadata_get_attr_metadata(SAI_OBJECT_TYPE_ACL_ENTRY, attr->id);
SWSS_LOG_ERROR("Attribute type (%s) isn't correct, expect SAI_ACL_ENTRY_ATTR_ACTION_MACSEC_FLOW", meta->attridname);
return SAI_STATUS_INVALID_PARAMETER;
}
// Enable MACsec
if (attr->value.aclaction.enable)
{
SWSS_LOG_DEBUG("Enable MACsec on entry %s", sai_serialize_object_id(entryId).c_str());
auto sid = sai_serialize_object_id(entryId);
CHECK_STATUS(set_internal(SAI_OBJECT_TYPE_ACL_ENTRY, sid, attr));
std::vector<MACsecAttr> macsecAttrs;
if (loadMACsecAttrsFromACLEntry(entryId, attr, SAI_OBJECT_TYPE_MACSEC_SA, macsecAttrs) == SAI_STATUS_SUCCESS)
{
// In Linux MACsec model, Egress SA need to be created before ingress SA
for (auto &macsecAttr : macsecAttrs)
{
if (macsecAttr.m_direction == SAI_MACSEC_DIRECTION_EGRESS)
{
if (m_macsecManager.create_macsec_sa(macsecAttr))
{
SWSS_LOG_NOTICE(
"Enable MACsec SA %s:%u at the device %s",
macsecAttr.m_sci.c_str(),
static_cast<std::uint32_t>(macsecAttr.m_an),
macsecAttr.m_macsecName.c_str());
}
}
}
for (auto &macsecAttr : macsecAttrs)
{
if (macsecAttr.m_direction == SAI_MACSEC_DIRECTION_INGRESS)
{
if (m_macsecManager.create_macsec_sa(macsecAttr))
{
SWSS_LOG_NOTICE(
"Enable MACsec SA %s:%u at the device %s",
macsecAttr.m_sci.c_str(),
static_cast<std::uint32_t>(macsecAttr.m_an),
macsecAttr.m_macsecName.c_str());
}
else
{
m_uncreatedIngressMACsecSAs.insert(macsecAttr);
}
}
}
}
else
{
SWSS_LOG_WARN("Cannot load MACsec SA from the entry %s", sai_serialize_object_id(entryId).c_str());
}
}
// Disable MACsec
else
{
SWSS_LOG_DEBUG("Disable MACsec on entry %s", sai_serialize_object_id(entryId).c_str());
std::vector<MACsecAttr> macsecAttrs;
if (loadMACsecAttrsFromACLEntry(entryId, attr, SAI_OBJECT_TYPE_MACSEC_SC, macsecAttrs) == SAI_STATUS_SUCCESS)
{
for (auto &macsecAttr : macsecAttrs)
{
if (m_macsecManager.delete_macsec_sa(macsecAttr))
{
SWSS_LOG_NOTICE(
"Delete MACsec SA %s:%u at the device %s",
macsecAttr.m_sci.c_str(),
static_cast<std::uint32_t>(macsecAttr.m_an),
macsecAttr.m_macsecName.c_str());
}
}
}
else
{
SWSS_LOG_WARN("Cannot load MACsec SC from the entry %s", sai_serialize_object_id(entryId).c_str());
}
CHECK_STATUS(set_internal(SAI_OBJECT_TYPE_ACL_ENTRY, sai_serialize_object_id(entryId), attr));
}
return SAI_STATUS_SUCCESS;
}
sai_status_t SwitchStateBase::setMACsecSA(
_In_ sai_object_id_t macsec_sa_id,
_In_ const sai_attribute_t* attr)
{
SWSS_LOG_ENTER();
MACsecAttr macsecAttr;
CHECK_STATUS(loadMACsecAttr(SAI_OBJECT_TYPE_MACSEC_SA, macsec_sa_id, macsecAttr));
if (attr->id == SAI_MACSEC_SA_ATTR_MINIMUM_INGRESS_XPN || attr->id == SAI_MACSEC_SA_ATTR_CONFIGURED_EGRESS_XPN)
{
if (!m_macsecManager.update_macsec_sa_pn(macsecAttr, attr->value.u64))
{
SWSS_LOG_WARN("Fail to update PN (%" PRIu64 ") of MACsec SA %s", attr->value.u64, sai_serialize_object_id(macsec_sa_id).c_str());
return SAI_STATUS_FAILURE;
}
}
auto sid = sai_serialize_object_id(macsec_sa_id);
return set_internal(SAI_OBJECT_TYPE_MACSEC_SA, sid, attr);
}
sai_status_t SwitchStateBase::createMACsecPort(
_In_ sai_object_id_t macsecSaId,
_In_ sai_object_id_t switchId,
_In_ uint32_t attrCount,
_In_ const sai_attribute_t *attrList)
{
SWSS_LOG_ENTER();
MACsecAttr macsecAttr;
if (loadMACsecAttr(SAI_OBJECT_TYPE_MACSEC_PORT, macsecSaId, attrCount, attrList, macsecAttr) == SAI_STATUS_SUCCESS)
{
if (m_macsecManager.create_macsec_port(macsecAttr))
{
SWSS_LOG_NOTICE("Enable MACsec port %s", macsecAttr.m_macsecName.c_str());
}
}
auto sid = sai_serialize_object_id(macsecSaId);
return create_internal(SAI_OBJECT_TYPE_MACSEC_PORT, sid, switchId, attrCount, attrList);
}
sai_status_t SwitchStateBase::createMACsecSC(
_In_ sai_object_id_t macsecScId,
_In_ sai_object_id_t switchId,
_In_ uint32_t attrCount,
_In_ const sai_attribute_t *attrList)
{
SWSS_LOG_ENTER();
MACsecAttr macsecAttr;
if (loadMACsecAttr(SAI_OBJECT_TYPE_MACSEC_SC, macsecScId, attrCount, attrList, macsecAttr) == SAI_STATUS_SUCCESS)
{
if (m_macsecManager.create_macsec_sc(macsecAttr))
{
SWSS_LOG_NOTICE(
"Create MACsec SC %s at the device %s",
macsecAttr.m_sci.c_str(),
macsecAttr.m_macsecName.c_str());
}
}
auto sid = sai_serialize_object_id(macsecScId);
return create_internal(SAI_OBJECT_TYPE_MACSEC_SC, sid, switchId, attrCount, attrList);
}
sai_status_t SwitchStateBase::createMACsecSA(
_In_ sai_object_id_t macsecSaId,
_In_ sai_object_id_t switchId,
_In_ uint32_t attrCount,
_In_ const sai_attribute_t *attrList)
{
SWSS_LOG_ENTER();
MACsecAttr macsecAttr;
if (loadMACsecAttr(SAI_OBJECT_TYPE_MACSEC_SA, macsecSaId, attrCount, attrList, macsecAttr) == SAI_STATUS_SUCCESS)
{
if (m_macsecManager.create_macsec_sa(macsecAttr))
{
SWSS_LOG_NOTICE(
"Enable MACsec SA %s:%u at the device %s",
macsecAttr.m_sci.c_str(),
static_cast<std::uint32_t>(macsecAttr.m_an),
macsecAttr.m_macsecName.c_str());
// Maybe there are some uncreated ingress SAs that were added into m_uncreatedIngressMACsecSAs
// because the corresponding egress SA has not been created.
// So retry to create them.
if (macsecAttr.m_direction == SAI_MACSEC_DIRECTION_EGRESS)
{
retryCreateIngressMaCsecSAs();
}
}
else
{
// In Linux MACsec model, Egress SA need to be created before ingress SA.
// So, if try to create the ingress SA firstly, it will failed.
// But to create the egress SA should be always successful.
m_uncreatedIngressMACsecSAs.insert(macsecAttr);
}
}
auto sid = sai_serialize_object_id(macsecSaId);
return create_internal(SAI_OBJECT_TYPE_MACSEC_SA, sid, switchId, attrCount, attrList);
}
sai_status_t SwitchStateBase::removeMACsecPort(
_In_ sai_object_id_t macsecPortId)
{
SWSS_LOG_ENTER();
MACsecAttr macsecAttr;
if (loadMACsecAttr(SAI_OBJECT_TYPE_MACSEC_PORT, macsecPortId, macsecAttr) == SAI_STATUS_SUCCESS)
{
if (m_macsecManager.delete_macsec_port(macsecAttr))
{
SWSS_LOG_NOTICE("The MACsec port %s is deleted", macsecAttr.m_macsecName.c_str());
}
}
auto flowItr = m_macsecFlowPortMap.begin();
while (flowItr != m_macsecFlowPortMap.end())
{
if (flowItr->second == macsecPortId)
{
flowItr = m_macsecFlowPortMap.erase(flowItr);
}
else
{
flowItr ++;
}
}
auto saItr = m_uncreatedIngressMACsecSAs.begin();
while (saItr != m_uncreatedIngressMACsecSAs.end())
{
if (saItr->m_macsecName == macsecAttr.m_macsecName)
{
saItr = m_uncreatedIngressMACsecSAs.erase(saItr);
}
else
{
saItr ++;
}
}
auto sid = sai_serialize_object_id(macsecPortId);
return remove_internal(SAI_OBJECT_TYPE_MACSEC_PORT, sid);
}
sai_status_t SwitchStateBase::removeMACsecSC(
_In_ sai_object_id_t macsecScId)
{
SWSS_LOG_ENTER();
MACsecAttr macsecAttr;
if (loadMACsecAttr(SAI_OBJECT_TYPE_MACSEC_SC, macsecScId, macsecAttr) == SAI_STATUS_SUCCESS)
{
if (m_macsecManager.delete_macsec_sc(macsecAttr))
{
SWSS_LOG_NOTICE(
"The MACsec sc %s in device %s is deleted",
macsecAttr.m_sci.c_str(),
macsecAttr.m_macsecName.c_str());
}
}
auto saItr = m_uncreatedIngressMACsecSAs.begin();
while (saItr != m_uncreatedIngressMACsecSAs.end())
{
if (saItr->m_macsecName == macsecAttr.m_macsecName && saItr->m_sci == macsecAttr.m_sci)
{
saItr = m_uncreatedIngressMACsecSAs.erase(saItr);
}
else
{
saItr ++;
}
}
auto sid = sai_serialize_object_id(macsecScId);
return remove_internal(SAI_OBJECT_TYPE_MACSEC_SC, sid);
}
sai_status_t SwitchStateBase::removeMACsecSA(
_In_ sai_object_id_t macsecSaId)
{
SWSS_LOG_ENTER();
MACsecAttr macsecAttr;
if (loadMACsecAttr(SAI_OBJECT_TYPE_MACSEC_SA, macsecSaId, macsecAttr) == SAI_STATUS_SUCCESS)
{
if (m_macsecManager.delete_macsec_sa(macsecAttr))
{
SWSS_LOG_NOTICE(
"The MACsec SA %s:%u at the device %s is deleted.",
macsecAttr.m_sci.c_str(),
static_cast<std::uint32_t>(macsecAttr.m_an),
macsecAttr.m_macsecName.c_str());
}
}
auto sid = sai_serialize_object_id(macsecSaId);
return remove_internal(SAI_OBJECT_TYPE_MACSEC_SA, sid);
}
sai_status_t SwitchStateBase::getACLTable(
_In_ sai_object_id_t entryId,
_Out_ sai_object_id_t &tableId)
{
SWSS_LOG_ENTER();
sai_attribute_t attr;
// Find ACL Table
attr.id = SAI_ACL_ENTRY_ATTR_TABLE_ID;
CHECK_STATUS(get(SAI_OBJECT_TYPE_ACL_ENTRY, entryId, 1, &attr));
tableId = attr.value.oid;
return SAI_STATUS_SUCCESS;
}
sai_status_t SwitchStateBase::findPortByMACsecFlow(
_In_ sai_object_id_t macsecFlowId,
_Out_ sai_object_id_t &portId)
{
SWSS_LOG_ENTER();
auto itr = m_macsecFlowPortMap.find(macsecFlowId);
if (itr != m_macsecFlowPortMap.end())
{
portId = itr->second;
return SAI_STATUS_SUCCESS;
}
sai_attribute_t attr;
// MACsec flow => ACL entry => ACL table
// MACsec flow => MACsec SC
// (ACL table & MACsec SC) => port
// Find ACL Entry
attr.id = SAI_ACL_ENTRY_ATTR_ACTION_MACSEC_FLOW;
attr.value.aclaction.parameter.oid = macsecFlowId;
attr.value.aclaction.enable = true;
std::vector<sai_object_id_t> aclEntryIds;
findObjects(SAI_OBJECT_TYPE_ACL_ENTRY, attr, aclEntryIds);
if (aclEntryIds.empty())
{
// No ACL Entry is bound to the flow
SWSS_LOG_DEBUG(
"Cannot find corresponding ACL entry for the flow %s",
sai_serialize_object_id(macsecFlowId).c_str());
return SAI_STATUS_FAILURE;
}
// Find ACL Table
sai_object_id_t aclTableId;
// All ACL entries with same MACsec flow correspond one ACL Table
// Because an ACL Table corresponds one MACsec Port
// And a flow never belongs to two MACsec Port
if (getACLTable(aclEntryIds.front(), aclTableId) != SAI_STATUS_SUCCESS)
{
SWSS_LOG_ERROR(
"Cannot find corresponding ACL table for the entry %s",
sai_serialize_object_id(aclEntryIds.front()).c_str());
return SAI_STATUS_FAILURE;
}
// Find MACsec SC
attr.id = SAI_MACSEC_SC_ATTR_FLOW_ID;
attr.value.oid = macsecFlowId;
std::vector<sai_object_id_t> macsecScIds;
findObjects(SAI_OBJECT_TYPE_MACSEC_SC, attr, macsecScIds);
// One MACsec SC will only belong to one MACsec flow
// Meanwhile one MACsec flow will just belong to one port
if (macsecScIds.empty())
{
SWSS_LOG_ERROR(
"Cannot find corresponding MACsec SC for the flow %s",
sai_serialize_object_id(macsecFlowId).c_str());
return SAI_STATUS_FAILURE;
}
attr.id = SAI_MACSEC_SC_ATTR_MACSEC_DIRECTION;
CHECK_STATUS(get(SAI_OBJECT_TYPE_MACSEC_SC, macsecScIds.front(), 1, &attr));
auto direction = attr.value.s32;
// Find port
attr.id = (direction == SAI_MACSEC_DIRECTION_EGRESS) ? SAI_PORT_ATTR_EGRESS_MACSEC_ACL : SAI_PORT_ATTR_INGRESS_MACSEC_ACL;
attr.value.oid = aclTableId;
std::vector<sai_object_id_t> port_ids;
findObjects(SAI_OBJECT_TYPE_PORT, attr, port_ids);
if (port_ids.size() != 1)
{
SWSS_LOG_ERROR(
"Expect one port to one ACL table %s, but got %zu",
sai_serialize_object_id(aclTableId).c_str(), port_ids.size());
return SAI_STATUS_FAILURE;
}
portId = port_ids.front();
m_macsecFlowPortMap[macsecFlowId] = portId;
return SAI_STATUS_SUCCESS;
}
std::shared_ptr<HostInterfaceInfo> SwitchStateBase::findHostInterfaceInfoByPort(
_In_ sai_object_id_t portId)
{
SWSS_LOG_ENTER();
std::shared_ptr<HostInterfaceInfo> info;
for (auto &kvp : m_hostif_info_map)
{
if (kvp.second->m_portId == portId)
{
info = kvp.second;
break;
}
}
return info;
}
sai_status_t SwitchStateBase::loadMACsecAttrFromMACsecPort(
_In_ sai_object_id_t objectId,
_In_ uint32_t attrCount,
_In_ const sai_attribute_t *attrList,
_Out_ MACsecAttr &macsecAttr)
{
SWSS_LOG_ENTER();
const sai_attribute_t *attr = nullptr;
SAI_METADATA_GET_ATTR_BY_ID(attr, SAI_MACSEC_PORT_ATTR_MACSEC_DIRECTION, attrCount, attrList);
macsecAttr.m_direction = attr->value.s32;
SAI_METADATA_GET_ATTR_BY_ID(attr, SAI_MACSEC_PORT_ATTR_PORT_ID, attrCount, attrList);
auto portId = attr->value.oid;
macsecAttr.m_info = findHostInterfaceInfoByPort(portId);
if (macsecAttr.m_info == nullptr)
{
SWSS_LOG_ERROR("Cannot find corresponding port %s", sai_serialize_object_id(portId).c_str());
return SAI_STATUS_FAILURE;
}
macsecAttr.m_vethName = vs_get_veth_name(macsecAttr.m_info->m_name, portId);
macsecAttr.m_macsecName = SAI_VS_MACSEC_PREFIX + macsecAttr.m_vethName;
return SAI_STATUS_SUCCESS;
}
sai_status_t SwitchStateBase::loadMACsecAttrFromMACsecSC(
_In_ sai_object_id_t objectId,
_In_ uint32_t attrCount,
_In_ const sai_attribute_t *attrList,
_Out_ MACsecAttr &macsecAttr)
{
SWSS_LOG_ENTER();
const sai_attribute_t *attr = nullptr;
SAI_METADATA_GET_ATTR_BY_ID(attr, SAI_MACSEC_SC_ATTR_MACSEC_CIPHER_SUITE, attrCount, attrList);
macsecAttr.m_cipher = MACsecAttr::get_cipher_name(attr->value.s32);
if (macsecAttr.m_cipher == MACsecAttr::CIPHER_NAME_INVALID)
{
return SAI_STATUS_FAILURE;
}
SAI_METADATA_GET_ATTR_BY_ID(attr, SAI_MACSEC_SC_ATTR_MACSEC_DIRECTION, attrCount, attrList);
macsecAttr.m_direction = attr->value.s32;
SAI_METADATA_GET_ATTR_BY_ID(attr, SAI_MACSEC_SC_ATTR_MACSEC_SCI, attrCount, attrList);
auto sci = attr->value.u64;
std::stringstream sciHexStr;
sciHexStr << std::setw(MACSEC_SCI_LENGTH) << std::setfill('0');
sciHexStr << std::hex << htobe64(sci);
macsecAttr.m_sci = sciHexStr.str();
if (macsecAttr.m_sci.length() < MACSEC_SCI_LENGTH)
{
// Fill leading zero to meet the MACsec SCI length
macsecAttr.m_sci = std::string(MACSEC_SCI_LENGTH - macsecAttr.m_sci.length(), '0') + macsecAttr.m_sci;
}
SAI_METADATA_GET_ATTR_BY_ID(attr, SAI_MACSEC_SC_ATTR_MACSEC_EXPLICIT_SCI_ENABLE, attrCount, attrList);
macsecAttr.m_sendSci = attr->value.booldata;
SAI_METADATA_GET_ATTR_BY_ID(attr, SAI_MACSEC_SC_ATTR_ENCRYPTION_ENABLE, attrCount, attrList);
macsecAttr.m_encryptionEnable = attr->value.booldata;
SAI_METADATA_GET_ATTR_BY_ID(attr, SAI_MACSEC_SC_ATTR_FLOW_ID, attrCount, attrList);
auto flow_id = attr->value.oid;
sai_object_id_t portId = SAI_NULL_OBJECT_ID;
CHECK_STATUS(findPortByMACsecFlow(flow_id, portId));
macsecAttr.m_info = findHostInterfaceInfoByPort(portId);
if (macsecAttr.m_info == nullptr)
{
SWSS_LOG_ERROR("Cannot find corresponding port %s", sai_serialize_object_id(portId).c_str());
return SAI_STATUS_FAILURE;
}
macsecAttr.m_vethName = vs_get_veth_name(macsecAttr.m_info->m_name, portId);
macsecAttr.m_macsecName = SAI_VS_MACSEC_PREFIX + macsecAttr.m_vethName;
return SAI_STATUS_SUCCESS;
}
sai_status_t SwitchStateBase::loadMACsecAttrFromMACsecSA(
_In_ sai_object_id_t objectId,
_In_ uint32_t attrCount,
_In_ const sai_attribute_t *attrList,
_Out_ MACsecAttr &macsecAttr)
{
SWSS_LOG_ENTER();
const sai_attribute_t *attr = nullptr;
SAI_METADATA_GET_ATTR_BY_ID(attr, SAI_MACSEC_SA_ATTR_SC_ID, attrCount, attrList);
// Find MACsec SC attributes
std::vector<sai_attribute_t> attrs(5);
attrs[0].id = SAI_MACSEC_SC_ATTR_FLOW_ID;
attrs[1].id = SAI_MACSEC_SC_ATTR_MACSEC_SCI;
attrs[2].id = SAI_MACSEC_SC_ATTR_ENCRYPTION_ENABLE;
attrs[3].id = SAI_MACSEC_SC_ATTR_MACSEC_CIPHER_SUITE;
attrs[4].id = SAI_MACSEC_SC_ATTR_MACSEC_EXPLICIT_SCI_ENABLE;
CHECK_STATUS(get(SAI_OBJECT_TYPE_MACSEC_SC, attr->value.oid, static_cast<uint32_t>(attrs.size()), attrs.data()));
macsecAttr.m_cipher = MACsecAttr::get_cipher_name(attrs[3].value.s32);
if (macsecAttr.m_cipher == MACsecAttr::CIPHER_NAME_INVALID)
{
return SAI_STATUS_FAILURE;
}
auto flow_id = attrs[0].value.oid;
auto sci = attrs[1].value.u64;
std::stringstream sciHexStr;
macsecAttr.m_encryptionEnable = attrs[2].value.booldata;
bool is_sak_128_bit = (attrs[3].value.s32 == SAI_MACSEC_CIPHER_SUITE_GCM_AES_128 || attrs[3].value.s32 == SAI_MACSEC_CIPHER_SUITE_GCM_AES_XPN_128);
macsecAttr.m_sendSci = attrs[4].value.booldata;
sciHexStr << std::setw(MACSEC_SCI_LENGTH) << std::setfill('0');
#if __BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__
sciHexStr << std::hex << bswap_64(sci);
#else
sciHexStr << std::hex << sci;
#endif
macsecAttr.m_sci = sciHexStr.str();
if (macsecAttr.m_sci.length() != MACSEC_SCI_LENGTH)
{
SWSS_LOG_ERROR("Invalid SCI %s", macsecAttr.m_sci.c_str());
return SAI_STATUS_FAILURE;
}
SAI_METADATA_GET_ATTR_BY_ID(attr, SAI_MACSEC_SA_ATTR_MACSEC_DIRECTION, attrCount, attrList);
macsecAttr.m_direction = attr->value.s32;
// Find veth name
sai_object_id_t portId = SAI_NULL_OBJECT_ID;
CHECK_STATUS(findPortByMACsecFlow(flow_id, portId));
macsecAttr.m_info = findHostInterfaceInfoByPort(portId);
if (macsecAttr.m_info == nullptr)
{
SWSS_LOG_ERROR("Cannot find corresponding port %s", sai_serialize_object_id(portId).c_str());
return SAI_STATUS_FAILURE;
}
macsecAttr.m_vethName = vs_get_veth_name(macsecAttr.m_info->m_name, portId);
macsecAttr.m_macsecName = SAI_VS_MACSEC_PREFIX + macsecAttr.m_vethName;
SAI_METADATA_GET_ATTR_BY_ID(attr, SAI_MACSEC_SA_ATTR_AN, attrCount, attrList);
macsecAttr.m_an = attr->value.u8;
SAI_METADATA_GET_ATTR_BY_ID(attr, SAI_MACSEC_SA_ATTR_SAK, attrCount, attrList);
macsecAttr.m_sak = sai_serialize_hex_binary(attr->value.macsecsak);
if (is_sak_128_bit)
{
macsecAttr.m_sak = macsecAttr.m_sak.substr(macsecAttr.m_sak.length() / 2, macsecAttr.m_sak.length() / 2);
}
SAI_METADATA_GET_ATTR_BY_ID(attr, SAI_MACSEC_SA_ATTR_AUTH_KEY, attrCount, attrList);
macsecAttr.m_authKey = sai_serialize_hex_binary(attr->value.macsecauthkey);
if (macsecAttr.m_direction == SAI_MACSEC_DIRECTION_EGRESS)
{
SAI_METADATA_GET_ATTR_BY_ID(attr, SAI_MACSEC_SA_ATTR_CONFIGURED_EGRESS_XPN, attrCount, attrList);
}
else
{
SAI_METADATA_GET_ATTR_BY_ID(attr, SAI_MACSEC_SA_ATTR_MINIMUM_INGRESS_XPN, attrCount, attrList);
}
macsecAttr.m_pn = attr->value.u64;
if (macsecAttr.is_xpn())
{
SAI_METADATA_GET_ATTR_BY_ID(attr, SAI_MACSEC_SA_ATTR_MACSEC_SSCI, attrCount, attrList);
// The Linux kernel directly uses ssci to XOR with the salt that is network order,
// So, this conversion is useful to convert SSCI from the host order to network order.
//
// Starting with Debian Bookworm (iproute2 6.1), ssci is interpreted as a hex string,
// so this needs to convert the ssci integer to a hex string, and doesn't need to change
// the encoding at this point.
std::stringstream ssciHexStr;
ssciHexStr << std::hex << attr->value.u32;
macsecAttr.m_ssci = ssciHexStr.str();
SAI_METADATA_GET_ATTR_BY_ID(attr, SAI_MACSEC_SA_ATTR_SALT, attrCount, attrList);
macsecAttr.m_salt = sai_serialize_hex_binary(attr->value.macsecsalt);
}
return SAI_STATUS_SUCCESS;
}
sai_status_t SwitchStateBase::loadMACsecAttr(
_In_ sai_object_type_t objectType,
_In_ sai_object_id_t objectId,
_In_ uint32_t attrCount,
_In_ const sai_attribute_t *attrList,
_Out_ MACsecAttr &macsecAttr)
{
SWSS_LOG_ENTER();
if (attrList == nullptr || attrCount == 0)
{
SWSS_LOG_ERROR("Attribute list is empty");
return SAI_STATUS_FAILURE;
}
switch (objectType)
{
case SAI_OBJECT_TYPE_MACSEC_PORT:
return loadMACsecAttrFromMACsecPort(objectId, attrCount, attrList, macsecAttr);
case SAI_OBJECT_TYPE_MACSEC_SC:
return loadMACsecAttrFromMACsecSC(objectId, attrCount, attrList, macsecAttr);
case SAI_OBJECT_TYPE_MACSEC_SA:
return loadMACsecAttrFromMACsecSA(objectId, attrCount, attrList, macsecAttr);
default:
SWSS_LOG_ERROR("Wrong type %s", sai_serialize_object_type(objectType).c_str());
break;
}
return SAI_STATUS_FAILURE;
}
sai_status_t SwitchStateBase::loadMACsecAttr(
_In_ sai_object_type_t objectType,
_In_ sai_object_id_t objectId,
_Out_ MACsecAttr &macsecAttr)
{
SWSS_LOG_ENTER();
std::vector<sai_attribute_t> attrs;
if (dumpObject(objectId, attrs))
{
if (loadMACsecAttr(objectType, objectId, static_cast<uint32_t>(attrs.size()), attrs.data(), macsecAttr) != SAI_STATUS_SUCCESS)
{
SWSS_LOG_DEBUG(
"Cannot load attributions of %s %s",
sai_serialize_object_type(objectType).c_str(),
sai_serialize_object_id(objectId).c_str());
return SAI_STATUS_FAILURE;
}
}
else
{
SWSS_LOG_WARN(
"The %s %s is not existed",
sai_serialize_object_type(objectType).c_str(),
sai_serialize_object_id(objectId).c_str());
return SAI_STATUS_FAILURE;
}
return SAI_STATUS_SUCCESS;
}
sai_status_t SwitchStateBase::loadMACsecAttrsFromACLEntry(
_In_ sai_object_id_t entryId,
_In_ const sai_attribute_t *entryAttr,
_In_ sai_object_type_t objectType,
_Out_ std::vector<MACsecAttr> &macsecAttrs)
{
SWSS_LOG_ENTER();
auto flow_id = entryAttr->value.aclaction.parameter.oid;
macsecAttrs.clear();
if (objectType == SAI_OBJECT_TYPE_MACSEC_PORT)
{
sai_object_id_t portId = SAI_NULL_OBJECT_ID;
CHECK_STATUS(findPortByMACsecFlow(flow_id, portId));
macsecAttrs.emplace_back();
macsecAttrs.back().m_info = findHostInterfaceInfoByPort(portId);
if (macsecAttrs.back().m_info == nullptr)
{
SWSS_LOG_ERROR("Cannot find corresponding port %s", sai_serialize_object_id(portId).c_str());
macsecAttrs.clear();
return SAI_STATUS_FAILURE;
}
macsecAttrs.back().m_vethName = vs_get_veth_name(macsecAttrs.back().m_info->m_name, portId);
macsecAttrs.back().m_macsecName = SAI_VS_MACSEC_PREFIX + macsecAttrs.back().m_vethName;
return SAI_STATUS_SUCCESS;
}
// Find all MACsec SCs that use this flow
std::vector<sai_object_id_t> macsecScs;
sai_attribute_t attr;
attr.id = SAI_MACSEC_SC_ATTR_FLOW_ID;
attr.value.oid = flow_id;
findObjects(SAI_OBJECT_TYPE_MACSEC_SC, attr, macsecScs);
if (objectType == SAI_OBJECT_TYPE_MACSEC_SC)
{
if (macsecScs.empty())
{
SWSS_LOG_DEBUG(
"No one MACsec SC is using the ACL entry %s",
sai_serialize_object_id(entryId).c_str());
}
macsecAttrs.reserve(macsecScs.size());
for (auto sc_id : macsecScs)
{
macsecAttrs.emplace_back();
if (loadMACsecAttr(SAI_OBJECT_TYPE_MACSEC_SC, sc_id, macsecAttrs.back()) != SAI_STATUS_SUCCESS)
{
// The fail log has been recorded at loadMACsecAttr
macsecAttrs.pop_back();
}
}
return SAI_STATUS_SUCCESS;
}
if (objectType == SAI_OBJECT_TYPE_MACSEC_SA)
{
// Find all MACsec SAs that use this entry
std::vector<sai_object_id_t> macsecSas;
for (auto sc_id : macsecScs)
{
attr.id = SAI_MACSEC_SA_ATTR_SC_ID;
attr.value.oid = sc_id;
findObjects(SAI_OBJECT_TYPE_MACSEC_SA, attr, macsecSas);
}
if (macsecSas.empty())
{
SWSS_LOG_DEBUG(
"No one MACsec SA is using the ACL entry %s",
sai_serialize_object_id(entryId).c_str());
}
macsecAttrs.reserve(macsecSas.size());
for (auto sa_id : macsecSas)
{
macsecAttrs.emplace_back();
if (loadMACsecAttr(SAI_OBJECT_TYPE_MACSEC_SA, sa_id, macsecAttrs.back()) != SAI_STATUS_SUCCESS)
{
// The fail log has been recorded at loadMACsecAttr
macsecAttrs.pop_back();
}
}
return SAI_STATUS_SUCCESS;
}
return SAI_STATUS_NOT_IMPLEMENTED;
}
sai_status_t SwitchStateBase::getMACsecSAPacketNumber(
_In_ sai_object_id_t macsecSaId,
_Out_ sai_attribute_t &attr)
{
SWSS_LOG_ENTER();
MACsecAttr macsecAttr;
if (loadMACsecAttr(SAI_OBJECT_TYPE_MACSEC_SA, macsecSaId, macsecAttr) == SAI_STATUS_SUCCESS)
{
if (m_macsecManager.get_macsec_sa_pn(macsecAttr, attr.value.u64))
{
return SAI_STATUS_SUCCESS;
}
}
else
{
SWSS_LOG_WARN(
"The MACsec SA %s isn't existing",
sai_serialize_object_id(macsecSaId).c_str());
}
return SAI_STATUS_FAILURE;
}
void SwitchStateBase::retryCreateIngressMaCsecSAs()
{
SWSS_LOG_ENTER();
auto itr = m_uncreatedIngressMACsecSAs.begin();
while (itr != m_uncreatedIngressMACsecSAs.end())
{
if (m_macsecManager.create_macsec_sa(*itr))
{
SWSS_LOG_NOTICE(
"Enable MACsec SA %s:%u at the device %s",
itr->m_sci.c_str(),
static_cast<std::uint32_t>(itr->m_an),
itr->m_macsecName.c_str());
itr = m_uncreatedIngressMACsecSAs.erase(itr);
}
else
{
itr ++;
}
}
}