in fboss/agent/hw/sai/switch/SaiAclTableManager.cpp [440:936]
AclEntrySaiId SaiAclTableManager::addAclEntry(
const std::shared_ptr<AclEntry>& addedAclEntry,
const std::string& aclTableName) {
// If we attempt to add entry to a table that does not exist, fail.
auto aclTableHandle = getAclTableHandle(aclTableName);
if (!aclTableHandle) {
throw FbossError(
"attempted to add AclEntry to a AclTable that does not exist: ",
aclTableName);
}
// If we already store a handle for this this Acl Entry, fail to add new one.
auto aclEntryHandle =
getAclEntryHandle(aclTableHandle, addedAclEntry->getPriority());
if (aclEntryHandle) {
throw FbossError(
"attempted to add a duplicate aclEntry: ", addedAclEntry->getID());
}
auto& aclEntryStore = saiStore_->get<SaiAclEntryTraits>();
SaiAclEntryTraits::Attributes::TableId aclTableId{
aclTableHandle->aclTable->adapterKey()};
SaiAclEntryTraits::Attributes::Priority priority{
swPriorityToSaiPriority(addedAclEntry->getPriority())};
std::optional<SaiAclEntryTraits::Attributes::FieldSrcIpV6> fieldSrcIpV6{
std::nullopt};
std::optional<SaiAclEntryTraits::Attributes::FieldSrcIpV4> fieldSrcIpV4{
std::nullopt};
if (addedAclEntry->getSrcIp().first) {
if (addedAclEntry->getSrcIp().first.isV6()) {
auto srcIpV6Mask = folly::IPAddressV6(
folly::IPAddressV6::fetchMask(addedAclEntry->getSrcIp().second));
fieldSrcIpV6 = SaiAclEntryTraits::Attributes::FieldSrcIpV6{
AclEntryFieldIpV6(std::make_pair(
addedAclEntry->getSrcIp().first.asV6(), srcIpV6Mask))};
} else if (addedAclEntry->getSrcIp().first.isV4()) {
auto srcIpV4Mask = folly::IPAddressV4(
folly::IPAddressV4::fetchMask(addedAclEntry->getSrcIp().second));
fieldSrcIpV4 = SaiAclEntryTraits::Attributes::FieldSrcIpV4{
AclEntryFieldIpV4(std::make_pair(
addedAclEntry->getSrcIp().first.asV4(), srcIpV4Mask))};
}
}
std::optional<SaiAclEntryTraits::Attributes::FieldDstIpV6> fieldDstIpV6{
std::nullopt};
std::optional<SaiAclEntryTraits::Attributes::FieldDstIpV4> fieldDstIpV4{
std::nullopt};
if (addedAclEntry->getDstIp().first) {
if (addedAclEntry->getDstIp().first.isV6()) {
auto dstIpV6Mask = folly::IPAddressV6(
folly::IPAddressV6::fetchMask(addedAclEntry->getDstIp().second));
fieldDstIpV6 = SaiAclEntryTraits::Attributes::FieldDstIpV6{
AclEntryFieldIpV6(std::make_pair(
addedAclEntry->getDstIp().first.asV6(), dstIpV6Mask))};
} else if (addedAclEntry->getDstIp().first.isV4()) {
auto dstIpV4Mask = folly::IPAddressV4(
folly::IPAddressV4::fetchMask(addedAclEntry->getDstIp().second));
fieldDstIpV4 = SaiAclEntryTraits::Attributes::FieldDstIpV4{
AclEntryFieldIpV4(std::make_pair(
addedAclEntry->getDstIp().first.asV4(), dstIpV4Mask))};
}
}
std::optional<SaiAclEntryTraits::Attributes::FieldSrcPort> fieldSrcPort{
std::nullopt};
if (addedAclEntry->getSrcPort()) {
if (addedAclEntry->getSrcPort().value() !=
cfg::switch_config_constants::CPU_PORT_LOGICALID()) {
auto portHandle = managerTable_->portManager().getPortHandle(
PortID(addedAclEntry->getSrcPort().value()));
if (!portHandle) {
throw FbossError(
"attempted to configure srcPort: ",
addedAclEntry->getSrcPort().value(),
" ACL:",
addedAclEntry->getID());
}
fieldSrcPort =
SaiAclEntryTraits::Attributes::FieldSrcPort{AclEntryFieldSaiObjectIdT(
std::make_pair(portHandle->port->adapterKey(), kMaskDontCare))};
} else {
fieldSrcPort = SaiAclEntryTraits::Attributes::FieldSrcPort{
AclEntryFieldSaiObjectIdT(std::make_pair(
managerTable_->switchManager().getCpuPort(), kMaskDontCare))};
}
}
std::optional<SaiAclEntryTraits::Attributes::FieldOutPort> fieldOutPort{
std::nullopt};
if (addedAclEntry->getDstPort()) {
auto portHandle = managerTable_->portManager().getPortHandle(
PortID(addedAclEntry->getDstPort().value()));
if (!portHandle) {
throw FbossError(
"attempted to configure dstPort: ",
addedAclEntry->getDstPort().value(),
" ACL:",
addedAclEntry->getID());
}
fieldOutPort =
SaiAclEntryTraits::Attributes::FieldOutPort{AclEntryFieldSaiObjectIdT(
std::make_pair(portHandle->port->adapterKey(), kMaskDontCare))};
}
std::optional<SaiAclEntryTraits::Attributes::FieldL4SrcPort> fieldL4SrcPort{
std::nullopt};
if (addedAclEntry->getL4SrcPort()) {
fieldL4SrcPort = SaiAclEntryTraits::Attributes::FieldL4SrcPort{
AclEntryFieldU16(std::make_pair(
addedAclEntry->getL4SrcPort().value(), kL4PortMask))};
}
std::optional<SaiAclEntryTraits::Attributes::FieldL4DstPort> fieldL4DstPort{
std::nullopt};
if (addedAclEntry->getL4DstPort()) {
fieldL4DstPort = SaiAclEntryTraits::Attributes::FieldL4DstPort{
AclEntryFieldU16(std::make_pair(
addedAclEntry->getL4DstPort().value(), kL4PortMask))};
}
std::optional<SaiAclEntryTraits::Attributes::FieldIpProtocol> fieldIpProtocol{
std::nullopt};
if (addedAclEntry->getProto()) {
fieldIpProtocol = SaiAclEntryTraits::Attributes::FieldIpProtocol{
AclEntryFieldU8(std::make_pair(
addedAclEntry->getProto().value(), kIpProtocolMask))};
}
std::optional<SaiAclEntryTraits::Attributes::FieldTcpFlags> fieldTcpFlags{
std::nullopt};
if (addedAclEntry->getTcpFlagsBitMap()) {
fieldTcpFlags = SaiAclEntryTraits::Attributes::FieldTcpFlags{
AclEntryFieldU8(std::make_pair(
addedAclEntry->getTcpFlagsBitMap().value(), kTcpFlagsMask))};
}
std::optional<SaiAclEntryTraits::Attributes::FieldIpFrag> fieldIpFrag{
std::nullopt};
if (addedAclEntry->getIpFrag()) {
auto ipFragData = cfgIpFragToSaiIpFrag(addedAclEntry->getIpFrag().value());
fieldIpFrag = SaiAclEntryTraits::Attributes::FieldIpFrag{
AclEntryFieldU32(std::make_pair(ipFragData, kMaskDontCare))};
}
std::optional<SaiAclEntryTraits::Attributes::FieldIcmpV4Type> fieldIcmpV4Type{
std::nullopt};
std::optional<SaiAclEntryTraits::Attributes::FieldIcmpV4Code> fieldIcmpV4Code{
std::nullopt};
std::optional<SaiAclEntryTraits::Attributes::FieldIcmpV6Type> fieldIcmpV6Type{
std::nullopt};
std::optional<SaiAclEntryTraits::Attributes::FieldIcmpV6Code> fieldIcmpV6Code{
std::nullopt};
if (addedAclEntry->getIcmpType()) {
if (addedAclEntry->getProto()) {
if (addedAclEntry->getProto().value() == AclEntryFields::kProtoIcmp) {
fieldIcmpV4Type = SaiAclEntryTraits::Attributes::FieldIcmpV4Type{
AclEntryFieldU8(std::make_pair(
addedAclEntry->getIcmpType().value(), kIcmpTypeMask))};
if (addedAclEntry->getIcmpCode()) {
fieldIcmpV4Code = SaiAclEntryTraits::Attributes::FieldIcmpV4Code{
AclEntryFieldU8(std::make_pair(
addedAclEntry->getIcmpCode().value(), kIcmpCodeMask))};
}
} else if (
addedAclEntry->getProto().value() == AclEntryFields::kProtoIcmpv6) {
fieldIcmpV6Type = SaiAclEntryTraits::Attributes::FieldIcmpV6Type{
AclEntryFieldU8(std::make_pair(
addedAclEntry->getIcmpType().value(), kIcmpTypeMask))};
if (addedAclEntry->getIcmpCode()) {
fieldIcmpV6Code = SaiAclEntryTraits::Attributes::FieldIcmpV6Code{
AclEntryFieldU8(std::make_pair(
addedAclEntry->getIcmpCode().value(), kIcmpCodeMask))};
}
}
}
}
std::optional<SaiAclEntryTraits::Attributes::FieldDscp> fieldDscp{
std::nullopt};
if (addedAclEntry->getDscp()) {
fieldDscp = SaiAclEntryTraits::Attributes::FieldDscp{AclEntryFieldU8(
std::make_pair(addedAclEntry->getDscp().value(), kDscpMask))};
}
std::optional<SaiAclEntryTraits::Attributes::FieldDstMac> fieldDstMac{
std::nullopt};
if (addedAclEntry->getDstMac()) {
fieldDstMac = SaiAclEntryTraits::Attributes::FieldDstMac{AclEntryFieldMac(
std::make_pair(addedAclEntry->getDstMac().value(), kMacMask()))};
}
std::optional<SaiAclEntryTraits::Attributes::FieldIpType> fieldIpType{
std::nullopt};
if (addedAclEntry->getIpType()) {
auto ipTypeData = cfgIpTypeToSaiIpType(addedAclEntry->getIpType().value());
fieldIpType = SaiAclEntryTraits::Attributes::FieldIpType{
AclEntryFieldU32(std::make_pair(ipTypeData, kMaskDontCare))};
}
std::optional<SaiAclEntryTraits::Attributes::FieldTtl> fieldTtl{std::nullopt};
if (addedAclEntry->getTtl()) {
fieldTtl =
SaiAclEntryTraits::Attributes::FieldTtl{AclEntryFieldU8(std::make_pair(
addedAclEntry->getTtl().value().getValue(),
addedAclEntry->getTtl().value().getMask()))};
}
std::optional<SaiAclEntryTraits::Attributes::FieldRouteDstUserMeta>
fieldRouteDstUserMeta{std::nullopt};
if (addedAclEntry->getLookupClassRoute()) {
fieldRouteDstUserMeta =
SaiAclEntryTraits::Attributes::FieldRouteDstUserMeta{
AclEntryFieldU32(cfgLookupClassToSaiRouteMetaDataAndMask(
addedAclEntry->getLookupClassRoute().value()))};
}
std::optional<SaiAclEntryTraits::Attributes::FieldNeighborDstUserMeta>
fieldNeighborDstUserMeta{std::nullopt};
if (addedAclEntry->getLookupClassNeighbor()) {
fieldNeighborDstUserMeta =
SaiAclEntryTraits::Attributes::FieldNeighborDstUserMeta{
AclEntryFieldU32(cfgLookupClassToSaiNeighborMetaDataAndMask(
addedAclEntry->getLookupClassNeighbor().value()))};
}
std::optional<SaiAclEntryTraits::Attributes::FieldEthertype> fieldEtherType{
std::nullopt};
if (addedAclEntry->getEtherType()) {
auto etherTypeData =
cfgEtherTypeToSaiEtherType(addedAclEntry->getEtherType().value());
fieldEtherType = SaiAclEntryTraits::Attributes::FieldEthertype{
AclEntryFieldU16(std::make_pair(etherTypeData, kEtherTypeMask))};
}
std::optional<SaiAclEntryTraits::Attributes::FieldOuterVlanId>
fieldOuterVlanId{std::nullopt};
if (addedAclEntry->getVlanID()) {
fieldOuterVlanId = SaiAclEntryTraits::Attributes::FieldOuterVlanId{
AclEntryFieldU16(std::make_pair(
addedAclEntry->getVlanID().value(), kOuterVlanIdMask))};
}
std::optional<SaiAclEntryTraits::Attributes::FieldFdbDstUserMeta>
fieldFdbDstUserMeta{std::nullopt};
if (addedAclEntry->getLookupClassL2()) {
fieldFdbDstUserMeta = SaiAclEntryTraits::Attributes::FieldFdbDstUserMeta{
AclEntryFieldU32(cfgLookupClassToSaiFdbMetaDataAndMask(
addedAclEntry->getLookupClassL2().value()))};
}
// TODO(skhare) Support all other ACL actions
std::optional<SaiAclEntryTraits::Attributes::ActionPacketAction>
aclActionPacketAction{std::nullopt};
const auto& act = addedAclEntry->getActionType();
if (act == cfg::AclActionType::DENY) {
aclActionPacketAction = SaiAclEntryTraits::Attributes::ActionPacketAction{
SAI_PACKET_ACTION_DROP};
} else {
aclActionPacketAction = SaiAclEntryTraits::Attributes::ActionPacketAction{
SAI_PACKET_ACTION_FORWARD};
}
std::shared_ptr<SaiAclCounter> saiAclCounter{nullptr};
std::vector<std::pair<cfg::CounterType, std::string>> aclCounterTypeAndName;
std::optional<SaiAclEntryTraits::Attributes::ActionCounter> aclActionCounter{
std::nullopt};
std::optional<SaiAclEntryTraits::Attributes::ActionSetTC> aclActionSetTC{
std::nullopt};
std::optional<SaiAclEntryTraits::Attributes::ActionSetDSCP> aclActionSetDSCP{
std::nullopt};
std::optional<SaiAclEntryTraits::Attributes::ActionMirrorIngress>
aclActionMirrorIngress{};
std::optional<SaiAclEntryTraits::Attributes::ActionMirrorEgress>
aclActionMirrorEgress{};
std::optional<std::string> ingressMirror{std::nullopt};
std::optional<std::string> egressMirror{std::nullopt};
std::optional<SaiAclEntryTraits::Attributes::ActionMacsecFlow>
aclActionMacsecFlow{std::nullopt};
auto action = addedAclEntry->getAclAction();
if (action) {
if (action.value().getTrafficCounter()) {
std::tie(saiAclCounter, aclCounterTypeAndName) = addAclCounter(
aclTableHandle, action.value().getTrafficCounter().value());
aclActionCounter = SaiAclEntryTraits::Attributes::ActionCounter{
AclEntryActionSaiObjectIdT(
AclCounterSaiId{saiAclCounter->adapterKey()})};
}
if (action.value().getSendToQueue()) {
auto sendToQueue = action.value().getSendToQueue().value();
bool sendToCpu = sendToQueue.second;
if (!sendToCpu) {
auto queueId = static_cast<sai_uint8_t>(*sendToQueue.first.queueId());
aclActionSetTC = SaiAclEntryTraits::Attributes::ActionSetTC{
AclEntryActionU8(queueId)};
} else {
/*
* When sendToCpu is set, a copy of the packet will be sent
* to CPU.
* By default, these packets are sent to queue 0.
* Set TC to set the right traffic class which
* will be mapped to queue id.
*
* TODO(skhare)
* By default, BCM maps TC i to Queue i for i in [0, 9].
* Tajo claims to map TC i to Queue i by default as well.
* However, explicitly set the QoS Map and associate with the CPU port.
*/
auto setCopyOrTrap = [&aclActionPacketAction, &aclActionSetTC](
const MatchAction::SendToQueue& sendToQueue,
sai_uint32_t packetAction) {
aclActionPacketAction =
SaiAclEntryTraits::Attributes::ActionPacketAction{packetAction};
auto queueId = static_cast<sai_uint8_t>(*sendToQueue.first.queueId());
aclActionSetTC = SaiAclEntryTraits::Attributes::ActionSetTC{
AclEntryActionU8(queueId)};
};
if (action.value().getToCpuAction()) {
switch (action.value().getToCpuAction().value()) {
case cfg::ToCpuAction::COPY:
if (!platform_->getAsic()->isSupported(
HwAsic::Feature::ACL_COPY_TO_CPU)) {
throw FbossError("COPY_TO_CPU is not supported on this ASIC");
}
setCopyOrTrap(sendToQueue, SAI_PACKET_ACTION_COPY);
break;
case cfg::ToCpuAction::TRAP:
setCopyOrTrap(sendToQueue, SAI_PACKET_ACTION_TRAP);
break;
}
}
}
}
if (action.value().getIngressMirror().has_value()) {
std::vector<sai_object_id_t> aclEntryMirrorIngressOidList;
auto mirrorHandle = managerTable_->mirrorManager().getMirrorHandle(
action.value().getIngressMirror().value());
if (mirrorHandle) {
aclEntryMirrorIngressOidList.push_back(mirrorHandle->adapterKey());
}
ingressMirror = action.value().getIngressMirror().value();
aclActionMirrorIngress =
SaiAclEntryTraits::Attributes::ActionMirrorIngress{
AclEntryActionSaiObjectIdList(aclEntryMirrorIngressOidList)};
}
if (action.value().getEgressMirror().has_value()) {
std::vector<sai_object_id_t> aclEntryMirrorEgressOidList;
auto mirrorHandle = managerTable_->mirrorManager().getMirrorHandle(
action.value().getEgressMirror().value());
if (mirrorHandle) {
aclEntryMirrorEgressOidList.push_back(mirrorHandle->adapterKey());
}
egressMirror = action.value().getEgressMirror().value();
aclActionMirrorEgress = SaiAclEntryTraits::Attributes::ActionMirrorEgress{
AclEntryActionSaiObjectIdList(aclEntryMirrorEgressOidList)};
}
if (action.value().getSetDscp()) {
const int dscpValue = *action.value().getSetDscp().value().dscpValue();
aclActionSetDSCP = SaiAclEntryTraits::Attributes::ActionSetDSCP{
AclEntryActionU8(dscpValue)};
}
if (action.value().getMacsecFlow()) {
auto macsecFlowAction = action.value().getMacsecFlow().value();
if (*macsecFlowAction.action() ==
cfg::MacsecFlowPacketAction::MACSEC_FLOW) {
sai_object_id_t flowId =
static_cast<sai_object_id_t>(*macsecFlowAction.flowId());
aclActionMacsecFlow = SaiAclEntryTraits::Attributes::ActionMacsecFlow{
AclEntryActionSaiObjectIdT(flowId)};
} else if (
*macsecFlowAction.action() == cfg::MacsecFlowPacketAction::FORWARD) {
aclActionPacketAction =
SaiAclEntryTraits::Attributes::ActionPacketAction{
SAI_PACKET_ACTION_FORWARD};
} else if (
*macsecFlowAction.action() == cfg::MacsecFlowPacketAction::DROP) {
aclActionPacketAction =
SaiAclEntryTraits::Attributes::ActionPacketAction{
SAI_PACKET_ACTION_DROP};
} else {
throw FbossError(
"Unsupported Macsec Flow action for ACL entry: ",
addedAclEntry->getID(),
" Macsec Flow action ",
apache::thrift::util::enumNameSafe(*macsecFlowAction.action()));
}
}
}
// TODO(skhare) At least one field and one action must be specified.
// Once we add support for all fields and actions, throw error if that is not
// honored.
auto matcherIsValid =
(fieldSrcIpV6.has_value() || fieldDstIpV6.has_value() ||
fieldSrcIpV4.has_value() || fieldDstIpV4.has_value() ||
fieldSrcPort.has_value() || fieldOutPort.has_value() ||
fieldL4SrcPort.has_value() || fieldL4DstPort.has_value() ||
fieldIpProtocol.has_value() || fieldTcpFlags.has_value() ||
fieldIpFrag.has_value() || fieldIcmpV4Type.has_value() ||
fieldIcmpV4Code.has_value() || fieldIcmpV6Type.has_value() ||
fieldIcmpV6Code.has_value() || fieldDscp.has_value() ||
fieldDstMac.has_value() || fieldIpType.has_value() ||
fieldTtl.has_value() || fieldFdbDstUserMeta.has_value() ||
fieldRouteDstUserMeta.has_value() || fieldEtherType.has_value() ||
fieldNeighborDstUserMeta.has_value() ||
platform_->getAsic()->isSupported(HwAsic::Feature::EMPTY_ACL_MATCHER));
if (fieldSrcPort.has_value()) {
matcherIsValid &= platform_->getAsic()->isSupported(
HwAsic::Feature::SAI_ACL_ENTRY_SRC_PORT_QUALIFIER);
}
auto actionIsValid =
(aclActionPacketAction.has_value() || aclActionCounter.has_value() ||
aclActionSetTC.has_value() || aclActionSetDSCP.has_value() ||
aclActionMirrorIngress.has_value() ||
aclActionMirrorEgress.has_value() || aclActionMacsecFlow.has_value());
if (!(matcherIsValid && actionIsValid)) {
XLOG(WARNING) << "Unsupported field/action for aclEntry: "
<< addedAclEntry->getID() << " MactherValid "
<< ((matcherIsValid) ? "true" : "false") << " ActionValid "
<< ((actionIsValid) ? "true" : "false");
return AclEntrySaiId{0};
}
SaiAclEntryTraits::AdapterHostKey adapterHostKey{aclTableId, priority};
SaiAclEntryTraits::CreateAttributes attributes{
aclTableId,
priority,
fieldSrcIpV6,
fieldDstIpV6,
fieldSrcIpV4,
fieldDstIpV4,
fieldSrcPort,
fieldOutPort,
fieldL4SrcPort,
fieldL4DstPort,
fieldIpProtocol,
fieldTcpFlags,
fieldIpFrag,
fieldIcmpV4Type,
fieldIcmpV4Code,
fieldIcmpV6Type,
fieldIcmpV6Code,
fieldDscp,
fieldDstMac,
fieldIpType,
fieldTtl,
fieldFdbDstUserMeta,
fieldRouteDstUserMeta,
fieldNeighborDstUserMeta,
fieldEtherType,
fieldOuterVlanId,
aclActionPacketAction,
aclActionCounter,
aclActionSetTC,
aclActionSetDSCP,
aclActionMirrorIngress,
aclActionMirrorEgress,
aclActionMacsecFlow,
};
auto saiAclEntry = aclEntryStore.setObject(adapterHostKey, attributes);
auto entryHandle = std::make_unique<SaiAclEntryHandle>();
entryHandle->aclEntry = saiAclEntry;
entryHandle->aclCounter = saiAclCounter;
entryHandle->aclCounterTypeAndName = aclCounterTypeAndName;
entryHandle->ingressMirror = ingressMirror;
entryHandle->egressMirror = egressMirror;
auto [it, inserted] = aclTableHandle->aclTableMembers.emplace(
addedAclEntry->getPriority(), std::move(entryHandle));
CHECK(inserted);
XLOG(INFO) << "added acl entry " << addedAclEntry->getID() << " priority "
<< addedAclEntry->getPriority();
return it->second->aclEntry->adapterKey();
}