AclEntrySaiId SaiAclTableManager::addAclEntry()

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();
}