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