bool BcmAclEntry::isStateSame()

in fboss/agent/hw/bcm/BcmAclEntry.cpp [480:796]


bool BcmAclEntry::isStateSame(
    const BcmSwitch* hw,
    int gid,
    BcmAclEntryHandle handle,
    const std::shared_ptr<AclEntry>& acl) {
  auto aclMsg = folly::to<std::string>(
      "Group=", gid, ", acl=", acl->getID(), ", handle=", handle);
  bool isSame = true;
  // There is aliasing among qualifiers in hardware in the tcam and it will set
  // the same field depending on the packet type. Thus, bcm will use the same
  // field for icmp type of icmp package and L4 src port of UDP/TCP package.
  // So for these re-use fields, we only check them if S/W also have the value.
  // And then at the very end of this function, we will check the H/W value
  // should be empty if no one is using it.
  bool isSrcL4PortFieldUsed = false;

  // check enable status
  int enableFlag = 0;
  auto rv = bcm_field_entry_enable_get(hw->getUnit(), handle, &enableFlag);
  bcmCheckError(rv, aclMsg, " failed to get acl enable status");
  if (!enableFlag) {
    XLOG(ERR) << aclMsg << " is disabled.";
    isSame = false;
  }

  // check priority
  int prio = 0;
  rv = bcm_field_entry_prio_get(hw->getUnit(), handle, &prio);
  bcmCheckError(rv, aclMsg, " failed to get acl priority");
  auto expectedPrio = swPriorityToHwPriority(acl->getPriority());
  if (prio != expectedPrio) {
    XLOG(ERR) << aclMsg << " priority doesn't match. Expected=" << expectedPrio
              << ", actual=" << prio;
    isSame = false;
  }

  // check action type
  isSame &= isActionStateSame(
      hw, hw->getUnit(), handle, acl, aclMsg, getAclActionParameters(hw, acl));

  // check ip addresses
  isSame &= isBcmIp6QualFieldStateSame(
      hw->getUnit(),
      handle,
      aclMsg,
      "Src IP",
      bcm_field_qualify_SrcIp6_get,
      acl->getSrcIp());
  isSame &= isBcmIp6QualFieldStateSame(
      hw->getUnit(),
      handle,
      aclMsg,
      "Dst IP",
      bcm_field_qualify_DstIp6_get,
      acl->getDstIp());

  // check protocol
  std::optional<uint8> protoD{std::nullopt};
  if (acl->getProto()) {
    protoD = acl->getProto().value();
  }
  isSame &= isBcmQualFieldStateSame(
      bcm_field_qualify_IpProtocol_get,
      hw->getUnit(),
      handle,
      protoD,
      aclMsg,
      "IpProtocol");

  // check l4 src/dst port
  if (acl->getL4SrcPort()) {
    isSrcL4PortFieldUsed = true;
    std::optional<bcm_l4_port_t> portData{acl->getL4SrcPort().value()};
    isSame &= isBcmQualFieldStateSame(
        bcm_field_qualify_L4SrcPort_get,
        hw->getUnit(),
        handle,
        portData,
        aclMsg,
        "L4SrcPort");
  }
  std::optional<bcm_l4_port_t> portData{std::nullopt};
  if (acl->getL4DstPort()) {
    portData = acl->getL4DstPort().value();
  }
  isSame &= isBcmQualFieldStateSame(
      bcm_field_qualify_L4DstPort_get,
      hw->getUnit(),
      handle,
      portData,
      aclMsg,
      "L4DstPort");

  // check tcp flags mask
  std::optional<uint8> tcpFlagsD{std::nullopt};
  if (acl->getTcpFlagsBitMap()) {
    tcpFlagsD = acl->getTcpFlagsBitMap().value();
  }
  isSame &= isBcmQualFieldStateSame(
      bcm_field_qualify_TcpControl_get,
      hw->getUnit(),
      handle,
      tcpFlagsD,
      aclMsg,
      "TcpFlags");

  // check src/dst port
  isSame &= isBcmPortQualFieldStateSame(
      hw->getUnit(),
      handle,
      aclMsg,
      "SrcPort",
      bcm_field_qualify_SrcPort_get,
      acl->getSrcPort());
  isSame &= isBcmPortQualFieldStateSame(
      hw->getUnit(),
      handle,
      aclMsg,
      "DstPort",
      bcm_field_qualify_DstPort_get,
      acl->getDstPort());

  // check ip frag
  std::optional<bcm_field_IpFrag_t> ipFragD{std::nullopt};
  if (acl->getIpFrag()) {
    ipFragD = cfgIpFragToBcmIpFrag(acl->getIpFrag().value());
  }
  isSame &= isBcmEnumQualFieldStateSame(
      bcm_field_qualify_IpFrag_get,
      hw->getUnit(),
      handle,
      ipFragD,
      aclMsg,
      "IpFrag");

  // check icmp type. Since the latest sdk is 6.4+, no need to check sdk version
  if (acl->getIcmpType()) {
    isSrcL4PortFieldUsed = true;
    uint16_t typeCode, typeCodeMask;
    cfgIcmpTypeCodeToBcmIcmpCodeMask(
        acl->getIcmpType(), acl->getIcmpCode(), &typeCode, &typeCodeMask);
    std::optional<bcm_l4_port_t> icmpD = typeCode;
    std::optional<bcm_l4_port_t> icmpM = typeCodeMask;
    // BRCM is reusing L4SrcPort for icmp
    isSame &= isBcmQualFieldWithMaskStateSame(
        bcm_field_qualify_L4SrcPort_get,
        hw->getUnit(),
        handle,
        icmpD,
        icmpM,
        aclMsg,
        "IcmpTypeCode");
  }

  // check dscp
  std::optional<uint8> dscpD{std::nullopt};
  if (acl->getDscp()) {
    uint8 data, mask;
    cfgDscpToBcmDscp(acl->getDscp().value(), &data, &mask);
    dscpD = data;
  }
  isSame &= isBcmQualFieldStateSame(
      bcm_field_qualify_DSCP_get, hw->getUnit(), handle, dscpD, aclMsg, "Dscp");

  // check ipType
  std::optional<bcm_field_IpType_t> ipTypeD{std::nullopt};
  if (acl->getIpType()) {
    ipTypeD = cfgIpTypeToBcmIpType(acl->getIpType().value());
  }
  isSame &= isBcmEnumQualFieldStateSame(
      bcm_field_qualify_IpType_get,
      hw->getUnit(),
      handle,
      ipTypeD,
      aclMsg,
      "IpType");

  // check ttl
  std::optional<uint8_t> ttlD{std::nullopt};
  if (acl->getTtl()) {
    auto ttl = acl->getTtl().value();
    ttlD = ttl.getValue() & ttl.getMask();
  }
  isSame &= isBcmQualFieldStateSame(
      bcm_field_qualify_Ttl_get, hw->getUnit(), handle, ttlD, aclMsg, "Ttl");

  std::optional<MacAddress> dstMacMask{std::nullopt};
  if (acl->getDstMac()) {
    dstMacMask = MacAddress::BROADCAST;
  }
  // DstMac
  isSame &= isBcmMacQualFieldStateSame(
      hw->getUnit(),
      handle,
      aclMsg,
      "DstMac",
      bcm_field_qualify_DstMac_get,
      acl->getDstMac());
  // if isSrcL4PortFieldUsed we need to check H/W value shoule be empty too
  if (!isSrcL4PortFieldUsed) {
    std::optional<bcm_l4_port_t> nullDate{std::nullopt};
    isSame &= isBcmQualFieldStateSame(
        bcm_field_qualify_L4SrcPort_get,
        hw->getUnit(),
        handle,
        nullDate,
        aclMsg,
        "L4SrcPort");
  }

  /*
   * lookupClassNeighbor and lookupClassRoute are both represented by
   * DstClassL3. So, if either is set, compare it with DstClassL3.
   */
  std::optional<uint32> lookupClassNeighborOrRoute;
  if (acl->getLookupClassNeighbor()) {
    lookupClassNeighborOrRoute =
        static_cast<int>(acl->getLookupClassNeighbor().value());
  } else if (acl->getLookupClassRoute()) {
    lookupClassNeighborOrRoute =
        static_cast<int>(acl->getLookupClassRoute().value());
  }
  isSame &= isBcmQualFieldStateSame(
      bcm_field_qualify_DstClassL3_get,
      hw->getUnit(),
      handle,
      lookupClassNeighborOrRoute,
      aclMsg,
      "LookupClassNeighborOrRoute");

  /*
   * bcmFieldQualifyDstClassL2 is not configured for Trident2 or else we runs
   * out of resources in fp group. bcmFieldQualifyDstClassL2 is needed for
   * MH-NIC queue-per-host solution. HOwever, the solution is not appliable for
   * Trident2 as Trident2 does not support queues.
   */
  if (BCM_FIELD_QSET_TEST(
          getAclQset(hw->getPlatform()->getAsic()->getAsicType()),
          bcmFieldQualifyDstClassL2)) {
    std::optional<uint32> lookupClassL2;
    if (acl->getLookupClassL2()) {
      lookupClassL2 = static_cast<int>(acl->getLookupClassL2().value());
    }
    isSame &= isBcmQualFieldStateSame(
        bcm_field_qualify_DstClassL2_get,
        hw->getUnit(),
        handle,
        lookupClassL2,
        aclMsg,
        "LookupClassL2");
  }

  if (BCM_FIELD_QSET_TEST(
          getAclQset(hw->getPlatform()->getAsic()->getAsicType()),
          bcmFieldQualifyPacketRes)) {
    std::optional<uint32> packetLookupResult;
    if (acl->getPacketLookupResult()) {
      packetLookupResult = cfgPacketLookupResultToBcmPktResult(
          acl->getPacketLookupResult().value());
    }
    isSame &= isBcmQualFieldStateSame(
        bcm_field_qualify_PacketRes_get,
        hw->getUnit(),
        handle,
        packetLookupResult,
        aclMsg,
        "PacketRes");
  }

  // check EtherType
  if (BCM_FIELD_QSET_TEST(
          getAclQset(hw->getPlatform()->getAsic()->getAsicType()),
          bcmFieldQualifyEtherType)) {
    std::optional<uint16> etherType{std::nullopt};
    if (acl->getEtherType()) {
      uint16 data, mask;
      cfgEtherTypeToBcmEtherType(acl->getEtherType().value(), &data, &mask);
      etherType = data;
    }
    isSame &= isBcmQualFieldStateSame(
        bcm_field_qualify_EtherType_get,
        hw->getUnit(),
        handle,
        etherType,
        aclMsg,
        "EtherType");
  }

  if (BCM_FIELD_QSET_TEST(
          getAclQset(hw->getPlatform()->getAsic()->getAsicType()),
          bcmFieldQualifyOuterVlanId)) {
    std::optional<bcm_vlan_t> outerVlanId{std::nullopt};
    if (acl->getVlanID()) {
      outerVlanId = acl->getVlanID().value();
    }
    isSame &= isBcmQualFieldStateSame(
        bcm_field_qualify_OuterVlanId_get,
        hw->getUnit(),
        handle,
        outerVlanId,
        aclMsg,
        "OuterVlanId");
  }

  // check acl stat
  auto aclStatHandle =
      BcmAclStat::getAclStatHandleFromAttachedAcl(hw, gid, handle);
  if (aclStatHandle && acl->getAclAction() &&
      acl->getAclAction()->getTrafficCounter()) {
    auto counter = acl->getAclAction()->getTrafficCounter().value();
    isSame &= ((BcmAclStat::isStateSame(hw, *aclStatHandle, counter)) ? 1 : 0);
  } else {
    isSame &= ((!aclStatHandle.has_value()) ? 1 : 0);
  }

  return isSame;
}