in fboss/agent/hw/bcm/BcmIntf.cpp [124:287]
void BcmIntf::program(const shared_ptr<Interface>& intf) {
const auto& oldIntf = intf_;
if (oldIntf != nullptr) {
// vrf and vid cannot be changed by this method.
if (oldIntf->getRouterID() != intf->getRouterID() ||
oldIntf->getVlanID() != intf->getVlanID()) {
throw FbossError(
"Interface router ID (",
oldIntf->getRouterID(),
" vs ",
intf->getRouterID(),
" or VLAN ID (",
oldIntf->getVlanID(),
" vs ",
intf->getVlanID(),
" is changed during programming");
}
}
// create or update a station entry
if (station_ == nullptr) {
station_ = unique_ptr<BcmStation>(new BcmStation(hw_));
}
if (oldIntf == nullptr || oldIntf->getMac() != intf->getMac()) {
// Program BCM station to have the same ID as interface
// so we can look them up during warm boot
station_->program(intf->getMac(), intf->getID());
}
const auto vrf = BcmSwitch::getBcmVrfId(intf->getRouterID());
// Lambda to compare with existing intf to determine if we need to
// reprogram
auto updateIntfIfNeeded = [&](bcm_l3_intf_t& newIntf,
const bcm_l3_intf_t& oldIntf) {
auto equivalent = [=](const bcm_l3_intf_t& newIntf,
const bcm_l3_intf_t& existingIntf) {
CHECK(newIntf.l3a_vrf == existingIntf.l3a_vrf);
return macFromBcm(newIntf.l3a_mac_addr) ==
macFromBcm(existingIntf.l3a_mac_addr) &&
newIntf.l3a_vid == existingIntf.l3a_vid &&
newIntf.l3a_mtu == existingIntf.l3a_mtu;
};
if (!equivalent(newIntf, oldIntf)) {
XLOG(DBG1) << "Updating interface for vlan : " << intf->getVlanID()
<< " and mac: " << intf->getMac();
CHECK_NE(bcmIfId_, INVALID);
newIntf.l3a_intf_id = bcmIfId_;
newIntf.l3a_flags = BCM_L3_WITH_ID | BCM_L3_REPLACE;
return true;
}
return false;
};
const auto intftoIfparam = [=](const auto& Intf) {
bcm_l3_intf_t ifParams;
bcm_l3_intf_t_init(&ifParams);
#ifndef IS_OSS
// Something about the open source build breaks here in ubuntu 16_04
//
// Specifically g++-5.4 seems to segfault (!!) compiling this line.
// It's a simple assignment from ultimately a
// BOOST_STRONG_TYPE RouterID (which is an int) to
// a bcm_vrf_t (which is also an int!!). The casting should
// be a NOOP but somehow kills g++'s type checking system
// This is not a problem with g++-6 that the ONL OSS build uses
//
// -- hack around this until compilers fix themselves :-/
//
ifParams.l3a_vrf = vrf;
#else
ifParams.l3a_vrf = 0; // Currently VRF is always zero anyway
#endif
memcpy(
&ifParams.l3a_mac_addr,
Intf->getMac().bytes(),
sizeof(ifParams.l3a_mac_addr));
ifParams.l3a_vid = Intf->getVlanID();
ifParams.l3a_mtu = Intf->getMtu();
return ifParams;
};
auto ifParams = intftoIfparam(intf);
// create the interface if needed
if (bcmIfId_ == INVALID) {
// create a BCM l3 interface
bool addInterface = false;
const auto warmBootCache = hw_->getWarmBootCache();
auto vlanMac2IntfItr =
warmBootCache->findL3Intf(intf->getVlanID(), intf->getMac());
if (vlanMac2IntfItr != warmBootCache->vlanAndMac2Intf_end()) {
const auto& existingIntf = vlanMac2IntfItr->second;
bcmIfId_ = existingIntf.l3a_intf_id;
if (updateIntfIfNeeded(ifParams, existingIntf)) {
// Set add interface to true, we will no issue the call to add
// but with the above flags set this will cause the entry to be
// updated.
addInterface = true;
} else {
XLOG(DBG1) << "Interface for vlan " << intf->getVlanID() << " and mac "
<< intf->getMac() << " already exists";
}
} else {
addInterface = true;
}
if (addInterface) {
bool updateIngress = true;
if (vlanMac2IntfItr == warmBootCache->vlanAndMac2Intf_end()) {
XLOG(DBG1) << "Adding interface for vlan : " << intf->getVlanID()
<< " and mac: " << intf->getMac();
updateIngress = false;
}
auto rc = bcm_l3_intf_create(hw_->getUnit(), &ifParams);
bcmCheckError(rc, "failed to create L3 interface ", intf->getID());
bcmIfId_ = ifParams.l3a_intf_id;
programIngressIfNeeded(intf, updateIngress);
}
if (vlanMac2IntfItr != warmBootCache->vlanAndMac2Intf_end()) {
warmBootCache->programmed(vlanMac2IntfItr);
}
CHECK_NE(bcmIfId_, INVALID);
}
if (oldIntf) {
auto oldIfParams = intftoIfparam(oldIntf);
if (updateIntfIfNeeded(ifParams, oldIfParams)) {
auto rc = bcm_l3_intf_create(hw_->getUnit(), &ifParams);
bcmCheckError(rc, "failed to update L3 interface ", intf->getID());
programIngressIfNeeded(intf, true);
}
}
std::unordered_set<std::shared_ptr<BcmHostIf>> hosts;
for (const auto& addr : intf->getAddresses()) {
std::shared_ptr<BcmHostIf> host;
BcmHostKey key = BcmHostKey(vrf, addr.first, intf->getID());
if (hw_->getPlatform()->getAsic()->isSupported(
HwAsic::Feature::HOSTTABLE)) {
host = hw_->writableHostTable()->refOrEmplaceHost(key);
} else {
host = hw_->writableRouteTable()->refOrEmplaceHost(key);
}
CHECK(host);
if (!host->isProgrammed()) {
// new host has been created
if (addr.first.isV4()) {
host->setLookupClassId(BcmAclEntry::kLocalIp4DstClassL3Id);
} else {
host->setLookupClassId(BcmAclEntry::kLocalIp6DstClassL3Id);
}
host->programToCPU(bcmIfId_);
}
hosts.emplace(std::move(host));
}
// create new host entries and discard updated or deleted ones,
// existing host entries are not "deleted", they carry over.
hosts_ = std::move(hosts);
// all new info have been programmed, store the interface configuration
intf_ = intf;
XLOG(DBG3) << "updated L3 interface " << bcmIfId_ << " for interface ID "
<< intf->getID();
}