diff --git a/BUILD.md b/BUILD.md index 9d1fb973a428e..c8d42724fbfb5 100644 --- a/BUILD.md +++ b/BUILD.md @@ -15,6 +15,7 @@ If you want to build them manually, the dependencies are as follows: * iproute2-3.19.0: download from [here](https://www.kernel.org/pub/linux/utils/net/iproute2/iproute2-3.19.0.tar.xz) * Broadcom's [OpenNSL](https://github.com/Broadcom-Switch/OpenNSL) +* [OCP-SAI](https://github.com/opencomputeproject/SAI.git), download only. Once the prerequisites are available, take the following steps. @@ -26,7 +27,11 @@ git clone https://github.com/facebook/fboss.git If you installed dependencies manually, CMAKE_INCLUDE_PATH and CMAKE_LIBRARY_PATH must be modified to include the paths to the dependencies. -If you use `getdeps.sh`, then this should work out of the box. +If you use `getdeps.sh`, then this should work out of the box. + +If you build with SAI there are two additional steps: (1) copy and rename the +SAI adapter library as external/SAI/lib/libSaiAdapter.so (2) copy and rename +./fboss/github/CMakeLists_SAI.txt as ./CMakeLists_SAI.txt. Build as follows: @@ -36,5 +41,4 @@ cd fboss/build cmake .. make ``` - -The produced executables are `sim_agent` and `wedge_agent` in the build directory. +The produced executables are `sim_agent` and `wedge_agent` (or `sai_agent`) in the build directory. diff --git a/fboss/agent/hw/sai/SaiError.cpp b/fboss/agent/hw/sai/SaiError.cpp new file mode 100644 index 0000000000000..352df416763b0 --- /dev/null +++ b/fboss/agent/hw/sai/SaiError.cpp @@ -0,0 +1,18 @@ +/* + * Copyright (c) 2004-present, Facebook, Inc. + * Copyright (c) 2016, Cavium, Inc. + * All rights reserved. + * + * This source code is licensed under the BSD-style license found in the + * LICENSE file in the root directory of this source tree. An additional grant + * of patent rights can be found in the PATENTS file in the same directory. + * + */ +#include "SaiError.h" + +#include +#include + +namespace facebook { namespace fboss { + +}} // facebook::fboss diff --git a/fboss/agent/hw/sai/SaiError.h b/fboss/agent/hw/sai/SaiError.h new file mode 100644 index 0000000000000..36992370ebcff --- /dev/null +++ b/fboss/agent/hw/sai/SaiError.h @@ -0,0 +1,37 @@ +/* + * Copyright (c) 2004-present, Facebook, Inc. + * Copyright (c) 2016, Cavium, Inc. + * All rights reserved. + * + * This source code is licensed under the BSD-style license found in the + * LICENSE file in the root directory of this source tree. An additional grant + * of patent rights can be found in the PATENTS file in the same directory. + * + */ +#pragma once + +#include "fboss/agent/FbossError.h" + +namespace facebook { namespace fboss { + +class SaiError : public FbossError { +public: + template + explicit SaiError(Args&&... args) + : FbossError("SAI-agent: ", std::forward(args)...){ + } + + ~SaiError() throw() {} +}; + +class SaiFatal : public FbossError { +public: + template + explicit SaiFatal(Args&&... args) + : FbossError("SAI-agent: ", std::forward(args)...){ + } + + ~SaiFatal() throw() {} +}; + +}} // facebook::fboss diff --git a/fboss/agent/hw/sai/SaiHost.cpp b/fboss/agent/hw/sai/SaiHost.cpp new file mode 100644 index 0000000000000..ff10b5047ffc4 --- /dev/null +++ b/fboss/agent/hw/sai/SaiHost.cpp @@ -0,0 +1,143 @@ +/* + * Copyright (c) 2004-present, Facebook, Inc. + * Copyright (c) 2016, Cavium, Inc. + * All rights reserved. + * + * This source code is licensed under the BSD-style license found in the + * LICENSE file in the root directory of this source tree. An additional grant + * of patent rights can be found in the PATENTS file in the same directory. + * + */ +#include "SaiHost.h" +#include "SaiVrfTable.h" +#include "SaiVrf.h" +#include "SaiIntfTable.h" +#include "SaiIntf.h" +#include "SaiSwitch.h" +#include "SaiError.h" + +using folly::IPAddress; +using folly::MacAddress; + +namespace facebook { namespace fboss { + +SaiHost::SaiHost(const SaiSwitch *hw, InterfaceID intf, + const IPAddress &ip, const folly::MacAddress &mac) + : hw_(hw) + , intf_(intf) + , ip_(ip) + , mac_(mac) { + + VLOG(4) << "Entering " << __FUNCTION__; + + saiNeighborApi_ = hw_->getSaiNeighborApi(); +} + +SaiHost::~SaiHost() { + VLOG(4) << "Entering " << __FUNCTION__; + + if (!added_) { + return; + } + + saiNeighborApi_->remove_neighbor_entry(&neighborEntry_); + VLOG(3) << "Deleted L3 host object for " << ip_; +} + +void SaiHost::program(sai_packet_action_t action, const folly::MacAddress &mac) { + VLOG(4) << "Entering " << __FUNCTION__; + + if (!added_) { + + if (ip_.empty() || ip_.isZero()) { + throw SaiError("Wrong IP address ", ip_, " specified"); + } + + // fill neighborEntry_ + neighborEntry_.rif_id = hw_->getIntfTable()->getIntf(intf_)->getIfId(); + + if (ip_.isV4()) { + // IPv4 + neighborEntry_.ip_address.addr_family = SAI_IP_ADDR_FAMILY_IPV4; + + memcpy(&neighborEntry_.ip_address.addr.ip4, ip_.bytes(), + sizeof(neighborEntry_.ip_address.addr.ip4)); + } else { + // IPv6 + neighborEntry_.ip_address.addr_family = SAI_IP_ADDR_FAMILY_IPV6; + + memcpy(neighborEntry_.ip_address.addr.ip6, ip_.bytes(), + sizeof(neighborEntry_.ip_address.addr.ip6)); + } + + // fill neighbor attributes + std::vector attrList; + sai_attribute_t attr {0}; + + // MAC + attr.id = SAI_NEIGHBOR_ATTR_DST_MAC_ADDRESS; + memcpy(attr.value.mac, mac_.bytes(), sizeof(attr.value.mac)); + attrList.push_back(attr); + + // packet action + attr.id = SAI_NEIGHBOR_ATTR_PACKET_ACTION; + attr.value.u32 = action; + attrList.push_back(attr); + + // create neighbor + sai_status_t saiRetVal = saiNeighborApi_->create_neighbor_entry(&neighborEntry_, + attrList.size(), + attrList.data()); + if (saiRetVal != SAI_STATUS_SUCCESS) { + throw SaiError("Could not create SAI neighbor with IP addr: ", ip_, + " MAC: ", mac_, " router interface ID: ", intf_, + " Error: ", saiRetVal); + } + + added_ = true; + action_ = action; + + return; + } + + if (mac != mac_) { + // fill neighbor MAC attribute + sai_attribute_t macAttr {0}; + macAttr.id = SAI_NEIGHBOR_ATTR_DST_MAC_ADDRESS; + memcpy(macAttr.value.mac, mac.bytes(), sizeof(macAttr.value.mac)); + + // set action attribute + sai_status_t saiRetVal = saiNeighborApi_->set_neighbor_attribute(&neighborEntry_, + &macAttr); + if (saiRetVal != SAI_STATUS_SUCCESS) { + throw SaiError("Could not set MAC: ", mac, + "to SAI neighbor with IP addr: ", ip_, + " router interface ID: ", intf_, + " Error: ", saiRetVal); + } + + mac_ = mac; + } + + if (action != action_) { + + // fill neighbor packet action attribute + sai_attribute_t actionAttr {0}; + actionAttr.id = SAI_NEIGHBOR_ATTR_PACKET_ACTION; + actionAttr.value.u32 = action; + + // set action attribute + sai_status_t saiRetVal = saiNeighborApi_->set_neighbor_attribute(&neighborEntry_, + &actionAttr); + if (saiRetVal != SAI_STATUS_SUCCESS) { + throw SaiError("Could not set packet action attribute: ", action, + " to SAI neighbor with IP addr: ", ip_, + " MAC: ", mac_, " router interface ID: ", intf_, + " Error: ", saiRetVal); + } + + action_ = action; + } +} + +}} // facebook::fboss diff --git a/fboss/agent/hw/sai/SaiHost.h b/fboss/agent/hw/sai/SaiHost.h new file mode 100644 index 0000000000000..3e07efc91780c --- /dev/null +++ b/fboss/agent/hw/sai/SaiHost.h @@ -0,0 +1,83 @@ +/* + * Copyright (c) 2004-present, Facebook, Inc. + * Copyright (c) 2016, Cavium, Inc. + * All rights reserved. + * + * This source code is licensed under the BSD-style license found in the + * LICENSE file in the root directory of this source tree. An additional grant + * of patent rights can be found in the PATENTS file in the same directory. + * + */ +#pragma once + +#include +#include + +#include "fboss/agent/types.h" + +extern "C" { +#include "saitypes.h" +#include "saineighbor.h" +#include "saiswitch.h" +} + +namespace facebook { namespace fboss { + +class SaiSwitch; + +class SaiHost { +public: + /** + * Constructs the SaiHost object. + * + * This method doesn't make any calls to the SAI to program host there. + * program() will be called soon after construction, and any + * actual initialization logic will be performed there. + */ + SaiHost(const SaiSwitch *hw, + InterfaceID intf, + const folly::IPAddress &ip, + const folly::MacAddress &mac); + /** + * Destructs the SaiHost object. + * + * This method removes the host from the HW as well. + */ + virtual ~SaiHost(); + + /** + * Gets Sai Host action. + * + * @return Host action of sai_packet_action_t type. + */ + sai_packet_action_t getSaiAction() const { + return action_; + } + + /** + * Programs host in HW and stores sai_neighbor_entry_t instance + * which holds SAI host data needed. + * + * If one of SAI HW calls fails it throws an exception; + * + * @return none + */ + void program(sai_packet_action_t action, const folly::MacAddress &mac); + +private: + // no copy or assignment + SaiHost(SaiHost const &) = delete; + SaiHost &operator=(SaiHost const &) = delete; + + const SaiSwitch *hw_; + const InterfaceID intf_; + const folly::IPAddress ip_; + folly::MacAddress mac_; + sai_packet_action_t action_ {SAI_PACKET_ACTION_DROP}; + bool added_ {false}; // if added to the HW host table or not + + sai_neighbor_entry_t neighborEntry_; + sai_neighbor_api_t *saiNeighborApi_ { nullptr }; +}; + +}} // facebook::fboss diff --git a/fboss/agent/hw/sai/SaiHostTable.cpp b/fboss/agent/hw/sai/SaiHostTable.cpp new file mode 100644 index 0000000000000..11681418703a8 --- /dev/null +++ b/fboss/agent/hw/sai/SaiHostTable.cpp @@ -0,0 +1,95 @@ +/* + * Copyright (c) 2004-present, Facebook, Inc. + * Copyright (c) 2016, Cavium, Inc. + * All rights reserved. + * + * This source code is licensed under the BSD-style license found in the + * LICENSE file in the root directory of this source tree. An additional grant + * of patent rights can be found in the PATENTS file in the same directory. + * + */ +#include "SaiHostTable.h" +#include "SaiHost.h" +#include "SaiSwitch.h" + +namespace facebook { namespace fboss { + +SaiHostTable::SaiHostTable(const SaiSwitch *hw) : hw_(hw) { + VLOG(4) << "Entering " << __FUNCTION__; +} + +SaiHostTable::~SaiHostTable() { + VLOG(4) << "Entering " << __FUNCTION__; +} + +SaiHost *SaiHostTable::getSaiHost(InterfaceID intf, + const folly::IPAddress &ip) const { + VLOG(4) << "Entering " << __FUNCTION__; + + Key key(intf, ip); + auto iter = hosts_.find(key); + + if (iter == hosts_.end()) { + return nullptr; + } + + return iter->second.get(); +} + +SaiHost* SaiHostTable::createOrUpdateSaiHost(InterfaceID intf, + const folly::IPAddress &ip, + const folly::MacAddress &mac) { + VLOG(4) << "Entering " << __FUNCTION__; + + Key key(intf, ip); + auto ret = hosts_.emplace(key, nullptr); + auto &iter = ret.first; + + if (!ret.second) { + // there was an entry already there + return iter->second.get(); + } + + SCOPE_FAIL { + hosts_.erase(iter); + }; + + auto newHost = folly::make_unique(hw_, intf, ip, mac); + auto hostPtr = newHost.get(); + + iter->second = std::move(newHost); + + return hostPtr; +} + +void SaiHostTable::removeSaiHost(InterfaceID intf, + const folly::IPAddress &ip) noexcept { + VLOG(4) << "Entering " << __FUNCTION__; + + Key key(intf, ip); + auto iter = hosts_.find(key); + + if (iter != hosts_.end()) { + hosts_.erase(iter); + } +} + +SaiHostTable::Key::Key(InterfaceID intf, + const folly::IPAddress &ip) + : intf_(intf) + , ip_(ip) { +} + +bool SaiHostTable::Key::operator<(const Key &k) const{ + if (intf_ < k.intf_) { + return true; + } + + if (intf_ > k.intf_) { + return false; + } + + return (ip_ < k.ip_); +} + +}} // facebook::fboss diff --git a/fboss/agent/hw/sai/SaiHostTable.h b/fboss/agent/hw/sai/SaiHostTable.h new file mode 100644 index 0000000000000..15a4677e13a0f --- /dev/null +++ b/fboss/agent/hw/sai/SaiHostTable.h @@ -0,0 +1,63 @@ +/* + * Copyright (c) 2004-present, Facebook, Inc. + * Copyright (c) 2016, Cavium, Inc. + * All rights reserved. + * + * This source code is licensed under the BSD-style license found in the + * LICENSE file in the root directory of this source tree. An additional grant + * of patent rights can be found in the PATENTS file in the same directory. + * + */ +#pragma once + +#include +#include +#include + +#include "fboss/agent/types.h" + +namespace facebook { namespace fboss { + +class SaiSwitch; +class SaiHost; + +class SaiHostTable { +public: + explicit SaiHostTable(const SaiSwitch *hw); + virtual ~SaiHostTable(); + + // return nullptr if not found + SaiHost *getSaiHost(InterfaceID intf, const folly::IPAddress &ip) const; + /** + * Allocates a new SaiHost if no such one exists or + * updates an existing one. + * + * @return The SaiHost pointer just created or found. + */ + SaiHost *createOrUpdateSaiHost(InterfaceID intf, + const folly::IPAddress &ip, + const folly::MacAddress &mac); + + /** + * Remove an existing SaiHost entry + */ + void removeSaiHost(InterfaceID intf, + const folly::IPAddress &ip) noexcept; + +private: + struct Key { + Key(InterfaceID intf, + const folly::IPAddress &ip); + bool operator<(const Key &k) const; + + InterfaceID intf_; + folly::IPAddress ip_; + }; + + typedef boost::container::flat_map> HostMap; + + const SaiSwitch *hw_; + HostMap hosts_; +}; + +}} // facebook::fboss diff --git a/fboss/agent/hw/sai/SaiIntf.cpp b/fboss/agent/hw/sai/SaiIntf.cpp new file mode 100644 index 0000000000000..7560a7a695997 --- /dev/null +++ b/fboss/agent/hw/sai/SaiIntf.cpp @@ -0,0 +1,234 @@ +/* + * Copyright (c) 2004-present, Facebook, Inc. + * Copyright (c) 2016, Cavium, Inc. + * All rights reserved. + * + * This source code is licensed under the BSD-style license found in the + * LICENSE file in the root directory of this source tree. An additional grant + * of patent rights can be found in the PATENTS file in the same directory. + * + */ +#include + +#include "SaiSwitch.h" +#include "SaiIntf.h" +#include "SaiVrf.h" +#include "SaiVrfTable.h" +#include "SaiError.h" +#include "SaiHostTable.h" +#include "SaiHost.h" +#include "SaiStation.h" + +using std::shared_ptr; +using folly::IPAddress; + +namespace facebook { namespace fboss { + +SaiIntf::SaiIntf(const SaiSwitch *hw) + : hw_(hw) { + VLOG(4) << "Entering " << __FUNCTION__; + + saiRouterInterfaceApi_ = hw->getSaiRouterIntfApi(); +} + +SaiIntf::~SaiIntf() { + VLOG(4) << "Entering " << __FUNCTION__; + + if (saiIfId_ != SAI_NULL_OBJECT_ID) { + + auto iter = hosts_.begin(); + while (iter != hosts_.end()) { + removeHost(*iter); + + iter = hosts_.begin(); + } + + saiRouterInterfaceApi_->remove_router_interface(saiIfId_); + } + + hw_->writableVrfTable()->derefSaiVrf(intf_->getRouterID()); +} + +void SaiIntf::program(const shared_ptr &intf) { + VLOG(4) << "Entering " << __FUNCTION__; + sai_status_t saiRetVal = SAI_STATUS_FAILURE; + + 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 SaiError("Interface router ID ( ", + static_cast(oldIntf->getRouterID()), " vs ", + static_cast(intf->getRouterID()), " ) or ", + "VLAN ID (", static_cast(oldIntf->getVlanID()), + " vs ", static_cast(intf->getVlanID()), + " ) is changed during programming"); + } + } else { + auto vrf = hw_->writableVrfTable()->incRefOrCreateSaiVrf(intf->getRouterID()); + + try { + vrf->program(); + } catch (const FbossError &e) { + hw_->writableVrfTable()->derefSaiVrf(intf->getRouterID()); + // let handle this in the caller. + throw; + } + + uint8_t attr_count = 0; + + sai_attribute_t attr; + sai_attribute_t attr_list[SAI_ROUTER_IF_ATTR_COUNT] = {0}; + + //Interface type + attr_list[0].id = SAI_ROUTER_INTERFACE_ATTR_TYPE; + attr_list[0].value.u32 = SAI_ROUTER_INTERFACE_TYPE_VLAN; + attr_count++; + + //VlanID + attr_list[1].id = SAI_ROUTER_INTERFACE_ATTR_VLAN_ID; + attr_list[1].value.u32 = intf->getVlanID(); + attr_count++; + + //Router ID + attr_list[2].id = SAI_ROUTER_INTERFACE_ATTR_VIRTUAL_ROUTER_ID; + attr_list[2].value.oid = hw_->writableVrfTable()->getSaiVrfId(intf->getRouterID()); + attr_count++; + + //Admin V4 state + attr_list[3].id = SAI_ROUTER_INTERFACE_ATTR_ADMIN_V4_STATE; + attr_list[3].value.u32 = 1; + attr_count++; + + //Admin V6 state + attr_list[4].id = SAI_ROUTER_INTERFACE_ATTR_ADMIN_V6_STATE; + attr_list[4].value.u32 = 1; + attr_count++; + + //MAC + attr_list[5].id = SAI_ROUTER_INTERFACE_ATTR_SRC_MAC_ADDRESS; + memcpy(&attr_list[5].value.mac, intf->getMac().bytes(), sizeof(attr_list[5].value.mac)); + attr_count++; + + sai_object_id_t rif_id = 0; + sai_status_t saiRetVal = saiRouterInterfaceApi_->create_router_interface(&rif_id, attr_count, attr_list); + + if (saiRetVal != SAI_STATUS_SUCCESS) { + throw SaiError("SAI create_router_interface() failed. Error: ", saiRetVal); + } + + saiIfId_ = rif_id; + } + + if ((oldIntf != nullptr) && (oldIntf->getMac() != intf->getMac())) { + //MAC + sai_attribute_t attr {0}; + attr.id = SAI_ROUTER_INTERFACE_ATTR_SRC_MAC_ADDRESS; + memcpy(&attr.value.mac, intf->getMac().bytes(), sizeof(attr.value.mac)); + saiRetVal = saiRouterInterfaceApi_->set_router_interface_attribute(saiIfId_, &attr); + if (saiRetVal != SAI_STATUS_SUCCESS) { + throw SaiError("set_router_interface_attribute() failed while setting MAC: ", + intf->getMac(), " retVal ", saiRetVal); + } + } +/* + //MTU + attr.id = SAI_ROUTER_INTERFACE_ATTR_MTU; + //TODO verify what MTU should be set here + attr.value.u32 = 1500; //ifParams.l3a_mtu = 1500; + saiRetVal = saiRouterInterfaceApi_->set_router_interface_attribute(rif_id, &attr); + if (saiRetVal != SAI_STATUS_SUCCESS) { + throw SaiError("SAI create_router_interface() failed : retVal ", saiRetVal); + } + */ + + // Need this temp variable since some strange + // compilation errors occur when try to call + // intf_->getMac() from lambda functions below. + folly::MacAddress oldMac; + + if (oldIntf != nullptr) { + oldMac = oldIntf->getMac(); + } + + auto createHost = [&](const IPAddress& addr) { + auto host = hw_->writableHostTable()->createOrUpdateSaiHost(intf->getID(), + addr, + intf->getMac()); + + try { + host->program(SAI_PACKET_ACTION_TRAP, intf->getMac()); + } catch (const FbossError &e) { + hw_->writableHostTable()->removeSaiHost(intf->getID(), addr); + // let handle this in the caller. + throw; + } + + auto ret = hosts_.insert(addr); + CHECK(ret.second); + SCOPE_FAIL { + hw_->writableHostTable()->removeSaiHost(intf->getID(), addr); + hosts_.erase(ret.first); + }; + }; + + auto ifsAreEqual = [&](const IPAddress& oldAddr, const IPAddress& newAddr) { + return ((oldAddr == newAddr) && + (intf_->getID() == intf->getID()) && + (oldMac == intf->getMac())); + }; + + + if (!oldIntf) { + // create host entries for all network IP addresses + for (const auto& addr : intf->getAddresses()) { + createHost(addr.first); + } + } else { + // address change + auto oldIter = oldIntf->getAddresses().begin(); + auto newIter = intf->getAddresses().begin(); + + while ((oldIter != oldIntf->getAddresses().end()) && + (newIter != intf->getAddresses().end())) { + + // If such an address has been removed or changed + // then need to remove the old host as well + if ((oldIter != oldIntf->getAddresses().end()) && + ((newIter == intf->getAddresses().end()) || + !ifsAreEqual(oldIter->first, newIter->first))) { + + removeHost(oldIter->first); + } + + // If a new address has been added or the old one changed + // then need to add a new host + if ((newIter != intf->getAddresses().end()) && + ((oldIter == oldIntf->getAddresses().end()) || + !ifsAreEqual(oldIter->first, newIter->first))) { + + createHost(newIter->first); + } + + ++oldIter; + ++newIter; + } + } + + intf_ = intf; +} + +void SaiIntf::removeHost(const folly::IPAddress& addr) { + auto iter = hosts_.find(addr); + if (iter == hosts_.end()) { + return; + } + + hw_->writableHostTable()->removeSaiHost(intf_->getID(), addr); + hosts_.erase(iter); +} + +}} // facebook::fboss diff --git a/fboss/agent/hw/sai/SaiIntf.h b/fboss/agent/hw/sai/SaiIntf.h new file mode 100644 index 0000000000000..d70d08a6930b9 --- /dev/null +++ b/fboss/agent/hw/sai/SaiIntf.h @@ -0,0 +1,71 @@ +/* + * Copyright (c) 2004-present, Facebook, Inc. + * Copyright (c) 2016, Cavium, Inc. + * All rights reserved. + * + * This source code is licensed under the BSD-style license found in the + * LICENSE file in the root directory of this source tree. An additional grant + * of patent rights can be found in the PATENTS file in the same directory. + * + */ +#pragma once + +#include + +#include "fboss/agent/state/Interface.h" +#include "fboss/agent/types.h" + +extern "C" { +#include "saitypes.h" +#include "sairouterintf.h" +} + +namespace facebook { namespace fboss { + +class SaiSwitch; +class SaiStation; +class SaiHost; + +class SaiIntf { +public: + explicit SaiIntf(const SaiSwitch *hw); + virtual ~SaiIntf(); + + sai_object_id_t getIfId() const { + return saiIfId_; + } + + const std::shared_ptr &getInterface() const { + return intf_; + } + void program(const std::shared_ptr &intf); + +private: + // no copy or assignment + SaiIntf(SaiIntf const &) = delete; + SaiIntf &operator=(SaiIntf const &) = delete; + + void removeHost(const folly::IPAddress& addr); + + enum { + SAI_ROUTER_IF_ATTR_COUNT = 7 + }; + + const SaiSwitch *hw_; + std::shared_ptr intf_ {0}; + sai_object_id_t saiIfId_ {SAI_NULL_OBJECT_ID}; + // The interface addresses that have SaiHost object created + std::set hosts_; + + sai_router_interface_api_t *saiRouterInterfaceApi_ { nullptr }; + + + // TODO: we now generate one station entry per interface, even if all + // interfaces are sharing the same MAC. We can save some station + // entries by just generate one per different MAC. But with + // the small number of interfaces we have, no gain for that now. + std::unique_ptr station_; + +}; + +}} // facebook::fboss diff --git a/fboss/agent/hw/sai/SaiIntfTable.cpp b/fboss/agent/hw/sai/SaiIntfTable.cpp new file mode 100644 index 0000000000000..95d7c94ae41f0 --- /dev/null +++ b/fboss/agent/hw/sai/SaiIntfTable.cpp @@ -0,0 +1,144 @@ +/* + * Copyright (c) 2004-present, Facebook, Inc. + * Copyright (c) 2016, Cavium, Inc. + * All rights reserved. + * + * This source code is licensed under the BSD-style license found in the + * LICENSE file in the root directory of this source tree. An additional grant + * of patent rights can be found in the PATENTS file in the same directory. + * + */ +#include "SaiIntf.h" +#include "SaiIntfTable.h" +#include "SaiSwitch.h" +#include "SaiError.h" + +using std::shared_ptr; +using std::unique_ptr; + +namespace facebook { namespace fboss { + +SaiIntfTable::SaiIntfTable(const SaiSwitch *hw) + : hw_(hw) { + VLOG(4) << "Entering " << __FUNCTION__; +} + +SaiIntfTable::~SaiIntfTable() { + VLOG(4) << "Entering " << __FUNCTION__; +} + +SaiIntf *SaiIntfTable::getIntfIf(sai_object_id_t id) const { + VLOG(4) << "Entering " << __FUNCTION__; + + auto iter = saiIntfs_.find(id); + + if (iter == saiIntfs_.end()) { + return nullptr; + } + + return iter->second; +} + +SaiIntf *SaiIntfTable::getIntf(sai_object_id_t id) const { + VLOG(4) << "Entering " << __FUNCTION__; + auto ptr = getIntfIf(id); + + if (ptr == nullptr) { + throw SaiError("Cannot find interface ", id); + } + + return ptr; +} + +SaiIntf *SaiIntfTable::getIntfIf(InterfaceID id) const { + VLOG(4) << "Entering " << __FUNCTION__; + + auto iter = intfs_.find(id); + + if (iter == intfs_.end()) { + return nullptr; + } + + return iter->second.get(); +} + +SaiIntf *SaiIntfTable::getIntf(InterfaceID id) const { + VLOG(4) << "Entering " << __FUNCTION__; + + auto ptr = getIntfIf(id); + + if (ptr == nullptr) { + throw SaiError("Cannot find interface ", id); + } + + return ptr; +} + +SaiIntf* SaiIntfTable::getFirstIntfIf() const { + VLOG(4) << "Entering " << __FUNCTION__; + + auto iter = intfs_.begin(); + + if (iter == intfs_.end()) { + return nullptr; + } + + return iter->second.get(); +} + +SaiIntf* SaiIntfTable::getNextIntfIf(const SaiIntf *intf) const { + + if (intf == nullptr) { + return nullptr; + } + + auto iter = intfs_.find(intf->getInterface()->getID()); + + if (iter != intfs_.end()) { + if(++iter != intfs_.end()) { + return iter->second.get(); + } + } + + return nullptr; +} + +void SaiIntfTable::addIntf(const shared_ptr &intf) { + VLOG(4) << "Entering " << __FUNCTION__; + + auto newIntf = unique_ptr(new SaiIntf(hw_)); + auto intfPtr = newIntf.get(); + auto ret = intfs_.insert(std::make_pair(intf->getID(), std::move(newIntf))); + + if (!ret.second) { + throw SaiError("Adding an existing interface ", intf->getID()); + } + + intfPtr->program(intf); + auto ret2 = saiIntfs_.insert(std::make_pair(intfPtr->getIfId(), intfPtr)); + CHECK_EQ(ret2.second, true); +} + +void SaiIntfTable::programIntf(const shared_ptr &intf) { + VLOG(4) << "Entering " << __FUNCTION__; + + auto intfPtr = getIntf(intf->getID()); + intfPtr->program(intf); +} + +void SaiIntfTable::deleteIntf(const std::shared_ptr &intf) { + VLOG(4) << "Entering " << __FUNCTION__; + + auto iter = intfs_.find(intf->getID()); + + if (iter == intfs_.end()) { + throw SaiError("Failed to delete a non-existing interface ", + intf->getID()); + } + + auto saiIfId = iter->second->getIfId(); + intfs_.erase(iter); + saiIntfs_.erase(saiIfId); +} + +}} // facebook::fboss diff --git a/fboss/agent/hw/sai/SaiIntfTable.h b/fboss/agent/hw/sai/SaiIntfTable.h new file mode 100644 index 0000000000000..583c39ea51865 --- /dev/null +++ b/fboss/agent/hw/sai/SaiIntfTable.h @@ -0,0 +1,57 @@ +/* + * Copyright (c) 2004-present, Facebook, Inc. + * Copyright (c) 2016, Cavium, Inc. + * All rights reserved. + * + * This source code is licensed under the BSD-style license found in the + * LICENSE file in the root directory of this source tree. An additional grant + * of patent rights can be found in the PATENTS file in the same directory. + * + */ +#pragma once + +#include + +#include "fboss/agent/state/Interface.h" +#include "fboss/agent/types.h" + +extern "C" { +#include "saitypes.h" +} + +namespace facebook { namespace fboss { + +class SaiSwitch; +class SaiIntf; + +class SaiIntfTable { +public: + explicit SaiIntfTable(const SaiSwitch *hw); + virtual ~SaiIntfTable(); + + // throw an error if not found + SaiIntf *getIntf(InterfaceID id) const; + SaiIntf *getIntf(sai_object_id_t id) const; + + // return nullptr if not found + SaiIntf *getIntfIf(InterfaceID id) const; + SaiIntf *getIntfIf(sai_object_id_t id) const; + SaiIntf *getFirstIntfIf() const; + SaiIntf *getNextIntfIf(const SaiIntf *intf) const; + + // The following functions will modify the object. They rely on the global + // HW update lock in SaiSwitch::lock_ for the protection. + void addIntf(const std::shared_ptr &intf); + void programIntf(const std::shared_ptr &intf); + void deleteIntf(const std::shared_ptr &intf); + +private: + const SaiSwitch *hw_; + // There are two mapping tables with different index types. + // Both are mapped to the SaiIntf. The SaiIntf object's life is + // controlled by table with InterfaceID as the index (intfs_). + boost::container::flat_map> intfs_; + boost::container::flat_map saiIntfs_; +}; + +}} // facebook::fboss diff --git a/fboss/agent/hw/sai/SaiNextHop.cpp b/fboss/agent/hw/sai/SaiNextHop.cpp new file mode 100644 index 0000000000000..c2726a3b954ed --- /dev/null +++ b/fboss/agent/hw/sai/SaiNextHop.cpp @@ -0,0 +1,281 @@ +/* + * Copyright (c) 2004-present, Facebook, Inc. + * Copyright (c) 2016, Cavium, Inc. + * All rights reserved. + * + * This source code is licensed under the BSD-style license found in the + * LICENSE file in the root directory of this source tree. An additional grant + * of patent rights can be found in the PATENTS file in the same directory. + * + */ +#include "fboss/agent/state/RouteForwardInfo.h" + +#include "SaiNextHop.h" +#include "SaiSwitch.h" +#include "SaiError.h" +#include "SaiIntfTable.h" +#include "SaiIntf.h" +#include "SaiHostTable.h" + +namespace facebook { namespace fboss { + +SaiNextHop::SaiNextHop(const SaiSwitch *hw, const RouteForwardInfo &fwdInfo) + : hw_(hw), + fwdInfo_(fwdInfo) { + + VLOG(4) << "Entering " << __FUNCTION__; + + for(auto nh : fwdInfo_.getNexthops()) { + + if (hw_->getHostTable()->getSaiHost(nh.intf, nh.nexthop)) { + resolvedNextHops_.emplace(nh); + } else { + unresolvedNextHops_.emplace(nh); + } + } + + isGroup_ = (fwdInfo_.getNexthops().size() > 1); + + saiNextHopApi_ = hw->getSaiNextHopApi(); + saiNextHopGroupApi_ = hw->getSaiNextHopGroupApi(); +} + +SaiNextHop::~SaiNextHop() { + VLOG(4) << "Entering " << __FUNCTION__; + + sai_status_t saiRetVal = SAI_STATUS_FAILURE; + + // Remove all next hops and group(if it was created) from the HW. + for (auto& nhPair : hwNextHops_) { + + if (nhGroupId_ != SAI_NULL_OBJECT_ID) { + + saiRetVal = saiNextHopGroupApi_->remove_next_hop_from_group(nhGroupId_, 1, &nhPair.first); + if (saiRetVal != SAI_STATUS_SUCCESS) { + LOG(ERROR) << "Could not remove next hop " << nhPair.second.str() << " from next hop group: " + << nhGroupId_ << " of next hops: " << fwdInfo_.str() << "Error: " << saiRetVal; + } + } + + saiRetVal = saiNextHopApi_->remove_next_hop(nhPair.first); + if (saiRetVal != SAI_STATUS_SUCCESS) { + LOG(ERROR) << "Could not remove next hop " << nhPair.second.str() + << " from HW. Error: " << saiRetVal; + } + } + + if (nhGroupId_ != SAI_NULL_OBJECT_ID) { + saiRetVal = saiNextHopGroupApi_->remove_next_hop_group(nhGroupId_); + if (saiRetVal != SAI_STATUS_SUCCESS) { + LOG(ERROR) << "Could not remove next hop group: " << nhGroupId_ + << " of next hops: " << fwdInfo_.str() << " from HW. Error: " << saiRetVal; + } + } +} + +sai_object_id_t SaiNextHop::getSaiNextHopId() const { + + if (isGroup_) { + return nhGroupId_; + } + + auto iter = hwNextHops_.begin(); + if (iter == hwNextHops_.end()) { + return SAI_NULL_OBJECT_ID; + } + + return iter->first; +} + +void SaiNextHop::onResolved(InterfaceID intf, const folly::IPAddress &ip) { + VLOG(4) << "Entering " << __FUNCTION__; + + sai_status_t saiRetVal {SAI_STATUS_FAILURE}; + + RouteForwardInfo::Nexthop nh(intf, ip); + + // Check if this next hop is in the list of unresolved ones. + auto iter = unresolvedNextHops_.find(nh); + if (iter == unresolvedNextHops_.end()) { + return; + } + + VLOG(3) << __FUNCTION__ << " Resolving NH: " << ip << "/" << intf; + + // Program the next hop on HW. + sai_object_id_t nhId = programNh(iter->intf, iter->nexthop); + if (nhId == SAI_NULL_OBJECT_ID) { + return; + } + + if (isGroup_) { + if (nhGroupId_ != SAI_NULL_OBJECT_ID) { + // NH Group already exists, so just add NH to it + saiRetVal = saiNextHopGroupApi_->add_next_hop_to_group(nhGroupId_, 1, &nhId); + if (saiRetVal != SAI_STATUS_SUCCESS) { + LOG(ERROR) << "Could not create next hop group" << nh.str() << " on HW. Error: " << saiRetVal; + saiNextHopApi_->remove_next_hop(nhId); + return; + } + + } else { + // No group existing. Create it with the next hop just resolved. + sai_object_id_t nhGroupId = programNhGroup(1, &nhId); + if (nhGroupId == SAI_NULL_OBJECT_ID) { + saiNextHopApi_->remove_next_hop(nhId); + return; + } + } + } + + hwNextHops_.emplace(std::make_pair(nhId, (*iter))); + resolvedNextHops_.emplace(*iter); + unresolvedNextHops_.erase(iter); + + resolved_ = true; +} + +void SaiNextHop::program() { + + VLOG(4) << "Entering " << __FUNCTION__; + + sai_status_t saiRetVal {SAI_STATUS_FAILURE}; + + if (fwdInfo_.getNexthops().empty()) { + throw SaiError("Next hop list to be programmed on HW is empty"); + } + + if ((nhGroupId_ != SAI_NULL_OBJECT_ID) || !hwNextHops_.empty()) { + // Already configured. Nothing to do more here. + return; + } + + // an array of object IDs passed to create_next_hop_group() + std::vector nextHopIds; + + // Create all next hops and fill the hwNextHops_ map and nextHopIds array + for (auto& nhPair : resolvedNextHops_) { + + sai_object_id_t nhId = programNh(nhPair.intf, nhPair.nexthop); + + if (nhId != SAI_NULL_OBJECT_ID) { + hwNextHops_.emplace(nhId, nhPair); + nextHopIds.push_back(nhId); + } + } + + if (!isGroup_ || nextHopIds.empty()) { + // Nothing more to do for a single next hop or a group + // with empty NH list. + return; + } + + // Add group with the Next Hops configured above on HW + sai_object_id_t nhGroupId = programNhGroup(nextHopIds.size(), nextHopIds.data()); + if (nhGroupId == SAI_NULL_OBJECT_ID) { + // Cleanup hosts + for (auto& nhPair : hwNextHops_) { + + saiRetVal = saiNextHopApi_->remove_next_hop(nhPair.first); + if (saiRetVal != SAI_STATUS_SUCCESS) { + LOG(ERROR) << "Could not remove next hop " << nhPair.second.str() + << " from HW. Error: " << saiRetVal; + } + + unresolvedNextHops_.emplace(nhPair.second); + } + + resolvedNextHops_.clear(); + hwNextHops_.clear(); + } + + resolved_ = true; +} + +sai_object_id_t SaiNextHop::programNh(InterfaceID intf, const folly::IPAddress &ip) { + + VLOG(4) << "Entering " << __FUNCTION__; + + sai_status_t saiRetVal {SAI_STATUS_FAILURE}; + std::vector attrList; + sai_object_id_t nhId {SAI_NULL_OBJECT_ID}; + sai_attribute_t attr; + + // Fill atributes + // NH type + attr.id = SAI_NEXT_HOP_ATTR_TYPE; + attr.value.u32 = SAI_NEXT_HOP_IP; + attrList.push_back(attr); + + // IP address + attr.id = SAI_NEXT_HOP_ATTR_IP; + + if (ip.isV4()) { + attr.value.ipaddr.addr_family = SAI_IP_ADDR_FAMILY_IPV4; + memcpy(&attr.value.ipaddr.addr.ip4, ip.bytes(), sizeof(attr.value.ip4)); + } else { + attr.value.ipaddr.addr_family = SAI_IP_ADDR_FAMILY_IPV6; + memcpy(attr.value.ipaddr.addr.ip6, ip.bytes(), sizeof(attr.value.ip6)); + } + attrList.push_back(attr); + + // Router interface ID + attr.id = SAI_NEXT_HOP_ATTR_ROUTER_INTERFACE_ID; + attr.value.oid = hw_->getIntfTable()->getIntfIf(intf)->getIfId(); + attrList.push_back(attr); + + // Create NH in SAI + saiRetVal = saiNextHopApi_->create_next_hop(&nhId, attrList.size(), attrList.data()); + if (saiRetVal != SAI_STATUS_SUCCESS) { + LOG(ERROR) << "Could not create next hop" << ip << " on HW. Error: " << saiRetVal; + } + + return nhId; +} + +sai_object_id_t SaiNextHop::programNhGroup(uint32_t nhCount, sai_object_id_t *nhIds) { + + VLOG(4) << "Entering " << __FUNCTION__; + + sai_status_t saiRetVal {SAI_STATUS_FAILURE}; + + if (nhGroupId_ == SAI_NULL_OBJECT_ID) { + // Now we will create NH group with the next hops list + std::vector groupAttrList; + sai_attribute_t attr; + sai_object_id_t nhGroupId = SAI_NULL_OBJECT_ID; + sai_object_list_t nhList = {nhCount, nhIds}; + + // Fill atributes + // NH group type + attr.id = SAI_NEXT_HOP_GROUP_ATTR_TYPE; + attr.value.u32 = SAI_NEXT_HOP_GROUP_ECMP; + groupAttrList.push_back(attr); + + // NH list + attr.id = SAI_NEXT_HOP_GROUP_ATTR_NEXT_HOP_LIST; + attr.value.objlist = nhList; + groupAttrList.push_back(attr); + + // Create NH group in SAI + saiRetVal = saiNextHopGroupApi_->create_next_hop_group(&nhGroupId, groupAttrList.size(), groupAttrList.data()); + if (saiRetVal != SAI_STATUS_SUCCESS) { + LOG(ERROR) << "Could not create next hop group" << fwdInfo_.str() << " on HW. Error: " << saiRetVal; + return SAI_NULL_OBJECT_ID; + } + + nhGroupId_ = nhGroupId; + + } else { + // Add NH to group in SAI + saiRetVal = saiNextHopGroupApi_->add_next_hop_to_group(nhGroupId_, nhCount, nhIds); + if (saiRetVal != SAI_STATUS_SUCCESS) { + LOG(ERROR) << "Could not add next hop to group " << fwdInfo_.str() << " on HW. Error: " << saiRetVal; + return SAI_NULL_OBJECT_ID; + } + } + + return nhGroupId_; +} + +}} // facebook::fboss diff --git a/fboss/agent/hw/sai/SaiNextHop.h b/fboss/agent/hw/sai/SaiNextHop.h new file mode 100644 index 0000000000000..bba10b13800a9 --- /dev/null +++ b/fboss/agent/hw/sai/SaiNextHop.h @@ -0,0 +1,117 @@ +/* + * Copyright (c) 2004-present, Facebook, Inc. + * Copyright (c) 2016, Cavium, Inc. + * All rights reserved. + * + * This source code is licensed under the BSD-style license found in the + * LICENSE file in the root directory of this source tree. An additional grant + * of patent rights can be found in the PATENTS file in the same directory. + * + */ +#pragma once + +#include +#include + +#include "fboss/agent/types.h" + +extern "C" { +#include "saitypes.h" +#include "sainexthop.h" +#include "sainexthopgroup.h" +} + +namespace facebook { namespace fboss { + +class SaiSwitch; +class RouteForwardInfo; + +class SaiNextHop { +public: + /** + * Constructs the SaiNextHop object. + * + * This method doesn't make any calls to the SAI to program next hop there. + * program() will be called soon after construction, and any + * actual initialization logic is be performed there. + */ + explicit SaiNextHop(const SaiSwitch *hw, const RouteForwardInfo &fwdInfo); + /** + * Destructs the SaiNextHop object. + * + * This method removes the next hops from the HW as well. + */ + virtual ~SaiNextHop(); + + /** + * Gets Sai Next Hop ID or Sai Next Hop Group ID + * of sai_object_id_t type. + * + * @return ID of Next Hop if it's already programmed + * to HW, otherwise SAI_NULL_OBJECT_ID. + */ + sai_object_id_t getSaiNextHopId() const; + + /** + * Checks whether the Next Hop is resolved. + * + * @return 'true' if Next Hop is resolved otherwise 'false' + */ + bool isResolved() const { + return resolved_; + } + + /** + * Programs Next Hop to HW in case the the unresolved + * Next Hop with "ip" is resolved. Stores + * SAI Next Hop ID or adds it to Next Hop Group ID(if fwdInfo_ + * contains several ECMP next hops) returned from the HW. + * + * @return none + */ + void onResolved(InterfaceID intf, const folly::IPAddress &ip); + + /** + * Programs next hop(s) from the fwdInfo_ in HW and stores + * SAI Next Hop ID or SAI Next Hop Group ID(if fwdInfo_ + * contains several ECMP next hops) returned from the HW. + * + * @return none + */ + void program(); + +private: + // no copy or assignment + SaiNextHop(SaiNextHop const &) = delete; + SaiNextHop &operator=(SaiNextHop const &) = delete; + + /** + * Programs single Next Hop on HW. + * + * @return ID of the Next Hop programmed or SAI_NULL_OBJECT_ID in + * case of fail. + */ + sai_object_id_t programNh(InterfaceID intf, const folly::IPAddress &ip); + /** + * programs Next Hop group with Next Hops IDs "nhIds" on HW. + * + * @return ID of the Next Hop Group programmed or SAI_NULL_OBJECT_ID in + * case of fail. + */ + sai_object_id_t programNhGroup(uint32_t nhCount, sai_object_id_t *nhIds); + + const SaiSwitch *hw_ {nullptr}; + + RouteForwardInfo fwdInfo_; + bool resolved_ {false}; + bool isGroup_ {false}; + std::set unresolvedNextHops_; + std::set resolvedNextHops_; + std::map hwNextHops_; + sai_object_id_t nhGroupId_ {SAI_NULL_OBJECT_ID}; + + sai_next_hop_api_t *saiNextHopApi_ {nullptr}; + sai_next_hop_group_api_t *saiNextHopGroupApi_ {nullptr}; +}; + +}} // facebook::fboss diff --git a/fboss/agent/hw/sai/SaiNextHopTable.cpp b/fboss/agent/hw/sai/SaiNextHopTable.cpp new file mode 100644 index 0000000000000..413259fd799e7 --- /dev/null +++ b/fboss/agent/hw/sai/SaiNextHopTable.cpp @@ -0,0 +1,95 @@ +/* + * Copyright (c) 2004-present, Facebook, Inc. + * Copyright (c) 2016, Cavium, Inc. + * All rights reserved. + * + * This source code is licensed under the BSD-style license found in the + * LICENSE file in the root directory of this source tree. An additional grant + * of patent rights can be found in the PATENTS file in the same directory. + * + */ +#include "fboss/agent/state/RouteForwardInfo.h" + +#include "SaiNextHopTable.h" +#include "SaiNextHop.h" +#include "SaiSwitch.h" +#include "SaiError.h" + +namespace facebook { namespace fboss { + +SaiNextHopTable::SaiNextHopTable(const SaiSwitch *hw) : hw_(hw) { + VLOG(4) << "Entering " << __FUNCTION__; +} + +SaiNextHopTable::~SaiNextHopTable() { + VLOG(4) << "Entering " << __FUNCTION__; +} + +sai_object_id_t SaiNextHopTable::getSaiNextHopId(const RouteForwardInfo &fwdInfo) const { + VLOG(4) << "Entering " << __FUNCTION__; + + auto iter = nextHops_.find(fwdInfo.getNexthops()); + + if (iter == nextHops_.end()) { + return SAI_NULL_OBJECT_ID; + } + + return iter->second.first->getSaiNextHopId(); +} + +SaiNextHop* SaiNextHopTable::incRefOrCreateSaiNextHop(const RouteForwardInfo &fwdInfo) { + VLOG(4) << "Entering " << __FUNCTION__; + + auto ret = nextHops_.emplace(fwdInfo.getNexthops(), std::make_pair(nullptr, 1)); + auto &iter = ret.first; + + if (!ret.second) { + // there was an entry already in the map + iter->second.second++; // increase the reference counter + return iter->second.first.get(); + } + + SCOPE_FAIL { + nextHops_.erase(iter); + }; + + auto newNextHop = folly::make_unique(hw_, fwdInfo); + newNextHop->program(); + auto nextHopPtr = newNextHop.get(); + iter->second.first = std::move(newNextHop); + + return nextHopPtr; +} + +SaiNextHop* SaiNextHopTable::derefSaiNextHop(const RouteForwardInfo &fwdInfo) noexcept { + VLOG(4) << "Entering " << __FUNCTION__; + + auto iter = nextHops_.find(fwdInfo.getNexthops()); + + if (iter == nextHops_.end()) { + return nullptr; + } + + auto &entry = iter->second; + CHECK_GT(entry.second, 0); + + if (--entry.second == 0) { + nextHops_.erase(iter); + return nullptr; + } + + return entry.first.get(); +} + +void SaiNextHopTable::onResolved(InterfaceID intf, const folly::IPAddress &ip) { + + for (auto& entry : nextHops_) { + try { + entry.second.first->onResolved(intf, ip); + } catch (const SaiError &e) { + LOG(ERROR) << e.what(); + } + } +} + +}} // facebook::fboss diff --git a/fboss/agent/hw/sai/SaiNextHopTable.h b/fboss/agent/hw/sai/SaiNextHopTable.h new file mode 100644 index 0000000000000..1a4af80bc72ad --- /dev/null +++ b/fboss/agent/hw/sai/SaiNextHopTable.h @@ -0,0 +1,83 @@ +/* + * Copyright (c) 2004-present, Facebook, Inc. + * Copyright (c) 2016, Cavium, Inc. + * All rights reserved. + * + * This source code is licensed under the BSD-style license found in the + * LICENSE file in the root directory of this source tree. An additional grant + * of patent rights can be found in the PATENTS file in the same directory. + * + */ +#pragma once + +#include + +#include "fboss/agent/state/RouteForwardInfo.h" + +extern "C" { +#include "saitypes.h" +} + +namespace facebook { namespace fboss { + +class SaiSwitch; +class SaiNextHop; + +class SaiNextHopTable { +public: + explicit SaiNextHopTable(const SaiSwitch *hw); + virtual ~SaiNextHopTable(); + + /** + * Given fwdInfo gets Sai Next Hop ID of sai_object_id_t type. + * + * @return Sai Next Hop ID of sai_object_id_t type if found, + * otherwise SAI_NULL_OBJECT_ID + */ + sai_object_id_t getSaiNextHopId(const RouteForwardInfo &fwdInfo) const; + + /* + * The following functions will modify the object. They rely on the global + * HW update lock in SaiSwitch::lock_ for the protection. + * + * SaiNextHopTable maintains a reference counter for each SaiNextHop + * entry allocated. + */ + + /** + * Allocates a new SaiNextHop if no such one exists. For the existing + * entry, incRefOrCreateSaiNextHop() will increase the reference counter by 1. + * + * @return The SaiNextHop pointer just created or found. + */ + SaiNextHop* incRefOrCreateSaiNextHop(const RouteForwardInfo &fwdInfo); + + /** + * Decreases an existing SaiNextHop entry's reference counter by 1. + * Only when the reference counter is 0, the SaiNextHop entry + * is deleted. + * + * @return nullptr, if the SaiNextHop entry is deleted + * @retrun the SaiNextHop object that has reference counter + * decreased by 1, but the object is still valid as it is + * still referred in somewhere else + */ + SaiNextHop *derefSaiNextHop(const RouteForwardInfo &fwdInfo) noexcept; + + /** + * Loops trough all the Next Hops and resolves(if not resolved yet) + * those with 'intf' and 'ip' + * + * @return none + */ + void onResolved(InterfaceID intf, const folly::IPAddress &ip); + +private: + typedef std::pair, uint32_t> NextHopMapNode; + typedef boost::container::flat_map NextHopMap; + + const SaiSwitch *hw_; + NextHopMap nextHops_; +}; + +}} // facebook::fboss diff --git a/fboss/agent/hw/sai/SaiPlatformBase.h b/fboss/agent/hw/sai/SaiPlatformBase.h new file mode 100644 index 0000000000000..6b7064f5d29cd --- /dev/null +++ b/fboss/agent/hw/sai/SaiPlatformBase.h @@ -0,0 +1,47 @@ +/* + * Copyright (c) 2004-present, Facebook, Inc. + * Copyright (c) 2016, Cavium, Inc. + * All rights reserved. + * + * This source code is licensed under the BSD-style license found in the + * LICENSE file in the root directory of this source tree. An additional grant + * of patent rights can be found in the PATENTS file in the same directory. + * + */ +#pragma once + +#include + +#include "fboss/agent/Platform.h" + +extern "C" { +#include "saitypes.h" +} + +namespace facebook { namespace fboss { + +class SaiPlatformPort; + +class SaiPlatformBase : public Platform { +public: + typedef std::map InitPortMap; + + SaiPlatformBase() {} + + /* + * initPorts() will be called during port initialization. + * + * The SaiPlatformBase should return a map of SAI port ID to SaiPlatformPort + * objects. The SaiPlatformBase object will retain ownership of all the + * SaiPlatformPort objects, and must ensure that they remain valid for as + * long as the SaiSwitch exists. + */ + virtual InitPortMap initPorts() = 0; + +private: + // Forbidden copy constructor and assignment operator + SaiPlatformBase(SaiPlatformBase const &) = delete; + SaiPlatformBase &operator=(SaiPlatformBase const &) = delete; +}; + +}} // facebook::fboss diff --git a/fboss/agent/hw/sai/SaiPlatformPort.h b/fboss/agent/hw/sai/SaiPlatformPort.h new file mode 100644 index 0000000000000..616caf3381951 --- /dev/null +++ b/fboss/agent/hw/sai/SaiPlatformPort.h @@ -0,0 +1,37 @@ +/* + * Copyright (c) 2004-present, Facebook, Inc. + * Copyright (c) 2016, Cavium, Inc. + * All rights reserved. + * + * This source code is licensed under the BSD-style license found in the + * LICENSE file in the root directory of this source tree. An additional grant + * of patent rights can be found in the PATENTS file in the same directory. + * + */ +#pragma once + +#include "fboss/agent/PlatformPort.h" + +namespace facebook { namespace fboss { + +class SaiPortBase; + +class SaiPlatformPort : public PlatformPort { +public: + SaiPlatformPort() {} + SaiPlatformPort(SaiPlatformPort&&) = default; + SaiPlatformPort &operator=(SaiPlatformPort&&) = default; + + /* + * setPort() should be called once during port initialization. + */ + virtual void setPort(SaiPortBase *port) = 0; + virtual SaiPortBase *getPort() const = 0; + +private: + // Forbidden copy constructor and assignment operator + SaiPlatformPort(SaiPlatformPort const &) = delete; + SaiPlatformPort &operator=(SaiPlatformPort const &) = delete; +}; + +}} // facebook::fboss diff --git a/fboss/agent/hw/sai/SaiPortBase.cpp b/fboss/agent/hw/sai/SaiPortBase.cpp new file mode 100644 index 0000000000000..d0c8887a50425 --- /dev/null +++ b/fboss/agent/hw/sai/SaiPortBase.cpp @@ -0,0 +1,139 @@ +/* + * Copyright (c) 2004-present, Facebook, Inc. + * Copyright (c) 2016, Cavium, Inc. + * All rights reserved. + * + * This source code is licensed under the BSD-style license found in the + * LICENSE file in the root directory of this source tree. An additional grant + * of patent rights can be found in the PATENTS file in the same directory. + * + */ +#include "SaiPortBase.h" +#include "SaiSwitch.h" +#include "SaiPlatformPort.h" +#include "SaiError.h" + +extern "C" { +#include "sai.h" +} + +namespace facebook { namespace fboss { + +SaiPortBase::SaiPortBase(SaiSwitch *hw, sai_object_id_t saiPortId, PortID fbossPortId, SaiPlatformPort *platformPort) + : hw_(hw), + platformPort_(platformPort), + saiPortId_(saiPortId), + fbossPortId_(fbossPortId) { + VLOG(6) << "Entering " << __FUNCTION__; + + sai_api_query(SAI_API_PORT, (void **) &saiPortApi_); +} + +SaiPortBase::~SaiPortBase() { + VLOG(4) << "Entering " << __FUNCTION__; +} + +void SaiPortBase::init(bool warmBoot) { + VLOG(6) << "Entering " << __FUNCTION__; + + try { + setIngressVlan(pvId_); + } catch (const SaiError &e) { + LOG(ERROR) << e.what(); + } + + disable(); + + initDone_ = true; +} + +void SaiPortBase::setPortStatus(bool linkStatus) { + VLOG(6) << "Entering " << __FUNCTION__; + + if (initDone_ && (linkStatus_ == linkStatus)) { + return; + } + + VLOG(6) << "Set port: " << fbossPortId_.t << " status to: " << linkStatus; + // We ignore the return value. If we fail to get the port status + // we just tell the platformPort_ that it is enabled. + platformPort_->linkStatusChanged(linkStatus, adminMode_); + linkStatus_ = linkStatus; +} + +void SaiPortBase::setIngressVlan(VlanID vlan) { + VLOG(6) << "Entering " << __FUNCTION__; + + if (initDone_ && (pvId_ == vlan)) { + return; + } + + sai_status_t saiStatus = SAI_STATUS_SUCCESS; + sai_attribute_t attr; + + attr.id = SAI_PORT_ATTR_PORT_VLAN_ID; + attr.value.u16 = vlan.t; + + saiStatus = saiPortApi_->set_port_attribute(saiPortId_, &attr); + if(SAI_STATUS_SUCCESS != saiStatus) { + throw SaiError("Failed to update ingress VLAN for port ", fbossPortId_.t); + } + + VLOG(6) << "Set port: " << fbossPortId_.t << " ingress VLAN to: " << vlan.t; + + pvId_ = vlan; +} + +void SaiPortBase::enable() { + if (isEnabled()) { + // Port is already enabled, don't need to do anything + return; + } + + sai_attribute_t attr {0}; + attr.id = SAI_PORT_ATTR_ADMIN_STATE; + attr.value.booldata = true; + + sai_status_t saiStatus = saiPortApi_->set_port_attribute(saiPortId_, &attr); + if(SAI_STATUS_SUCCESS != saiStatus) { + LOG(ERROR) << "Failed to enable port admin mode. Error: " << saiStatus; + } + + adminMode_ = true; + VLOG(3) << "Enabled port: " << fbossPortId_; + // TODO: Temporary solution. Will need to get operational status from HW. + setPortStatus(adminMode_); +} + +void SaiPortBase::disable() { + if (initDone_ && !isEnabled()) { + // Port is already disabled, don't need to do anything + return; + } + + sai_attribute_t attr {0}; + attr.id = SAI_PORT_ATTR_ADMIN_STATE; + attr.value.booldata = false; + + sai_status_t saiStatus = saiPortApi_->set_port_attribute(saiPortId_, &attr); + if(SAI_STATUS_SUCCESS != saiStatus) { + LOG(ERROR) << "Failed to disable port admin mode. Error: " << saiStatus; + } + + adminMode_ = false; + VLOG(3) << "Disabled port: " << fbossPortId_; + // TODO: Temporary solution. Will need to get operational status from HW. + setPortStatus(adminMode_); +} + +std::string SaiPortBase::statName(folly::StringPiece name) const { + VLOG(6) << "Entering " << __FUNCTION__; + + return folly::to("port", platformPort_->getPortID(), ".", name); +} + +void SaiPortBase::updateStats() { + VLOG(6) << "Entering " << __FUNCTION__; +} + +}} // facebook::fboss diff --git a/fboss/agent/hw/sai/SaiPortBase.h b/fboss/agent/hw/sai/SaiPortBase.h new file mode 100644 index 0000000000000..c5a8caa0ab4cd --- /dev/null +++ b/fboss/agent/hw/sai/SaiPortBase.h @@ -0,0 +1,123 @@ +/* + * Copyright (c) 2004-present, Facebook, Inc. + * Copyright (c) 2016, Cavium, Inc. + * All rights reserved. + * + * This source code is licensed under the BSD-style license found in the + * LICENSE file in the root directory of this source tree. An additional grant + * of patent rights can be found in the PATENTS file in the same directory. + * + */ +#pragma once + +#include "fboss/agent/types.h" +#include "common/stats/MonotonicCounter.h" + +extern "C" { +#include "saitypes.h" +#include "saiport.h" +} + +namespace facebook { namespace fboss { + +using namespace facebook; + +class SaiPlatformPort; +class SaiSwitch; + +class SaiPortBase { +public: + /** + * @brief Constructor. All initialization steps should be done through init + * @param hw, pointer to SaiSwitch object + * @param saiPortId, sai_port_id_t port ID + * @param fbossPortId, fboss port ID + * @param platformPort, pointer to SaiPlatformPort object + */ + SaiPortBase(SaiSwitch *hw, sai_object_id_t saiPortId, PortID fbossPortId, SaiPlatformPort *platformPort); + virtual ~SaiPortBase(); + + /** + * @brief initialize port + * @param warmBoot, switch boot type + */ + void init(bool warmBoot); + + /* + * Getters. + */ + SaiPlatformPort *getPlatformPort() const { + return platformPort_; + } + + SaiSwitch *getHwSwitch() const { + return hw_; + } + + sai_object_id_t getSaiPortId() const { + return saiPortId_; + } + + PortID getFbossPortId() const { + return fbossPortId_; + } + + VlanID getIngressVlan() { + return pvId_; + } + + bool isEnabled() { + return adminMode_; + } + /* + * Setters. + */ + void enable(); + void disable(); + void setPortStatus(bool linkStatus); + + void setIngressVlan(VlanID vlan); + + /* + * Update this port's statistics. + */ + void updateStats(); + +private: + // Forbidden copy constructor and assignment operator + SaiPortBase(SaiPortBase const &) = delete; + SaiPortBase &operator=(SaiPortBase const &) = delete; + + std::string statName(folly::StringPiece name) const; + + SaiSwitch *const hw_ { nullptr }; // Pointer to HW Switch + SaiPlatformPort *const platformPort_ { nullptr }; // Pointer to Platform port + + sai_object_id_t saiPortId_ {0}; + PortID fbossPortId_ {0}; + VlanID pvId_ {0}; + bool adminMode_ {false}; + bool linkStatus_ {true}; + bool initDone_ {false}; + + sai_port_api_t *saiPortApi_ { nullptr }; + +private: + class MonotonicCounter : public stats::MonotonicCounter { + public: + // Inherit stats::MonotonicCounter constructors + using stats::MonotonicCounter::MonotonicCounter; + + // Export SUM and RATE by default + explicit MonotonicCounter(const std::string &name) + : stats::MonotonicCounter(name, stats::SUM, stats::RATE) {} + }; + + MonotonicCounter inBytes_ { statName("in_bytes") }; + MonotonicCounter inMulticastPkts_ { statName("in_multicast_pkts") }; + MonotonicCounter inBroadcastPkts_ { statName("in_broadcast_pkts") }; + + MonotonicCounter outBytes_ { statName("out_bytes") }; +}; + +}} // facebook::fboss diff --git a/fboss/agent/hw/sai/SaiPortTable.cpp b/fboss/agent/hw/sai/SaiPortTable.cpp new file mode 100644 index 0000000000000..2b4d220b1688f --- /dev/null +++ b/fboss/agent/hw/sai/SaiPortTable.cpp @@ -0,0 +1,102 @@ +/* + * Copyright (c) 2004-present, Facebook, Inc. + * Copyright (c) 2016, Cavium, Inc. + * All rights reserved. + * + * This source code is licensed under the BSD-style license found in the + * LICENSE file in the root directory of this source tree. An additional grant + * of patent rights can be found in the PATENTS file in the same directory. + * + */ +#include "SaiPortTable.h" +#include "SaiPortBase.h" +#include "SaiSwitch.h" +#include "SaiPlatformBase.h" +#include "SaiPlatformPort.h" +#include "SaiError.h" + +using folly::make_unique; +using std::unique_ptr; +using std::make_pair; + +namespace facebook { namespace fboss { + +SaiPortTable::SaiPortTable(SaiSwitch *hw) + :hw_(hw) { + VLOG(4) << "Entering " << __FUNCTION__; +} + +SaiPortTable::~SaiPortTable() { + VLOG(4) << "Entering " << __FUNCTION__; +} + +void SaiPortTable::initPorts(bool warmBoot) { + VLOG(4) << "Entering " << __FUNCTION__; + + auto platformPorts = hw_->getPlatform()->initPorts(); + + for (const auto& entry : platformPorts) { + sai_object_id_t saiPortId = entry.first; + SaiPlatformPort *platformPort = entry.second; + + PortID fbossPortID = platformPort->getPortID(); + auto saiPortBase = make_unique(hw_, saiPortId, fbossPortID, platformPort); + platformPort->setPort(saiPortBase.get()); + saiPortBase->init(warmBoot); + + fbossPhysicalPorts_.emplace(fbossPortID, saiPortBase.get()); + saiPhysicalPorts_.emplace(saiPortId, std::move(saiPortBase)); + } +} + +sai_object_id_t SaiPortTable::getSaiPortId(PortID id) const { + return getSaiPort(id)->getSaiPortId(); +} + +PortID SaiPortTable::getPortId(sai_object_id_t portId) const { + return getSaiPort(portId)->getFbossPortId(); +} + +SaiPortBase *SaiPortTable::getSaiPort(PortID id) const { + VLOG(6) << "Entering " << __FUNCTION__; + + auto iter = fbossPhysicalPorts_.find(id); + + if (iter == fbossPhysicalPorts_.end()) { + throw SaiError("Cannot find the SAI port object for FBOSS port ID ", id); + } + + return iter->second; +} + +SaiPortBase *SaiPortTable::getSaiPort(sai_object_id_t id) const { + VLOG(6) << "Entering " << __FUNCTION__; + + auto iter = saiPhysicalPorts_.find(id); + + if (iter == saiPhysicalPorts_.end()) { + std::stringstream stream; + stream << "0x" << std::hex << id; + throw SaiError("Cannot find the SAI port object for SAI port ", stream.str()); + } + + return iter->second.get(); +} + +void SaiPortTable::setPortStatus(sai_object_id_t portId, int status) { + VLOG(6) << "Entering " << __FUNCTION__; + + auto port = getSaiPort(portId); + port->setPortStatus(status); +} + +void SaiPortTable::updatePortStats() { + VLOG(6) << "Entering " << __FUNCTION__; + + for (const auto& entry : saiPhysicalPorts_) { + SaiPortBase *saiPort = entry.second.get(); + saiPort->updateStats(); + } +} + +}} // facebook::fboss diff --git a/fboss/agent/hw/sai/SaiPortTable.h b/fboss/agent/hw/sai/SaiPortTable.h new file mode 100644 index 0000000000000..4821767eaf0ff --- /dev/null +++ b/fboss/agent/hw/sai/SaiPortTable.h @@ -0,0 +1,78 @@ +/* + * Copyright (c) 2004-present, Facebook, Inc. + * Copyright (c) 2016, Cavium, Inc. + * All rights reserved. + * + * This source code is licensed under the BSD-style license found in the + * LICENSE file in the root directory of this source tree. An additional grant + * of patent rights can be found in the PATENTS file in the same directory. + * + */ +#pragma once + +#include + +#include "fboss/agent/types.h" + +extern "C" { +#include "saitypes.h" +} + +namespace facebook { namespace fboss { + +class SaiPortBase; +class SaiSwitch; + +class SaiPortTable { +public: + explicit SaiPortTable(SaiSwitch *hw); + virtual ~SaiPortTable(); + + /* + * Initialize the port table from the list of physical switch ports. + * + * No other SaiPortTable methods should be accessed before initPorts() + * completes. + */ + void initPorts(bool warmBoot); + + /* + * Getters. + */ + sai_object_id_t getSaiPortId(PortID id) const; + PortID getPortId(sai_object_id_t portId) const; + + // Throw an error if not found + SaiPortBase *getSaiPort(PortID id) const; + SaiPortBase *getSaiPort(sai_object_id_t id) const; + + /* + * Indicate that a port's link status has changed. + */ + void setPortStatus(sai_object_id_t portId, int status); + + /* + * Update all ports' statistics. + */ + void updatePortStats(); + +private: + typedef boost::container::flat_map> SaiPortMap; + typedef boost::container::flat_map FbossPortMap; + + SaiSwitch *hw_ {nullptr}; + + // Mappings for the physical ports. + // The set of physical ports is defined in initPorts(), and is read-only + // afterwards (since the physical ports on the switch cannot change). + // Therefore we don't need any locking on this data structure. + // (Modifiable data in the SaiPort objects themselves does require locking, + // though.) + + // A mapping from sai_object_id_t to SaiPort. + SaiPortMap saiPhysicalPorts_; + // A mapping from FBOSS PortID to SaiPort. + FbossPortMap fbossPhysicalPorts_; +}; + +}} // facebook::fboss diff --git a/fboss/agent/hw/sai/SaiRoute.cpp b/fboss/agent/hw/sai/SaiRoute.cpp new file mode 100644 index 0000000000000..acdff810465e8 --- /dev/null +++ b/fboss/agent/hw/sai/SaiRoute.cpp @@ -0,0 +1,265 @@ +/* + * Copyright (c) 2004-present, Facebook, Inc. + * Copyright (c) 2016, Cavium, Inc. + * All rights reserved. + * + * This source code is licensed under the BSD-style license found in the + * LICENSE file in the root directory of this source tree. An additional grant + * of patent rights can be found in the PATENTS file in the same directory. + * + */ +#include "SaiRoute.h" +#include "SaiVrfTable.h" +#include "SaiVrf.h" +#include "SaiIntfTable.h" +#include "SaiIntf.h" +#include "SaiNextHopTable.h" +#include "SaiNextHop.h" +#include "SaiSwitch.h" +#include "SaiError.h" + +namespace facebook { namespace fboss { + +SaiRoute::SaiRoute(const SaiSwitch *hw, + RouterID vrf, + const folly::IPAddress &addr, + uint8_t prefixLen) + : hw_(hw) + , vrf_(vrf) + , ipAddr_(addr) + , prefixLen_(prefixLen) { + + VLOG(4) << "Entering " << __FUNCTION__; + + auto intfPtr = hw_->getIntfTable()->getFirstIntfIf(); + while (intfPtr != nullptr) { + for (const auto& addrPair : intfPtr->getInterface()->getAddresses()) { + // If route IP adress is in the same subnet as an IP of one of the L3 interfaces + // then we deal with local route + if (addrPair.first.inSubnet(addr, prefixLen)) { + isLocal_ = true; + break; + } + } + intfPtr = hw_->getIntfTable()->getNextIntfIf(intfPtr); + } + + saiRouteApi_ = hw->getSaiRouteApi(); +} + +SaiRoute::~SaiRoute() { + VLOG(4) << "Entering " << __FUNCTION__; + + if(!added_) { + return; + } + + sai_status_t saiRetVal = saiRouteApi_->remove_route(&routeEntry_); + if (saiRetVal != SAI_STATUS_SUCCESS) { + LOG(ERROR) << "SAI remove_route() failed for route with address: " + << ipAddr_.str() << "/" << prefixLen_ << " Error: " << saiRetVal; + } + + if (fwd_.getAction() == RouteForwardAction::NEXTHOPS) { + // derefernce next hop + hw_->writableNextHopTable()->derefSaiNextHop(fwd_); + } + + // dereference vrf_ + hw_->writableVrfTable()->derefSaiVrf(vrf_); +} + +void SaiRoute::program(const RouteForwardInfo &fwd) { + VLOG(4) << "Entering " << __FUNCTION__; + + if (isLocal_) { + // FBOSS creates routes for local IP adresses of L3 interfaces + // For example if IP address of an interface is 10.10.10.1/24 + // there will be the next route created: 10.10.10.0 => 10.10.10.1/24. + // If such a route is programmed in HW all the traffic destined to other hosts + // from the same subnet will be dropped or trapped to cpu. + // In order to avoid this just return without programming to hardware. + VLOG(2) << "Local route " << ipAddr_ << "/" << prefixLen_ << " => " + << fwd << " will not be programmed to HW"; + return; + } + + auto newAction = fwd.getAction(); + + if ((newAction == RouteForwardAction::NEXTHOPS) && fwd.getNexthops().empty()) { + throw SaiError("RouteForwardInfo has empty next hops list"); + } + + if (added_) { + // if the route has been programmed to the HW and the forward info is + // not changed nothing to do. + if (fwd == fwd_) { + return; + } + + if (fwd_.getAction() == RouteForwardAction::NEXTHOPS) { + // derefernce old next hop + hw_->writableNextHopTable()->derefSaiNextHop(fwd_); + } + + } else { + // vrf as well as the route itself has to be referenced/created only + // for the first time + auto vrf = hw_->writableVrfTable()->incRefOrCreateSaiVrf(vrf_); + + try { + vrf->program(); + } catch (const SaiError &e) { + hw_->writableVrfTable()->derefSaiVrf(vrf_); + // let handle this in the caller. + throw; + } + + routeEntry_.vr_id = vrf->getSaiVrfId(); + + if (ipAddr_.empty()) { + throw SaiError("Could not program route with empty subnet IP address"); + } + + if ((!ipAddr_.isZero() && (prefixLen_ == 0)) || prefixLen_ > ipAddr_.bitCount()) { + throw SaiError("Wrong subnet IP prefix length ", prefixLen_, " specified"); + } + + // fill routeEntry_ + if (ipAddr_.isV4()) { + // IPv4 + routeEntry_.destination.addr_family = SAI_IP_ADDR_FAMILY_IPV4; + + memcpy(&routeEntry_.destination.addr.ip4, ipAddr_.bytes(), + sizeof(routeEntry_.destination.addr.ip4)); + + const uint8_t* mask = nullptr; + if (ipAddr_.isZero() && (prefixLen_ == 0)) { + // default gateway case + static const uint8_t zeroMask[4] = {0}; + mask = zeroMask; + } else { + mask = folly::IPAddressV4::fetchMask(prefixLen_).data(); + } + + memcpy(&routeEntry_.destination.mask.ip4, mask, + sizeof(routeEntry_.destination.mask.ip4)); + } else { + // IPv6 + routeEntry_.destination.addr_family = SAI_IP_ADDR_FAMILY_IPV6; + + memcpy(routeEntry_.destination.addr.ip6, ipAddr_.bytes(), + sizeof(routeEntry_.destination.addr.ip6)); + + const uint8_t* mask = nullptr; + if (ipAddr_.isZero() && (prefixLen_ == 0)) { + // default gateway case + static const uint8_t zeroMask[128] = {0}; + mask = zeroMask; + } else { + mask = folly::IPAddressV6::fetchMask(prefixLen_).data(); + } + + memcpy(routeEntry_.destination.mask.ip6, mask, + sizeof(routeEntry_.destination.mask.ip6)); + } + + // create route + sai_status_t saiRetVal = saiRouteApi_->create_route(&routeEntry_, 0, NULL); + if (saiRetVal != SAI_STATUS_SUCCESS) { + throw SaiError("Could not create route with addr: ", ipAddr_, + " netmask length: ", prefixLen_, " vrf: ", vrf_, + " Error: ", saiRetVal); + } + + added_ = true; + } + + sai_attribute_t routeAttr = {0}; + + // fill atributes + // route packet action + routeAttr.id = SAI_ROUTE_ATTR_PACKET_ACTION; + + switch (newAction) { + + case RouteForwardAction::DROP: + resolved_ = true; + routeAttr.value.u32 = SAI_PACKET_ACTION_DROP; + break; + + case RouteForwardAction::TO_CPU: + resolved_ = true; + routeAttr.value.u32 = SAI_PACKET_ACTION_TRAP; + break; + + case RouteForwardAction::NEXTHOPS: + routeAttr.value.u32 = SAI_PACKET_ACTION_FORWARD; + break; + + default: + throw SaiError("Unknown RouteForwardAction ", newAction, " specified"); + } + + // Set packet action route attribute in SAI + sai_status_t saiRetVal = saiRouteApi_->set_route_attribute(&routeEntry_, &routeAttr); + if (saiRetVal != SAI_STATUS_SUCCESS) { + throw SaiError("Could not set packet action attribute for route with addr: ", ipAddr_, + " netmask length: ", prefixLen_, " vrf: ", vrf_, + " Error: ", saiRetVal); + } + + if (newAction == RouteForwardAction::NEXTHOPS) { + // fill Next Hop ID + routeAttr.id = SAI_ROUTE_ATTR_NEXT_HOP_ID; + routeAttr.value.oid = hw_->writableNextHopTable()->incRefOrCreateSaiNextHop(fwd)->getSaiNextHopId(); + + if (routeAttr.value.oid != SAI_NULL_OBJECT_ID) { + // Set next hop ID route attribute in SAI + saiRetVal = saiRouteApi_->set_route_attribute(&routeEntry_, &routeAttr); + if (saiRetVal != SAI_STATUS_SUCCESS) { + throw SaiError("Could not set next hop ID attribute for route with addr: ", ipAddr_, + " netmask length: ", prefixLen_, " vrf: ", vrf_, + " Error: ", saiRetVal); + } + } + + resolved_ = (routeAttr.value.oid != SAI_NULL_OBJECT_ID) ? true : false; + } + + fwd_ = fwd; +} + +void SaiRoute::onResolved(InterfaceID intf, const folly::IPAddress &ip) { + + sai_status_t saiRetVal {SAI_STATUS_FAILURE}; + + RouteForwardInfo::Nexthop nh(intf, ip); + + // Check if this next hop is in the list of unresolved ones. + auto iter = fwd_.getNexthops().find(nh); + if (iter == fwd_.getNexthops().end()) { + return; + } + + VLOG(3) << __FUNCTION__ << " Resolving route: " << ipAddr_ << "=>" << fwd_ << " with NH: " << ip << "/" << intf; + + sai_attribute_t routeAttr {0, 0}; + routeAttr.id = SAI_ROUTE_ATTR_NEXT_HOP_ID; + routeAttr.value.oid = hw_->getNextHopTable()->getSaiNextHopId(fwd_); + + if (routeAttr.value.oid != SAI_NULL_OBJECT_ID) { + + // Set next hop ID route attribute in SAI + saiRetVal = saiRouteApi_->set_route_attribute(&routeEntry_, &routeAttr); + if (saiRetVal != SAI_STATUS_SUCCESS) { + LOG(ERROR) << "Could not set next hop ID attribute for route with addr: " << ipAddr_ + << " netmask length: " << prefixLen_ << " vrf: " << vrf_ << " Error: " << saiRetVal; + return; + } + } + + resolved_ = (routeAttr.value.oid != SAI_NULL_OBJECT_ID) ? true : false; +} + +}} // facebook::fboss diff --git a/fboss/agent/hw/sai/SaiRoute.h b/fboss/agent/hw/sai/SaiRoute.h new file mode 100644 index 0000000000000..c545b5a6d2ea3 --- /dev/null +++ b/fboss/agent/hw/sai/SaiRoute.h @@ -0,0 +1,93 @@ +/* + * Copyright (c) 2004-present, Facebook, Inc. + * Copyright (c) 2016, Cavium, Inc. + * All rights reserved. + * + * This source code is licensed under the BSD-style license found in the + * LICENSE file in the root directory of this source tree. An additional grant + * of patent rights can be found in the PATENTS file in the same directory. + * + */ +#pragma once + +#include +#include + +#include "fboss/agent/state/RouteForwardInfo.h" +#include "fboss/agent/types.h" + +extern "C" { +#include "saitypes.h" +#include "sairoute.h" +} + +namespace facebook { namespace fboss { + +class SaiSwitch; + +class SaiRoute { +public: + /** + * Constructs the SaiRoute object. + * + * This method doesn't make any calls to the SAI to program route there. + * program() will be called soon after construction, and any + * actual initialization logic is be performed there. + */ + SaiRoute(const SaiSwitch *hw, + RouterID vrf, + const folly::IPAddress &addr, + uint8_t prefixLen); + /** + * Destructs the SaiRoute object. + * + * This method removes the route from the HW and unreferences vrf and + * next hops used. + */ + virtual ~SaiRoute(); + /** + * Programs route in HW and stores sai_unicast_route_entry_t + * which holds SAI route data needed. + * + * If one of SAI HW calls fails it throws an exception; + * + * @return none + */ + void program(const RouteForwardInfo &fwd); + + /** + * Checks whether the Route is resolved. + * + * @return 'true' if Route is resolved otherwise 'false' + */ + bool isResolved() const { + return resolved_; + } + + /** + * Adds Next Hop to Route on HW in case the unresolved + * Next Hop with "ip" is resolved. + * + * @return none + */ + void onResolved(InterfaceID intf, const folly::IPAddress &ip); + +private: + SaiRoute(const SaiRoute &rhs); + SaiRoute &operator=(const SaiRoute &rhs); + + const SaiSwitch *hw_; + RouterID vrf_; + folly::IPAddress ipAddr_; + uint8_t prefixLen_; + RouteForwardInfo fwd_; + bool resolved_ {false}; + + sai_unicast_route_entry_t routeEntry_; + bool added_ {false}; + bool isLocal_ {false}; + + sai_route_api_t *saiRouteApi_ { nullptr }; +}; + +}} // facebook::fboss diff --git a/fboss/agent/hw/sai/SaiRouteTable.cpp b/fboss/agent/hw/sai/SaiRouteTable.cpp new file mode 100644 index 0000000000000..cc11c13f27369 --- /dev/null +++ b/fboss/agent/hw/sai/SaiRouteTable.cpp @@ -0,0 +1,150 @@ +/* + * Copyright (c) 2004-present, Facebook, Inc. + * Copyright (c) 2016, Cavium, Inc. + * All rights reserved. + * + * This source code is licensed under the BSD-style license found in the + * LICENSE file in the root directory of this source tree. An additional grant + * of patent rights can be found in the PATENTS file in the same directory. + * + */ +#include "fboss/agent/state/Route.h" +#include "SaiRouteTable.h" +#include "SaiSwitch.h" +#include "SaiError.h" +#include "SaiRoute.h" + +namespace facebook { namespace fboss { + +bool SaiRouteTable::Key::operator<(const Key &k2) const { + VLOG(4) << "Entering " << __FUNCTION__; + + if (vrf < k2.vrf) { + return true; + } else if (vrf > k2.vrf) { + return false; + } + + if (mask < k2.mask) { + return true; + } else if (mask > k2.mask) { + return false; + } + + return network < k2.network; +} + +SaiRouteTable::SaiRouteTable(const SaiSwitch *hw) + : hw_(hw) { + VLOG(4) << "Entering " << __FUNCTION__; +} + +SaiRouteTable::~SaiRouteTable() { + VLOG(4) << "Entering " << __FUNCTION__; +} + +SaiRoute *SaiRouteTable::getRouteIf( + RouterID vrf, + const folly::IPAddress &network, + uint8_t mask) const { + VLOG(4) << "Entering " << __FUNCTION__; + + Key key {network, mask, vrf}; + auto iter = fib_.find(key); + + if (iter == fib_.end()) { + return nullptr; + } + + return iter->second.get(); +} + +SaiRoute *SaiRouteTable::getRoute( + RouterID vrf, + const folly::IPAddress &network, + uint8_t mask) const { + VLOG(4) << "Entering " << __FUNCTION__; + + auto rt = getRouteIf(vrf, network, mask); + if (!rt) { + throw SaiError("Cannot find route for ", + network, "/", mask, ", vrf ", vrf.t); + } + + return rt; +} + +template +void SaiRouteTable::addRoute(RouterID vrf, const RouteT *route) { + VLOG(4) << "Entering " << __FUNCTION__; + + const auto &prefix = route->prefix(); + Key key {folly::IPAddress(prefix.network), prefix.mask, vrf}; + auto ret = fib_.emplace(key, nullptr); + + if (ret.second) { + SCOPE_FAIL { + fib_.erase(ret.first); + }; + ret.first->second.reset(new SaiRoute(hw_, vrf, + folly::IPAddress(prefix.network), + prefix.mask)); + } + + auto pRoute = ret.first->second.get(); + pRoute->program(route->getForwardInfo()); + + if (!pRoute->isResolved()) { + unresolvedRoutes_.insert(ret.first->second.get()); + } +} + +template +void SaiRouteTable::deleteRoute(RouterID vrf, const RouteT *route) { + VLOG(4) << "Entering " << __FUNCTION__; + + const auto &prefix = route->prefix(); + Key key {folly::IPAddress(prefix.network), prefix.mask, vrf}; + auto iter = fib_.find(key); + + if (iter == fib_.end()) { + throw SaiError("Failed to delete a non-existing route ", route->str()); + } + + auto unresIter = unresolvedRoutes_.find(iter->second.get()); + if (unresIter != unresolvedRoutes_.end()) { + unresolvedRoutes_.erase(unresIter); + } + + fib_.erase(iter); +} + +void SaiRouteTable::onResolved(InterfaceID intf, const folly::IPAddress &ip) { + + VLOG(4) << "Entering " << __FUNCTION__; + + auto iter = unresolvedRoutes_.begin(); + + while (iter != unresolvedRoutes_.end()) { + try { + (*iter)->onResolved(intf, ip); + + if ((*iter)->isResolved()) { + iter = unresolvedRoutes_.erase(iter); + continue; + } + + } catch (const SaiError &e) { + LOG(ERROR) << e.what(); + } + + ++iter; + } +} + +template void SaiRouteTable::addRoute(RouterID, const RouteV4 *); +template void SaiRouteTable::addRoute(RouterID, const RouteV6 *); +template void SaiRouteTable::deleteRoute(RouterID, const RouteV4 *); +template void SaiRouteTable::deleteRoute(RouterID, const RouteV6 *); + +}} // facebook::fboss diff --git a/fboss/agent/hw/sai/SaiRouteTable.h b/fboss/agent/hw/sai/SaiRouteTable.h new file mode 100644 index 0000000000000..9f2dc9aa9d719 --- /dev/null +++ b/fboss/agent/hw/sai/SaiRouteTable.h @@ -0,0 +1,65 @@ +/* + * Copyright (c) 2004-present, Facebook, Inc. + * Copyright (c) 2016, Cavium, Inc. + * All rights reserved. + * + * This source code is licensed under the BSD-style license found in the + * LICENSE file in the root directory of this source tree. An additional grant + * of patent rights can be found in the PATENTS file in the same directory. + * + */ +#pragma once + +#include +#include + +#include "fboss/agent/types.h" + + +namespace facebook { namespace fboss { + +class SaiSwitch; +class SaiRoute; + +class SaiRouteTable { +public: + explicit SaiRouteTable(const SaiSwitch *hw); + ~SaiRouteTable(); + // throw an error if not found + SaiRoute *getRoute( + RouterID vrf, const folly::IPAddress &prefix, uint8_t len) const; + // return nullptr if not found + SaiRoute *getRouteIf( + RouterID vrf, const folly::IPAddress &prefix, uint8_t len) const; + + /* + * The following functions will modify the object. They rely on the global + * HW update lock for the protection. + */ + template + void addRoute(RouterID vrf, const RouteT *route); + template + void deleteRoute(RouterID vrf, const RouteT *route); + + /** + * Loops trough all the unresolved Routes and resolves those which have + * Next Hops with 'intf' and 'ip' + * + * @return none + */ + void onResolved(InterfaceID intf, const folly::IPAddress &ip); + + +private: + struct Key { + folly::IPAddress network; + uint8_t mask; + RouterID vrf; + bool operator<(const Key &k2) const; + }; + const SaiSwitch *hw_; + boost::container::flat_map> fib_; + boost::container::flat_set unresolvedRoutes_; +}; + +}} // facebook::fboss diff --git a/fboss/agent/hw/sai/SaiRxPacket.cpp b/fboss/agent/hw/sai/SaiRxPacket.cpp new file mode 100644 index 0000000000000..a51e9cbbf1ba3 --- /dev/null +++ b/fboss/agent/hw/sai/SaiRxPacket.cpp @@ -0,0 +1,84 @@ +/* + * Copyright (c) 2004-present, Facebook, Inc. + * Copyright (c) 2016, Cavium, Inc. + * All rights reserved. + * + * This source code is licensed under the BSD-style license found in the + * LICENSE file in the root directory of this source tree. An additional grant + * of patent rights can be found in the PATENTS file in the same directory. + * + */ +#include +#include + +#include "SaiRxPacket.h" +#include "SaiSwitch.h" +#include "SaiPortTable.h" +#include "SaiError.h" +#include "SaiPortBase.h" + +extern "C" { +#include "saihostintf.h" +} + +using folly::IOBuf; + +namespace facebook { namespace fboss { + +SaiRxPacket::SaiRxPacket(const void* buf, + sai_size_t buf_size, + uint32_t attr_count, + const sai_attribute_t *attr_list, + const SaiSwitch *hw) { + + if (!buf || (buf_size == 0) || (attr_count == 0) || + !attr_list || !hw) { + + throw SaiError("Invalid input data."); + } + + const uint8_t PKT_MIN_LEN = 64; + + for (uint32_t i = 0; i < attr_count; i++) { + + if (attr_list[i].id == SAI_HOSTIF_PACKET_INGRESS_PORT) { + srcPort_ = hw->getPortTable()->getPortId(attr_list[i].value.oid); + } + } + + // Append ingress packet to 64 bytes. + len_ = (buf_size < PKT_MIN_LEN) ? PKT_MIN_LEN : buf_size; + + uint8_t *pkt_buf = new uint8_t[len_]; + memset(pkt_buf, 0, len_); + memcpy(pkt_buf, buf, buf_size); + + buf_ = IOBuf::takeOwnership(pkt_buf, // void* buf + len_, // uint32_t capacity + freeRxBufCallback, // Free Function freeFn + NULL); // void* userData + + folly::io::Cursor c(buf_.get()); + + c += folly::MacAddress::SIZE * 2; // skip dst and src MACs + + auto ethertype = c.readBE(); + if (ethertype == 0x8100) { + // Packet is tagged with a VLAN so take the VLAN from there. + srcVlan_ = VlanID(c.readBE()); + } else { + // In case of untagged packet we just take the ingress VLAN of the source port. + srcVlan_ = hw->getPortTable()->getSaiPort(srcPort_)->getIngressVlan(); + } +} + +SaiRxPacket::~SaiRxPacket() { + // Nothing to do. The IOBuf destructor will call freeRxBuf() + // to free the packet data +} + +void SaiRxPacket::freeRxBufCallback(void *ptr, void* arg) { + delete[] static_cast(ptr); +} + +}} // facebook::fboss diff --git a/fboss/agent/hw/sai/SaiRxPacket.h b/fboss/agent/hw/sai/SaiRxPacket.h new file mode 100644 index 0000000000000..12b436c2bb427 --- /dev/null +++ b/fboss/agent/hw/sai/SaiRxPacket.h @@ -0,0 +1,43 @@ +/* + * Copyright (c) 2004-present, Facebook, Inc. + * Copyright (c) 2016, Cavium, Inc. + * All rights reserved. + * + * This source code is licensed under the BSD-style license found in the + * LICENSE file in the root directory of this source tree. An additional grant + * of patent rights can be found in the PATENTS file in the same directory. + * + */ +#pragma once + +#include "fboss/agent/RxPacket.h" + +extern "C" { +#include "saitypes.h" +} + +namespace facebook { namespace fboss { + +class SaiSwitch; + +class SaiRxPacket : public RxPacket { +public: + /* + * Creates a SaiRxPacket from a sai buffer received by an rx callback. + */ + explicit SaiRxPacket(const void* buf, + sai_size_t buf_size, + uint32_t attr_count, + const sai_attribute_t *attr_list, + const SaiSwitch *hw); + + virtual ~SaiRxPacket(); + +private: + /* + * IOBuf callback which releases memory allocated for packet data. + */ + static void freeRxBufCallback(void *ptr, void* arg); +}; + +}} // facebook::fboss diff --git a/fboss/agent/hw/sai/SaiStation.cpp b/fboss/agent/hw/sai/SaiStation.cpp new file mode 100644 index 0000000000000..01d85cccdff10 --- /dev/null +++ b/fboss/agent/hw/sai/SaiStation.cpp @@ -0,0 +1,62 @@ +/* + * Copyright (c) 2004-present, Facebook, Inc. + * Copyright (c) 2016, Cavium, Inc. + * All rights reserved. + * + * This source code is licensed under the BSD-style license found in the + * LICENSE file in the root directory of this source tree. An additional grant + * of patent rights can be found in the PATENTS file in the same directory. + * + */ +#include "SaiStation.h" +#include "SaiSwitch.h" + +extern "C" { +#include "saiacl.h" +} + +namespace facebook { namespace fboss { + +SaiStation::SaiStation(const SaiSwitch *hw) + : hw_(hw) { + VLOG(4) << "Entering " << __FUNCTION__; +} + +SaiStation::~SaiStation() { + VLOG(4) << "Entering " << __FUNCTION__; + + if(id_ == INVALID) { + return; + } + + hw_->getSaiAclApi()->delete_acl_table(id_); + + VLOG(3) << "deleted SAI station entry " << id_; +} + +void SaiStation::program(folly::MacAddress mac, sai_object_id_t aclTableId) { + VLOG(4) << "Entering " << __FUNCTION__; + + CHECK_EQ(INVALID, id_); + + sai_object_id_t tableId = aclTableId; + sai_attribute_t attrList[3]; + + attrList[0].id = SAI_ACL_TABLE_ATTR_STAGE; + attrList[0].value.u32 = 0; + + attrList[1].id = SAI_ACL_TABLE_ATTR_FIELD_DST_MAC; + memcpy(&attrList[1].value.mac, mac.bytes(), sizeof(sai_mac_t)); + + attrList[2].id = SAI_ACL_TABLE_ATTR_FIELD_IP_TYPE; + attrList[2].value.u32 = SAI_ACL_IP_TYPE_ANY; + + hw_->getSaiAclApi()->create_acl_table(&tableId, 3, attrList); + id_ = aclTableId; + + VLOG (1) << "Adding SAI station with Mac : " << mac <<" and " << aclTableId; + + CHECK_NE(id_, INVALID); +} + +}} // facebook::fboss diff --git a/fboss/agent/hw/sai/SaiStation.h b/fboss/agent/hw/sai/SaiStation.h new file mode 100644 index 0000000000000..29cf978d73e5e --- /dev/null +++ b/fboss/agent/hw/sai/SaiStation.h @@ -0,0 +1,46 @@ +/* + * Copyright (c) 2004-present, Facebook, Inc. + * Copyright (c) 2016, Cavium, Inc. + * All rights reserved. + * + * This source code is licensed under the BSD-style license found in the + * LICENSE file in the root directory of this source tree. An additional grant + * of patent rights can be found in the PATENTS file in the same directory. + * + */ +#pragma once + +#include + +#include "fboss/agent/types.h" + +extern "C" { +#include "saitypes.h" +} + +namespace facebook { namespace fboss { + +class SaiSwitch; + +class SaiStation { +public: + // L2 station can be used to filter based on MAC, VLAN, or Port. + // We only use it to filter based on the MAC to enable the L3 processing. + explicit SaiStation(const SaiSwitch *hw); + ~SaiStation(); + + void program(folly::MacAddress mac, sai_object_id_t aclTableId); + +private: + // no copy or assignment + SaiStation(SaiStation const &) = delete; + SaiStation &operator=(SaiStation const &) = delete; + + enum : int { + INVALID = -1, + }; + const SaiSwitch *hw_; + int id_ {INVALID}; +}; + +}} // facebook::fboss diff --git a/fboss/agent/hw/sai/SaiSwitch.cpp b/fboss/agent/hw/sai/SaiSwitch.cpp new file mode 100644 index 0000000000000..bc13c98f771e4 --- /dev/null +++ b/fboss/agent/hw/sai/SaiSwitch.cpp @@ -0,0 +1,1111 @@ +/* + * Copyright (c) 2004-present, Facebook, Inc. + * Copyright (c) 2016, Cavium, Inc. + * All rights reserved. + * + * This source code is licensed under the BSD-style license found in the + * LICENSE file in the root directory of this source tree. An additional grant + * of patent rights can be found in the PATENTS file in the same directory. + * + */ +#include +#include +#include +#include +#include + +#include +#include +#include // TODO: Remove this when AddHostsForDemo are removed + +#include "SaiSwitch.h" +#include "SaiVrfTable.h" +#include "SaiNextHopTable.h" +#include "SaiHostTable.h" +#include "SaiHost.h" +#include "SaiRxPacket.h" +#include "SaiTxPacket.h" +#include "SaiPortTable.h" +#include "SaiPortBase.h" +#include "SaiIntfTable.h" +#include "SaiRouteTable.h" +#include "SaiWarmBootCache.h" +#include "SaiError.h" +#include "SaiIntf.h" + +#include "fboss/agent/packet/ArpHdr.h" // TODO: Remove this when AddHostsForDemo are removed +#include "fboss/agent/ArpHandler.h" // TODO: Remove this when AddHostsForDemo are removed +#include "fboss/agent/state/ArpEntry.h" +#include "fboss/agent/state/DeltaFunctions.h" +#include "fboss/agent/state/Interface.h" +#include "fboss/agent/state/InterfaceMap.h" +#include "fboss/agent/state/NodeMapDelta.h" +#include "fboss/agent/state/Port.h" +#include "fboss/agent/state/PortMap.h" +#include "fboss/agent/state/StateDelta.h" +#include "fboss/agent/state/SwitchState.h" +#include "fboss/agent/state/Vlan.h" +#include "fboss/agent/state/VlanMap.h" +#include "fboss/agent/state/VlanMapDelta.h" +#include "fboss/agent/state/Route.h" +#include "fboss/agent/state/RouteTableRib.h" +#include "fboss/agent/state/RouteTable.h" +#include "fboss/agent/state/RouteTableMap.h" +#include "fboss/agent/state/RouteDelta.h" + +#include "fboss/agent/SwitchStats.h" + +using std::make_shared; +using std::shared_ptr; +using std::unique_ptr; +using std::string; + +using folly::make_unique; +using folly::IPAddress; + +using facebook::fboss::DeltaFunctions::forEachChanged; +using facebook::fboss::DeltaFunctions::forEachAdded; +using facebook::fboss::DeltaFunctions::forEachRemoved; + + +//extern "C" { +//// Forward declaration for sai_service_method_table_initialize function which is not +//// currently added to some SAI header like saiplatform.h(will propagate this to SAI community) +//// so has to be defined here. A customer's SAI adapter must provide its implementation. +//sai_status_t sai_service_method_table_initialize(service_method_table_t* services); +//} + +namespace facebook { namespace fboss { + +// Default service method table getters +static const char* saiProfileValueGet(sai_switch_profile_id_t profile_id, + const char* var_name) +{ + return NULL; +} + +static int saiProfileValueGetNext(sai_switch_profile_id_t profile_id, + const char** variable, + const char** value) +{ + return -1; +} + +// TODO: remove this when SAI callbacks support pointer to user data storage. +SaiSwitch* SaiSwitch::instance_ = nullptr; + +facebook::fboss::cfg::PortSpeed getPortSpeed(int port) { + VLOG(4) << "Entering " << __FUNCTION__; + + return facebook::fboss::cfg::PortSpeed::XG; +} + +SaiSwitch::SaiSwitch(SaiPlatformBase *platform) + : platform_(platform) + , portTable_(new SaiPortTable(this)) + , intfTable_(new SaiIntfTable(this)) + , hostTable_(new SaiHostTable(this)) + , routeTable_(new SaiRouteTable(this)) + , vrfTable_(new SaiVrfTable(this)) + , nextHopTable_(new SaiNextHopTable(this)) + , warmBootCache_(new SaiWarmBootCache(this)) + , lockFd(-1) { + VLOG(4) << "Entering " << __FUNCTION__; + + service_.profile_get_value = saiProfileValueGet; + service_.profile_get_next_value = saiProfileValueGetNext; + + //sai_status_t status = sai_service_method_table_initialize(&service_); + //if (status != SAI_STATUS_SUCCESS) { + // throw SaiFatal("Failed to initialize service method table. Error: ", status); + //} + + sai_api_initialize(0, &service_); + + sai_api_query(SAI_API_SWITCH, (void **) &saiSwitchApi_); + sai_api_query(SAI_API_VLAN, (void **) &saiVlanApi_); + //sai_api_query(SAI_API_PORT, (void **) &saiPortApi_); + sai_api_query(SAI_API_ROUTER_INTERFACE, (void **) &saiRouterIntfApi_); + sai_api_query(SAI_API_ROUTE, (void **) &saiRouteApi_); + //sai_api_query(SAI_API_ACL, (void **) &saiAclApi_); + sai_api_query(SAI_API_VIRTUAL_ROUTER, (void **) &saiVrfApi_); + sai_api_query(SAI_API_NEIGHBOR, (void **) &saiNeighborApi_); + sai_api_query(SAI_API_NEXT_HOP, (void **) &saiNextHopApi_); + sai_api_query(SAI_API_NEXT_HOP_GROUP, (void **) &saiNextHopGroupApi_); + sai_api_query(SAI_API_HOST_INTERFACE, (void **) &saiHostIntfApi_); + + // TODO: remove this when SAI callbacks support pointer to user data storage. + SaiSwitch::instance_ = this; +} + +SaiSwitch::~SaiSwitch() { + VLOG(4) << "Entering " << __FUNCTION__; + + saiNextHopApi_ = nullptr; + saiAclApi_ = nullptr; + saiHostInterfaceApi_ = nullptr; + saiNeighborApi_ = nullptr; + saiRouterIntfApi_ = nullptr; + saiRouteApi_ = nullptr; + saiVrfApi_ = nullptr; + saiPortApi_ = nullptr; + saiVlanApi_ = nullptr; + saiSwitchApi_ = nullptr; + saiHostIntfApi_ = nullptr; + + sai_api_uninitialize(); + + service_ = {nullptr, nullptr}; + + releaseLock(); +} + +std::shared_ptr SaiSwitch::getColdBootSwitchState() const { + auto bootState = make_shared(); + + // On cold boot all ports are in Vlan 1 + auto vlanMap = make_shared(); + auto vlan = make_shared(VlanID(1), "initVlan"); + Vlan::MemberPorts memberPorts; + + // Cold boot - put all ports in Vlan 1 + for(uint32_t nPort = 0; nPort < saiPortList_.count; ++nPort) { + sai_object_id_t saiPort = saiPortList_.list[nPort]; + PortID portID = portTable_->getPortId(saiPort); + + string name = folly::to("port", portID); + bootState->registerPort(portID, name); + memberPorts.insert(std::make_pair(PortID(saiPort), false)); + } + + vlan->setPorts(memberPorts); + bootState->addVlan(vlan); + return bootState; +} + +std::shared_ptr SaiSwitch::getWarmBootSwitchState() const { + auto warmBootState = getColdBootSwitchState(); + for (auto port : *warmBootState->getPorts()) { + int portEnabled; + + port->setState(portEnabled == 1 ? cfg::PortState::UP: cfg::PortState::DOWN); + port->setSpeed(getPortSpeed(port->getID())); + } + warmBootState->resetIntfs(warmBootCache_->reconstructInterfaceMap()); + warmBootState->resetVlans(warmBootCache_->reconstructVlanMap()); + return warmBootState; +} + +std::pair, BootType> +SaiSwitch::init(HwSwitch::Callback *callback) { + VLOG(4) << "Entering " << __FUNCTION__; + + auto state = make_shared(); + auto bootType = BootType::COLD_BOOT; + + CHECK(bootType != BootType::UNINITIALIZED); + auto warmBoot = bootType == BootType::WARM_BOOT; + + char devId = 0; + callback_ = callback; + + sai_switch_profile_id_t profileId = SAI_SWITCH_DEFAULT_PROFILE_ID; + + // environment variable exists ? + if (NULL != std::getenv("SAI_SWITCH_PROFILE_ID")) + { + // get profile ID from variable value + profileId = atoi(std::getenv("SAI_SWITCH_PROFILE_ID")); + } + + // environment variable exists ? + if (NULL != std::getenv("SAI_SWITCH_HARDWARE_ID")) + { + // get switch hardware ID from variable value + hwId_ = std::string(std::getenv("SAI_SWITCH_HARDWARE_ID")); + } + + sai_switch_notification_t swNotif = {NULL, NULL, NULL, NULL, NULL, NULL}; + swNotif.on_packet_event = packetRxCallback; + + sai_status_t saiStatus = saiSwitchApi_->initialize_switch(profileId, + const_cast(hwId_.c_str()), + NULL, &swNotif); + if(SAI_STATUS_SUCCESS != saiStatus) { + throw SaiFatal("Failed to initialize SAI switch. Error: ", saiStatus); + } + + sai_attribute_t attr; + std::vector hostIfAttrList; + + // Create a host interface + attr.id = SAI_HOSTIF_ATTR_TYPE; + attr.value.s32 = SAI_HOSTIF_TYPE_FD; + hostIfAttrList.push_back(attr); + + saiStatus = saiHostIntfApi_->create_hostif(&hostIfFdId_, + hostIfAttrList.size(), + hostIfAttrList.data()); + if(SAI_STATUS_SUCCESS != saiStatus) { + throw SaiFatal("Failed to initialize SAI host interface. Error: ", saiStatus); + } + + attr.id = SAI_HOSTIF_TRAP_ATTR_PACKET_ACTION; + attr.value.s32 = SAI_PACKET_ACTION_TRAP; + + saiStatus = saiHostIntfApi_->set_trap_attribute(SAI_HOSTIF_TRAP_ID_ARP_REQUEST, &attr); + if(SAI_STATUS_SUCCESS != saiStatus) { + throw SaiFatal("Could not set ARP_REQUEST trap action to LOG. Error: ", saiStatus); + } + + saiStatus = saiHostIntfApi_->set_trap_attribute(SAI_HOSTIF_TRAP_ID_ARP_RESPONSE, &attr); + if(SAI_STATUS_SUCCESS != saiStatus) { + throw SaiFatal("Could not set ARP_RESPONSE trap action to LOG. Error: ", saiStatus); + } + + if(!warmBoot){ + bootType = BootType::COLD_BOOT; + + attr.id = SAI_SWITCH_ATTR_PORT_NUMBER; + saiStatus = getSaiSwitchApi()->get_switch_attribute(1, &attr); + + if(SAI_STATUS_SUCCESS != saiStatus) + throw SaiFatal("Retrieve port number error."); + + saiPortList_.list = new sai_object_id_t[attr.value.u32]; + saiPortList_.count = attr.value.u32; + attr.value.objlist = saiPortList_; + + attr.id = SAI_SWITCH_ATTR_PORT_LIST; + saiStatus = getSaiSwitchApi()->get_switch_attribute(1, &attr); + + if(SAI_STATUS_SUCCESS != saiStatus) + throw SaiFatal("Retrieve port list error."); + + VLOG(2) << "Performing cold boot"; + } + else { + VLOG(2) << "Performing warm boot"; + } + portTable_->initPorts(false); + // Set the spanning tree state of all ports to forwarding. + // TODO: Eventually the spanning tree state should be part of the Port + // state, and this should be handled in applyConfig(). + // + // Spanning tree group settings + // TODO: This should eventually be done as part of applyConfig() + + attr.id = SAI_SWITCH_ATTR_SRC_MAC_ADDRESS; + attr.value.mac[0] = 0; + attr.value.mac[1] = 1; + attr.value.mac[2] = 2; + attr.value.mac[3] = 3; + attr.value.mac[4] = 4; + attr.value.mac[5] = 5; + saiStatus = getSaiSwitchApi()->set_switch_attribute(&attr); + + if(SAI_STATUS_SUCCESS != saiStatus) + throw SaiFatal("Set switch MAC address error."); + + //TODO: Create file to indicate init state + tryGetLock(); + if (warmBoot) { + state = getWarmBootSwitchState(); + stateChanged(StateDelta(make_shared(), state)); + } + else + state = getColdBootSwitchState(); + + return std::make_pair(state, bootType); +} + +void SaiSwitch::unregisterCallbacks() { + VLOG(4) << "Entering " << __FUNCTION__; +} + +void SaiSwitch::ecmpHashSetup() { + VLOG(4) << "Entering " << __FUNCTION__; +} + +void SaiSwitch::stateChanged(const StateDelta &delta) { + VLOG(4) << "Entering " << __FUNCTION__; + + try{ + + // Take the lock before modifying any objects + std::lock_guard g(lock_); + // As the first step, disable ports that are now disabled. + // This ensures that we immediately stop forwarding traffic on these ports. + processDisabledPorts(delta); + + // remove all routes to be deleted + processRemovedRoutes(delta); + + // delete all interface not existing anymore. that should stop + // all traffic on that interface now + forEachRemoved(delta.getIntfsDelta(), &SaiSwitch::processRemovedIntf, this); + + // Add all new VLANs, and modify VLAN port memberships. + // We don't actually delete removed VLANs at this point, we simply remove + // all members from the VLAN. This way any ports that ingres packets to this + // VLAN will still switch use this VLAN until we get the new VLAN fully + // configured. + forEachChanged(delta.getVlansDelta(), + &SaiSwitch::processChangedVlan, + &SaiSwitch::processAddedVlan, + &SaiSwitch::preprocessRemovedVlan, + this); + + // Edit port ingress VLAN and speed settings + forEachChanged(delta.getPortsDelta(), + [&] (const shared_ptr &oldPort, const shared_ptr &newPort) { + if (oldPort->getIngressVlan() != newPort->getIngressVlan()) { + updateIngressVlan(oldPort, newPort); + } + + if (oldPort->getSpeed() != newPort->getSpeed()) { + updatePortSpeed(oldPort, newPort); + } + }); + + // Update changed interfaces + forEachChanged(delta.getIntfsDelta(), &SaiSwitch::processChangedIntf, this); + + // Remove deleted VLANs + forEachRemoved(delta.getVlansDelta(), &SaiSwitch::processRemovedVlan, this); + + // Add all new interfaces + forEachAdded(delta.getIntfsDelta(), &SaiSwitch::processAddedIntf, this); + + // Any ARP changes + processArpChanges(delta); + + // Process any new routes or route changes + processAddedChangedRoutes(delta); + + // As the last step, enable newly enabled ports. + // Doing this as the last step ensures that we only start forwarding traffic + // once the ports are correctly configured. + processEnabledPorts(delta); + + } catch(SaiError &e) { + LOG(ERROR) << e.what(); + } catch(SaiFatal &e) { + LOG(FATAL) << e.what(); + exitFatal(); + } +} + +std::unique_ptr SaiSwitch::allocatePacket(uint32_t size) { + VLOG(5) << "Entering " << __FUNCTION__; + + return make_unique(size); +} + +bool SaiSwitch::sendPacketSwitched(std::unique_ptr pkt) noexcept { + VLOG(5) << "Entering " << __FUNCTION__; + + sai_attribute_t attr; + std::vector attrList; + + attr.id = SAI_HOSTIF_PACKET_TX_TYPE; + attr.value.s32 = SAI_HOSTIF_TX_TYPE_PIPELINE_LOOKUP; + attrList.push_back(attr); + + sai_status_t status = saiHostIntfApi_->send_packet(hostIfFdId_, + pkt->buf()->writableData(), + pkt->buf()->length(), + attrList.size(), + attrList.data()); + if (status != SAI_STATUS_SUCCESS) { + LOG(ERROR) << "Failed to send switched packet. Error: " << status; + return false; + } + + return true; +} + +bool SaiSwitch::sendPacketOutOfPort(std::unique_ptr pkt, PortID portID) noexcept { + + sai_object_id_t portId {SAI_NULL_OBJECT_ID}; + + try { + portId = getPortTable()->getSaiPortId(portID); + } catch (const SaiError &e) { + LOG(ERROR) << "Could not sent packet out of port:" << portID.t + << " Reason: " << e.what(); + return false; + } + + sai_attribute_t attr; + std::vector attrList; + + attr.id = SAI_HOSTIF_PACKET_TX_TYPE; + attr.value.s32 = SAI_HOSTIF_TX_TYPE_PIPELINE_BYPASS; + attrList.push_back(attr); + + attr.id = SAI_HOSTIF_PACKET_EGRESS_PORT_OR_LAG; + attr.value.oid = portId; + attrList.push_back(attr); + + sai_status_t status = saiHostIntfApi_->send_packet(hostIfFdId_, + pkt->buf()->writableData(), + pkt->buf()->length(), + attrList.size(), + attrList.data()); + if (status != SAI_STATUS_SUCCESS) { + LOG(ERROR) << "Failed to send packet out of port: " << portID.t << " Error: " << status; + return false; + } + + return true; +} + +void SaiSwitch::updateIngressVlan(const std::shared_ptr &oldPort, + const std::shared_ptr &newPort) { + VLOG(4) << "Entering " << __FUNCTION__; + + try { + portTable_->getSaiPort(newPort->getID())->setIngressVlan(newPort->getIngressVlan()); + } catch (const SaiError &e) { + LOG(ERROR) << e.what(); + } +} + +void SaiSwitch::updatePortSpeed(const std::shared_ptr &oldPort, + const std::shared_ptr &newPort) { + VLOG(4) << "Entering " << __FUNCTION__; +} + +void SaiSwitch::processDisabledPorts(const StateDelta& delta) { + VLOG(4) << "Entering " << __FUNCTION__; + + forEachChanged(delta.getPortsDelta(), + [&] (const shared_ptr& oldPort, const shared_ptr& newPort) { + if (!oldPort->isDisabled() && newPort->isDisabled()) { + + try { + portTable_->getSaiPort(newPort->getID())->disable(); + } catch (const SaiError &e) { + LOG(ERROR) << e.what(); + } + } + }); +} + +void SaiSwitch::processEnabledPorts(const StateDelta& delta) { + VLOG(4) << "Entering " << __FUNCTION__; + + forEachChanged(delta.getPortsDelta(), + [&] (const shared_ptr& oldPort, const shared_ptr& newPort) { + + if (oldPort->isDisabled() && !newPort->isDisabled()) { + + try { + portTable_->getSaiPort(newPort->getID())->enable(); + } catch (const SaiError &e) { + LOG(ERROR) << e.what(); + } + } + + }); +} + +void SaiSwitch::clearWarmBootCache() { + VLOG(4) << "Entering " << __FUNCTION__; + + warmBootCache_->clear(); +} + +void SaiSwitch::exitFatal() const { + VLOG(4) << "Entering " << __FUNCTION__; + LOG(FATAL) << "Exit fatal"; + // TODO: dump state + + // report + callback_->exitFatal(); +} + +folly::dynamic SaiSwitch::gracefulExit() { + VLOG(4) << "Entering " << __FUNCTION__; + LOG(WARNING) << "Exit graceful"; + folly::dynamic hwSwitch = toFollyDynamic(); + // TODO: Save cache for next warm boot + getSaiSwitchApi()->disconnect_switch(); + return hwSwitch; +} + +folly::dynamic SaiSwitch::toFollyDynamic() const { + VLOG(4) << "Entering " << __FUNCTION__; + folly::dynamic hwSwitch = folly::dynamic::object; + + return hwSwitch; +} + +void SaiSwitch::initialConfigApplied() { + VLOG(4) << "Entering " << __FUNCTION__; + // TODO: +} + +bool SaiSwitch::isPortUp(PortID port) const { + VLOG(4) << "Entering " << __FUNCTION__; + + //TODO: + return true; +} + +void SaiSwitch::updateStats(SwitchStats *switchStats) { + VLOG(4) << "Entering " << __FUNCTION__; + + updateSwitchStats(switchStats); + + // Update thread-local per-port statistics. + PortStatsMap *portStatsMap = switchStats->getPortStats(); + + for (auto& entry : *portStatsMap) { + PortID portId = entry.first; + PortStats *portStats = entry.second.get(); + updatePortStats(portId, portStats); + } + + // Update global statistics. + portTable_->updatePortStats(); +} + +int SaiSwitch::getHighresSamplers( + HighresSamplerList *samplers, + const folly::StringPiece namespaceString, + const std::set &counterSet) { + return 0; +} + +bool SaiSwitch::getAndClearNeighborHit(RouterID vrf, folly::IPAddress& ip) { + VLOG(4) << "Entering " << __FUNCTION__; + return false; +} + +void SaiSwitch::fetchL2Table(std::vector *l2Table) { +} + +cfg::PortSpeed SaiSwitch::getPortSpeed(PortID port) const { + VLOG(4) << "Entering " << __FUNCTION__; + // TODO: Get the actual port speed. + return cfg::PortSpeed(10000); +} + +cfg::PortSpeed SaiSwitch::getMaxPortSpeed(PortID port) const { + VLOG(4) << "Entering " << __FUNCTION__; + // TODO: Get the actual max port speed. + return cfg::PortSpeed(10000); +} + +void SaiSwitch::updateSwitchStats(SwitchStats *switchStats) { + VLOG(4) << "Entering " << __FUNCTION__; + // TODO +} + +void SaiSwitch::updatePortStats(PortID portID, PortStats *portStats) { + VLOG(4) << "Entering " << __FUNCTION__; + // TODO +} + +SaiPlatformBase *SaiSwitch::getPlatform() const { + VLOG(4) << "Entering " << __FUNCTION__; + + return platform_; +} + +template +void SaiSwitch::processNeighborEntryDelta(const DELTA &delta) { + VLOG(4) << "Entering " << __FUNCTION__; + + const auto *oldEntry = delta.getOld().get(); + const auto *newEntry = delta.getNew().get(); + + auto updateCreateNewEntry = [&]() { + sai_packet_action_t action = newEntry->isPending() ? + SAI_PACKET_ACTION_DROP : + SAI_PACKET_ACTION_FORWARD; + + VLOG(3) << "Adding neighbor entry witch action: " << action; + auto host = hostTable_->createOrUpdateSaiHost(newEntry->getIntfID(), + IPAddress(newEntry->getIP()), + newEntry->getMac()); + try { + auto oldAction = host->getSaiAction(); + + host->program(action, newEntry->getMac()); + + if ((oldAction != action) && + (action == SAI_PACKET_ACTION_FORWARD)) { + + // The host has been just resolved so update + // dependant Next Hops and Routes correspondingly + nextHopTable_->onResolved(newEntry->getIntfID(), newEntry->getIP()); + routeTable_->onResolved(newEntry->getIntfID(), newEntry->getIP()); + } + } catch (const SaiError &e) { + hostTable_->removeSaiHost(newEntry->getIntfID(), + IPAddress(newEntry->getIP())); + LOG(ERROR) << e.what(); + return; + } + + }; + + auto removeOldEntry = [&]() { + hostTable_->removeSaiHost(oldEntry->getIntfID(), + IPAddress(oldEntry->getIP())); + }; + + if (!oldEntry) { + updateCreateNewEntry(); + + } else if (!newEntry) { + + removeOldEntry(); + + } else if ((oldEntry->getIntfID() != newEntry->getIntfID()) || + (oldEntry->getIP() != newEntry->getIP())) { + + updateCreateNewEntry(); + removeOldEntry(); + + } else if (oldEntry->getMac() != newEntry->getMac()) { + + updateCreateNewEntry(); + } +} + +void SaiSwitch::processArpChanges(const StateDelta &delta) { + VLOG(4) << "Entering " << __FUNCTION__; + + for (const auto& vlanDelta : delta.getVlansDelta()) { + for (const auto& arpDelta : vlanDelta.getArpDelta()) { + processNeighborEntryDelta(arpDelta); + } + + for (const auto& ndpDelta : vlanDelta.getNdpDelta()) { + processNeighborEntryDelta(ndpDelta); + } + } +} + +template +void SaiSwitch::processChangedRoute(const RouterID id, + const shared_ptr &oldRoute, + const shared_ptr &newRoute) { + VLOG(4) << "Entering " << __FUNCTION__; + + VLOG(2) << "Changing route entry vrf: " << (int)id << ", from " + << oldRoute->str() << " to " << newRoute->str(); + + // if the new route is not resolved, delete it instead of changing it + if (!newRoute->isResolved()) { + VLOG(2) << "Non-resolved route HW programming is skipped"; + processRemovedRoute(id, oldRoute); + } else { + try { + routeTable_->addRoute(id, newRoute.get()); + } catch (const SaiError &e) { + LOG(ERROR) << e.what(); + } + } +} + +template +void SaiSwitch::processAddedRoute(const RouterID id, + const shared_ptr &route) { + VLOG(4) << "Entering " << __FUNCTION__; + + VLOG(2) << "Adding route entry vrf: " << (int)id << ", " << route->str(); + + // if the new route is not resolved, ignore it + if (!route->isResolved()) { + VLOG(2) << "Non-resolved route HW programming is skipped"; + return; + } + + try { + routeTable_->addRoute(id, route.get()); + } catch (const SaiError &e) { + LOG(ERROR) << e.what(); + } +} + +template +void SaiSwitch::processRemovedRoute(const RouterID id, + const shared_ptr &route) { + VLOG(4) << "Entering " << __FUNCTION__; + + VLOG(3) << "removing route entry @ vrf " << id << " " << route->str(); + + if (!route->isResolved()) { + VLOG(1) << "Non-resolved route HW programming is skipped"; + return; + } + + try { + routeTable_->deleteRoute(id, route.get()); + } catch (const SaiError &e) { + LOG(ERROR) << e.what(); + } +} + +void SaiSwitch::processRemovedRoutes(const StateDelta &delta) { + VLOG(4) << "Entering " << __FUNCTION__; + + for (auto const& rtDelta : delta.getRouteTablesDelta()) { + if (!rtDelta.getOld()) { + // no old route table, must not removed route, skip + continue; + } + + RouterID id = rtDelta.getOld()->getID(); + forEachRemoved( + rtDelta.getRoutesV4Delta(), + &SaiSwitch::processRemovedRoute, + this, + id); + forEachRemoved( + rtDelta.getRoutesV6Delta(), + &SaiSwitch::processRemovedRoute, + this, + id); + } +} + +void SaiSwitch::processAddedChangedRoutes(const StateDelta &delta) { + VLOG(4) << "Entering " << __FUNCTION__; + + for (auto const& rtDelta : delta.getRouteTablesDelta()) { + if (!rtDelta.getNew()) { + // no new route table, must not added or changed route, skip + continue; + } + + RouterID id = rtDelta.getNew()->getID(); + + forEachChanged( + rtDelta.getRoutesV4Delta(), + &SaiSwitch::processChangedRoute, + &SaiSwitch::processAddedRoute, + [&](SaiSwitch *, RouterID, const shared_ptr &) {}, + this, + id); + + forEachChanged( + rtDelta.getRoutesV6Delta(), + &SaiSwitch::processChangedRoute, + &SaiSwitch::processAddedRoute, + [&](SaiSwitch *, RouterID, const shared_ptr &) {}, + this, + id); + } +} + +void SaiSwitch::processChangedVlan(const shared_ptr &oldVlan, + const shared_ptr &newVlan) { + VLOG(4) << "Entering " << __FUNCTION__; + + sai_status_t saiStatus = SAI_STATUS_SUCCESS; + sai_vlan_id_t vlanId = newVlan->getID(); + std::vector addedPorts; + std::vector removedPorts; + const auto &oldPorts = oldVlan->getPorts(); + const auto &newPorts = newVlan->getPorts(); + + // Populate the list of removed ports + for (auto& oldPortPair : oldPorts) { + if (newPorts.find(oldPortPair.first) == newPorts.end()) { + + sai_vlan_port_t vlanPort; + + try { + vlanPort.port_id = portTable_->getSaiPortId(oldPortPair.first); + } catch (const SaiError &e) { + LOG(ERROR) << e.what(); + continue; + } + + vlanPort.tagging_mode = oldPortPair.second.tagged ? SAI_VLAN_PORT_TAGGED : + SAI_VLAN_PORT_UNTAGGED; + removedPorts.push_back(vlanPort); + } + } + + // Populate the list of added ports + for (auto& newPortPair : newPorts) { + if (oldPorts.find(newPortPair.first) == oldPorts.end()) { + + sai_vlan_port_t vlanPort; + + try { + vlanPort.port_id = portTable_->getSaiPortId(newPortPair.first); + } catch (const SaiError &e) { + LOG(ERROR) << e.what(); + continue; + } + + vlanPort.tagging_mode = newPortPair.second.tagged ? SAI_VLAN_PORT_TAGGED : + SAI_VLAN_PORT_UNTAGGED; + addedPorts.push_back(vlanPort); + } + } + + if (removedPorts.size() > 0) { + + VLOG(2) << "updating VLAN " << newVlan->getID() << ": " << removedPorts.size() << " ports removed"; + + saiStatus = saiVlanApi_->remove_ports_from_vlan(vlanId, removedPorts.size(), + removedPorts.data()); + if (saiStatus != SAI_STATUS_SUCCESS) { + LOG(ERROR) << "Failed to remove ports from VLAN " << vlanId; + } + } + + if (addedPorts.size() > 0) { + + VLOG(2) << "updating VLAN " << newVlan->getID() << ": " << addedPorts.size() << " ports added"; + + saiStatus = saiVlanApi_->add_ports_to_vlan(vlanId, addedPorts.size(), + addedPorts.data()); + if (saiStatus != SAI_STATUS_SUCCESS) { + LOG(ERROR) << "Failed to add ports to VLAN " << vlanId; + } + } +} + +void SaiSwitch::processAddedVlan(const shared_ptr &vlan) { + VLOG(4) << "Entering " << __FUNCTION__; + + VLOG(3) << "Creating VLAN " << (uint16_t)vlan->getID() << " with " + << vlan->getPorts().size() << " ports."; + + sai_status_t saiStatus; + sai_vlan_id_t vlanId = vlan->getID(); + std::vector portList; + + for (const auto& entry : vlan->getPorts()) { + sai_vlan_port_t vlanPort; + + try { + vlanPort.port_id = portTable_->getSaiPortId(entry.first); + } catch (const SaiError &e) { + LOG(ERROR) << e.what(); + continue; + } + + vlanPort.tagging_mode = entry.second.tagged ? SAI_VLAN_PORT_TAGGED : + SAI_VLAN_PORT_UNTAGGED; + portList.push_back(vlanPort); + } + + typedef SaiWarmBootCache::VlanInfo VlanInfo; + // Since during warm boot all VLAN in the config will show + // up as added VLANs we only need to consult the warm boot + // cache here. + auto vlanItr = warmBootCache_->findVlanInfo(vlan->getID()); + if (vlanItr != warmBootCache_->vlan2VlanInfo_end()) { + + // Compare with existing vlan to determine if we need to reprogram + auto is_equal = [](VlanInfo newVlan, VlanInfo existingVlan) { + + auto newVPorts = newVlan.ports_; + auto existingVPorts = existingVlan.ports_; + + if((newVlan.vlan_ != existingVlan.vlan_) || + (newVPorts.size() != existingVPorts.size())) { + + return false; + } + + for(uint32_t i = 0; i < newVPorts.size(); ++i) { + + if((newVPorts[i].port_id != existingVPorts[i].port_id) || + (newVPorts[i].tagging_mode != existingVPorts[i].tagging_mode)) { + + return false; + } + } + + return true; + }; + + const auto& existingVlan = vlanItr->second; + + if (!is_equal(VlanInfo(vlan->getID(), portList), existingVlan)) { + + VLOG(3) << "Updating VLAN " << (uint16_t)vlan->getID() << " with " + << vlan->getPorts().size() << " ports."; + + auto oldVlan = vlan->clone(); + warmBootCache_->fillVlanPortInfo(oldVlan.get()); + processChangedVlan(oldVlan, vlan); + } else { + LOG(WARNING) << "Vlan " << vlan->getID() << " already exists."; + } + + warmBootCache_->programmed(vlanItr); + + } else { + VLOG(3) << "Creating VLAN " << (uint16_t)vlan->getID() << " with " + << vlan->getPorts().size() << " ports."; + + saiStatus = saiVlanApi_->create_vlan(vlanId); + if (saiStatus != SAI_STATUS_SUCCESS) { + LOG(ERROR) << "Failed to create VLAN " << vlanId; + return; + } + + saiStatus = saiVlanApi_->add_ports_to_vlan(vlanId, portList.size(), portList.data()); + if (saiStatus != SAI_STATUS_SUCCESS) { + LOG(ERROR) << "Failed to add ports to VLAN " << vlanId; + return; + } + + warmBootCache_->addVlanInfo(vlan->getID(), portList); + } +} + +void SaiSwitch::preprocessRemovedVlan(const shared_ptr &vlan) { + VLOG(4) << "Entering " << __FUNCTION__; + + sai_status_t saiStatus = SAI_STATUS_SUCCESS; + sai_vlan_id_t vlanId = vlan->getID(); + std::vector portList; + + for (const auto& entry : vlan->getPorts()) { + sai_vlan_port_t vlanPort; + + try { + vlanPort.port_id = portTable_->getSaiPortId(entry.first); + } catch (const SaiError &e) { + LOG(ERROR) << e.what(); + continue; + } + + vlanPort.tagging_mode = entry.second.tagged ? SAI_VLAN_PORT_TAGGED : + SAI_VLAN_PORT_UNTAGGED; + portList.push_back(vlanPort); + } + + saiStatus = saiVlanApi_->remove_ports_from_vlan(vlanId, portList.size(), portList.data()); + if (saiStatus != SAI_STATUS_SUCCESS) { + LOG(ERROR) << "Failed to remove VLAN " << vlanId; + } + + saiStatus = saiVlanApi_->remove_vlan(vlanId); + if (saiStatus != SAI_STATUS_SUCCESS) { + LOG(ERROR) << "Failed to remove VLAN " << vlanId; + } +} + +void SaiSwitch::processRemovedVlan(const shared_ptr &vlan) { + VLOG(4) << "Entering " << __FUNCTION__; + + sai_status_t saiStatus = SAI_STATUS_SUCCESS; + + VLOG(2) << "removing VLAN " << vlan->getID(); + + //saiStatus = saiVlanApi_->remove_vlan(vlan->getID()); + + if (saiStatus != SAI_STATUS_SUCCESS) + throw SaiError("Failed to remove VLAN ", vlan->getID()); +} + +void SaiSwitch::processChangedIntf(const shared_ptr &oldIntf, + const shared_ptr &newIntf) { + VLOG(4) << "Entering " << __FUNCTION__; + + CHECK_EQ(oldIntf->getID(), newIntf->getID()); + VLOG(2) << "changing interface " << oldIntf->getID(); + intfTable_->programIntf(newIntf); +} + +void SaiSwitch::processAddedIntf(const shared_ptr &intf) { + VLOG(4) << "Entering " << __FUNCTION__; + + VLOG(2) << "adding interface " << intf->getID(); + try { + intfTable_->addIntf(intf); + } catch (const SaiError &e) { + LOG(ERROR) << e.what(); + return; + } +} + +void SaiSwitch::processRemovedIntf(const shared_ptr &intf) { + VLOG(4) << "Entering " << __FUNCTION__; + + VLOG(2) << "deleting interface " << intf->getID(); + intfTable_->deleteIntf(intf); +} + +#define lockPath "sai_agent.lock" +int SaiSwitch::tryGetLock(){ + VLOG(4) << "Entering " << __FUNCTION__; + + if(lockFd != -1) + return lockFd; + + mode_t mode = umask(0); + lockFd = open(lockPath, O_RDWR|O_CREAT, 0666 ); + umask(mode); + + if(lockFd >= 0 && flock(lockFd, LOCK_EX | LOCK_NB ) < 0) + { + close( lockFd ); + lockFd = -1; + } + + return lockFd; +} + +void SaiSwitch::packetRxCallback(const void *buf, + sai_size_t buf_size, + uint32_t attr_count, + const sai_attribute_t *attr_list) { + + // TODO: Get SaiSwitch from callback parameter + // when this is supported in SAI. + auto* sw = SaiSwitch::instance_; + + return sw->onPacketReceived(buf, buf_size, attr_count, attr_list); +} + +void SaiSwitch::onPacketReceived(const void *buf, + sai_size_t buf_size, + uint32_t attr_count, + const sai_attribute_t *attr_list) noexcept{ + unique_ptr pkt; + + try { + pkt = make_unique(buf, buf_size, attr_count, attr_list, this); + } catch (const SaiError &e) { + + LOG(ERROR) << __FUNCTION__ << " Could not allocate SaiRxPacket. Reason: " + << e.what(); + return; + + } catch (const std::exception &ex) { + + LOG(ERROR) << __FUNCTION__ << " Could not allocate SaiRxPacket. Reason: " + << folly::exceptionStr(ex); + return; + } + + callback_->packetReceived(std::move(pkt)); +} + +void SaiSwitch::releaseLock() +{ + VLOG(4) << "Entering " << __FUNCTION__; + + if(lockFd < 0) + return; + remove(lockPath); + close(lockFd); +} + +}} // facebook::fboss diff --git a/fboss/agent/hw/sai/SaiSwitch.h b/fboss/agent/hw/sai/SaiSwitch.h new file mode 100644 index 0000000000000..e2071d1cccc2c --- /dev/null +++ b/fboss/agent/hw/sai/SaiSwitch.h @@ -0,0 +1,301 @@ +/* + * Copyright (c) 2004-present, Facebook, Inc. + * Copyright (c) 2016, Cavium, Inc. + * All rights reserved. + * + * This source code is licensed under the BSD-style license found in the + * LICENSE file in the root directory of this source tree. An additional grant + * of patent rights can be found in the PATENTS file in the same directory. + * + */ +#pragma once + +#include "fboss/agent/types.h" +#include "fboss/agent/HwSwitch.h" + +extern "C" { +#include "sai.h" +} + +#include + +namespace facebook { namespace fboss { + +class Port; +class PortStats; +class Vlan; +class Interface; +class SaiPlatformBase; +class SaiPortTable; +class SaiIntfTable; +class SaiHostTable; +class SaiRouteTable; +class SaiVrfTable; +class SaiNextHopTable; +class SaiWarmBootCache; + +class SaiSwitch : public HwSwitch { +public: + explicit SaiSwitch(SaiPlatformBase *platform); + virtual ~SaiSwitch(); + + /* + * Get default state switch is in on a cold boot + */ + std::shared_ptr getColdBootSwitchState() const; + /* + * Get state switch is in on a warm boot. + */ + std::shared_ptr getWarmBootSwitchState() const; + + std::pair, BootType> init(Callback *callback) override; + + void unregisterCallbacks() override; + + void stateChanged(const StateDelta &delta) override; + + std::unique_ptr allocatePacket(uint32_t size) override; + + bool sendPacketSwitched(std::unique_ptr pkt) noexcept override; + + bool sendPacketOutOfPort(std::unique_ptr pkt, PortID portID) noexcept override; + + folly::dynamic gracefulExit() override; + /* + * SaiSwitch state as folly::dynamic + * For now we dump nothing. + */ + folly::dynamic toFollyDynamic() const override; + + void clearWarmBootCache() override; + + void initialConfigApplied() override; + + SaiPlatformBase *getPlatform() const; + + // TODO + void updateStats(SwitchStats *switchStats) override; + + /* + * Get SAI-specific samplers. + * + * @return The number of requested counters we can handle. + * @param[out] samplers A vector of high-resolution samplers. We will + * append new samplers to this list. + * @param[in] namespaceString A string respresentation of the current + * counter namespace. + * @param[in] counterSet The set of requested counters. + */ + int getHighresSamplers( + HighresSamplerList *samplers, + const folly::StringPiece namespaceString, + const std::set &counterSet) override; + + /* Returns true if the neighbor entry for the passed in ip + * has been hit. + */ + bool getAndClearNeighborHit(RouterID vrf, + folly::IPAddress& ip) override; + + void fetchL2Table(std::vector *l2Table) override; + + cfg::PortSpeed getPortSpeed(PortID port) const override; + cfg::PortSpeed getMaxPortSpeed(PortID port) const override; + + SaiHostTable *writableHostTable() const { + return hostTable_.get(); + } + + SaiVrfTable *writableVrfTable() const { + return vrfTable_.get(); + } + + SaiNextHopTable *writableNextHopTable() const { + return nextHopTable_.get(); + } + + const SaiPortTable *getPortTable() const { + return portTable_.get(); + } + const SaiIntfTable *getIntfTable() const { + return intfTable_.get(); + } + const SaiHostTable *getHostTable() const { + return hostTable_.get(); + } + + const SaiVrfTable *getVrfTable() const { + return vrfTable_.get(); + } + + const SaiNextHopTable *getNextHopTable() const { + return nextHopTable_.get(); + } + + SaiWarmBootCache *getWarmBootCache() const { + return warmBootCache_.get(); + } + + void exitFatal() const override; + + //This function should be called from Switch which know what port and where is located + bool isPortUp(PortID port) const override; + + const sai_object_list_t &getSaiPortList() const { + return saiPortList_; + } + + /* SAI API lists */ + sai_switch_api_t *getSaiSwitchApi() const { + return saiSwitchApi_; + } + + sai_vlan_api_t *getSaiVlanApi() const { + return saiVlanApi_; + } + + sai_port_api_t *getSaiPortApi() const { + return saiPortApi_; + } + + sai_virtual_router_api_t *getSaiVrfApi() const { + return saiVrfApi_; + } + + sai_route_api_t *getSaiRouteApi() const { + return saiRouteApi_; + } + + sai_router_interface_api_t *getSaiRouterIntfApi() const { + return saiRouterIntfApi_; + } + + sai_neighbor_api_t *getSaiNeighborApi() const { + return saiNeighborApi_; + } + + sai_hostif_api_t *getSaiHostInterfaceApi() const { + return saiHostInterfaceApi_; + } + + sai_acl_api_t *getSaiAclApi() const { + return saiAclApi_; + } + + sai_next_hop_api_t *getSaiNextHopApi() const { + return saiNextHopApi_; + } + + sai_next_hop_group_api_t *getSaiNextHopGroupApi() const { + return saiNextHopGroupApi_; + } + +private: + // Forbidden copy constructor and assignment operator + SaiSwitch(SaiSwitch const &) = delete; + SaiSwitch &operator=(SaiSwitch const &) = delete; + + void updateSwitchStats(SwitchStats *switchStats); + void updatePortStats(PortID portID, PortStats *portStats); + + void updateIngressVlan(const std::shared_ptr &oldPort, + const std::shared_ptr &newPort); + void processChangedVlan(const std::shared_ptr &oldVlan, + const std::shared_ptr &newVlan); + void processAddedVlan(const std::shared_ptr &vlan); + void preprocessRemovedVlan(const std::shared_ptr &vlan); + void processRemovedVlan(const std::shared_ptr &vlan); + + void processChangedIntf(const std::shared_ptr &oldIntf, + const std::shared_ptr &newIntf); + void processAddedIntf(const std::shared_ptr &intf); + void processRemovedIntf(const std::shared_ptr &intf); + + void deinit(bool warm = true) const; + + void ecmpHashSetup(); + + template + void processNeighborEntryDelta(const DELTA &delta); + void processArpChanges(const StateDelta &delta); + + template + void processChangedRoute( + const RouterID id, const std::shared_ptr &oldRoute, + const std::shared_ptr &newRoute); + template + void processAddedRoute( + const RouterID id, const std::shared_ptr &route); + template + void processRemovedRoute( + const RouterID id, const std::shared_ptr &route); + void processRemovedRoutes(const StateDelta &delta); + void processAddedChangedRoutes(const StateDelta &delta); + + void updatePortSpeed(const std::shared_ptr &oldPort, + const std::shared_ptr &newPort); + void processDisabledPorts(const StateDelta& delta); + void processEnabledPorts(const StateDelta& delta); + + /* + * Private callback called by the SAI API. Dispatches to + * SaiSwitch::onPacketReceived. + */ + static void packetRxCallback(const void *buf, + sai_size_t buf_size, + uint32_t attr_count, + const sai_attribute_t *attr_list); + /* + * Private callback called by SaiSwitch::packetRxCallback. Dispatches to + * callback_->packetReceived. + */ + void onPacketReceived(const void *buf, + sai_size_t buf_size, + uint32_t attr_count, + const sai_attribute_t *attr_list) noexcept; + enum { + SAI_SWITCH_DEFAULT_PROFILE_ID = 0 + }; + + SaiPlatformBase *platform_ {nullptr}; + HwSwitch::Callback *callback_ {nullptr}; + + std::unique_ptr portTable_; + std::unique_ptr intfTable_; + std::unique_ptr hostTable_; + std::unique_ptr routeTable_; + std::unique_ptr vrfTable_; + std::unique_ptr nextHopTable_; + std::unique_ptr warmBootCache_; + + service_method_table_t service_ {nullptr, nullptr}; + std::string hwId_ {"dev0"}; + sai_object_list_t saiPortList_; + sai_object_id_t hostIfFdId_ {SAI_NULL_OBJECT_ID}; + + sai_switch_api_t *saiSwitchApi_ {nullptr}; + sai_vlan_api_t *saiVlanApi_ {nullptr}; + sai_port_api_t *saiPortApi_ {nullptr}; + sai_virtual_router_api_t *saiVrfApi_ {nullptr}; + sai_route_api_t *saiRouteApi_ {nullptr}; + sai_router_interface_api_t *saiRouterIntfApi_ {nullptr}; + sai_neighbor_api_t *saiNeighborApi_ {nullptr}; + sai_hostif_api_t *saiHostInterfaceApi_ {nullptr}; + sai_acl_api_t *saiAclApi_ {nullptr}; + sai_next_hop_api_t *saiNextHopApi_ {nullptr}; + sai_next_hop_group_api_t *saiNextHopGroupApi_ {nullptr}; + sai_hostif_api_t *saiHostIntfApi_ {nullptr}; + + // Temporarry storage for SaiSwitch instance while SAI callbacks + // don't provide a pointer to user data. + // TODO: remove this when SAI is updated. + static SaiSwitch *instance_; + + std::mutex lock_; + + int lockFd; + int tryGetLock(); + void releaseLock(); +}; + +}} // facebook::fboss diff --git a/fboss/agent/hw/sai/SaiTxPacket.cpp b/fboss/agent/hw/sai/SaiTxPacket.cpp new file mode 100644 index 0000000000000..595eda78501a3 --- /dev/null +++ b/fboss/agent/hw/sai/SaiTxPacket.cpp @@ -0,0 +1,38 @@ +/* + * Copyright (c) 2004-present, Facebook, Inc. + * Copyright (c) 2016, Cavium, Inc. + * All rights reserved. + * + * This source code is licensed under the BSD-style license found in the + * LICENSE file in the root directory of this source tree. An additional grant + * of patent rights can be found in the PATENTS file in the same directory. + * + */ +#include "SaiTxPacket.h" +#include "SaiError.h" + + +namespace facebook { namespace fboss { + +SaiTxPacket::SaiTxPacket(uint32_t size) { + + if (size == 0) { + throw SaiError("Could not allocate SaiTxPacket with zero buffer size."); + } + + buf_ = folly::IOBuf::takeOwnership(new uint8_t[size], // void* buf + size, // uint32_t capacity + freeTxBufCallback, // Free Function freeFn + NULL); // void* userData +} + +SaiTxPacket::~SaiTxPacket() { + // Nothing to do. The IOBuf destructor will call freeTxBufCallback() + // to free the packet data +} + +void SaiTxPacket::freeTxBufCallback(void *ptr, void* arg) { + delete[] static_cast(ptr); +} + +}} // facebook::fboss diff --git a/fboss/agent/hw/sai/SaiTxPacket.h b/fboss/agent/hw/sai/SaiTxPacket.h new file mode 100644 index 0000000000000..47f215adbdd08 --- /dev/null +++ b/fboss/agent/hw/sai/SaiTxPacket.h @@ -0,0 +1,34 @@ +/* + * Copyright (c) 2004-present, Facebook, Inc. + * Copyright (c) 2016, Cavium, Inc. + * All rights reserved. + * + * This source code is licensed under the BSD-style license found in the + * LICENSE file in the root directory of this source tree. An additional grant + * of patent rights can be found in the PATENTS file in the same directory. + * + */ +#pragma once + +#include "fboss/agent/TxPacket.h" + + +namespace facebook { namespace fboss { + +class SaiTxPacket : public TxPacket { +public: + /* + * Creates a SaiTxPacket with buffer of size 'size'. + */ + SaiTxPacket(uint32_t size); + + virtual ~SaiTxPacket(); + +private: + /* + * IOBuf callback which releases memory allocated for packet data. + */ + static void freeTxBufCallback(void *ptr, void* arg); +}; + +}} // facebook::fboss diff --git a/fboss/agent/hw/sai/SaiVrf.cpp b/fboss/agent/hw/sai/SaiVrf.cpp new file mode 100644 index 0000000000000..576837ef5821b --- /dev/null +++ b/fboss/agent/hw/sai/SaiVrf.cpp @@ -0,0 +1,74 @@ +/* + * Copyright (c) 2004-present, Facebook, Inc. + * Copyright (c) 2016, Cavium, Inc. + * All rights reserved. + * + * This source code is licensed under the BSD-style license found in the + * LICENSE file in the root directory of this source tree. An additional grant + * of patent rights can be found in the PATENTS file in the same directory. + * + */ +#include "SaiVrf.h" +#include "SaiSwitch.h" +#include "SaiError.h" + +namespace facebook { namespace fboss { + +SaiVrf::SaiVrf(const SaiSwitch *hw, RouterID fbossVrfId) + : hw_(hw), + fbossVrfId_(fbossVrfId) { + + VLOG(4) << "Entering " << __FUNCTION__; + + saiVirtualRouterApi_ = hw->getSaiVrfApi(); +} + +SaiVrf::~SaiVrf() { + VLOG(4) << "Entering " << __FUNCTION__; + + if (saiVrfId_ != SAI_NULL_OBJECT_ID) { + saiVirtualRouterApi_->remove_virtual_router(saiVrfId_); + } +} + +sai_object_id_t SaiVrf::getSaiVrfId() const { + + if (saiVrfId_ == SAI_NULL_OBJECT_ID) { + throw SaiError("Attempt to get SAI ID of the VRF ", fbossVrfId_, + "which is not programmed on hardware"); + } + + return saiVrfId_; +} + +void SaiVrf::program() { + + VLOG(4) << "Entering " << __FUNCTION__; + + if (saiVrfId_ == SAI_NULL_OBJECT_ID) { + + sai_status_t saiRetVal = SAI_STATUS_FAILURE; + sai_object_id_t vrf_id = SAI_NULL_OBJECT_ID; + + sai_attribute_t attr_list[SAI_VRF_ATTR_COUNT] = {0}; + + // V4 router admin state + attr_list[0].id = SAI_VIRTUAL_ROUTER_ATTR_ADMIN_V4_STATE; + attr_list[0].value.booldata = true; + + // V6 router admin state + attr_list[1].id = SAI_VIRTUAL_ROUTER_ATTR_ADMIN_V6_STATE; + attr_list[1].value.booldata = true; + + VLOG(2) << "Create virtual router " << fbossVrfId_.t << " through SAI"; + + saiRetVal = saiVirtualRouterApi_->create_virtual_router(&vrf_id, SAI_VRF_ATTR_COUNT, attr_list); + if (saiRetVal != SAI_STATUS_SUCCESS) { + throw SaiError("SAI create_virtual_router() failed : retVal: ", saiRetVal); + } + + saiVrfId_ = vrf_id; + } +} + +}} // facebook::fboss diff --git a/fboss/agent/hw/sai/SaiVrf.h b/fboss/agent/hw/sai/SaiVrf.h new file mode 100644 index 0000000000000..b5611cca64e8d --- /dev/null +++ b/fboss/agent/hw/sai/SaiVrf.h @@ -0,0 +1,82 @@ +/* + * Copyright (c) 2004-present, Facebook, Inc. + * Copyright (c) 2016, Cavium, Inc. + * All rights reserved. + * + * This source code is licensed under the BSD-style license found in the + * LICENSE file in the root directory of this source tree. An additional grant + * of patent rights can be found in the PATENTS file in the same directory. + * + */ +#pragma once + +#include "fboss/agent/types.h" + +extern "C" { +#include "saitypes.h" +#include "sairouter.h" +} + +namespace facebook { namespace fboss { + +class SaiSwitch; + +class SaiVrf { +public: + /** + * Constructs the SaiVrf object. + * + * This method doesn't make any calls to the SAI to program vrf there. + * program() will be called soon after construction, and any + * actual initialization logic has to be performed there. + */ + explicit SaiVrf(const SaiSwitch *hw, RouterID fbossVrfId); + /** + * Destructs the SaiVrf object. + * + * This method removes the next hops from the HW as well. + */ + virtual ~SaiVrf(); + /** + * Gets Sai Vrf ID of sai_object_id_t type. + * Should be called after program() call. + * Till that moment will always throw an exception; + * + * @return Sai Next Hop of sai_object_id_t type + */ + sai_object_id_t getSaiVrfId() const; + /** + * Gets FBOSS Vrf ID of RouterID type. + * + * @return FBOSS Vrf ID of RouterID type + */ + RouterID getFbossVrfId() const { + return fbossVrfId_; + } + + /** + * Programs Vrf in HW and stores SAI Vrf ID returned from the HW. + * If one of SAI HW calls fails throws an exception; + * + * @return none + */ + void program(); + +private: + // no copy or assignment + SaiVrf(SaiVrf const &) = delete; + SaiVrf &operator=(SaiVrf const &) = delete; + + enum { + SAI_VRF_ATTR_COUNT = 2 + }; + + const SaiSwitch *hw_ {nullptr}; + RouterID fbossVrfId_ {0}; + sai_object_id_t saiVrfId_ {SAI_NULL_OBJECT_ID}; + + sai_virtual_router_api_t *saiVirtualRouterApi_ {nullptr}; +}; + +}} // facebook::fboss + diff --git a/fboss/agent/hw/sai/SaiVrfTable.cpp b/fboss/agent/hw/sai/SaiVrfTable.cpp new file mode 100644 index 0000000000000..f7b95b1cddfb1 --- /dev/null +++ b/fboss/agent/hw/sai/SaiVrfTable.cpp @@ -0,0 +1,81 @@ +/* + * Copyright (c) 2004-present, Facebook, Inc. + * Copyright (c) 2016, Cavium, Inc. + * All rights reserved. + * + * This source code is licensed under the BSD-style license found in the + * LICENSE file in the root directory of this source tree. An additional grant + * of patent rights can be found in the PATENTS file in the same directory. + * + */ +#include "SaiVrfTable.h" +#include "SaiVrf.h" +#include "SaiSwitch.h" +#include "SaiError.h" + +namespace facebook { namespace fboss { + +SaiVrfTable::SaiVrfTable(const SaiSwitch *hw) : hw_(hw) { + VLOG(4) << "Entering " << __FUNCTION__; +} + +SaiVrfTable::~SaiVrfTable() { + VLOG(4) << "Entering " << __FUNCTION__; +} + +sai_object_id_t SaiVrfTable::getSaiVrfId(RouterID fbossVrfId) const { + VLOG(4) << "Entering " << __FUNCTION__; + + auto iter = vrfs_.find(fbossVrfId); + + if (iter == vrfs_.end()) { + throw SaiError("Cannot find SaiVrf object for vrf: ", fbossVrfId); + } + + return iter->second.first->getSaiVrfId(); +} + +SaiVrf* SaiVrfTable::incRefOrCreateSaiVrf(RouterID fbossVrfId) { + VLOG(4) << "Entering " << __FUNCTION__; + + auto ret = vrfs_.emplace(fbossVrfId, std::make_pair(nullptr, 1)); + auto &iter = ret.first; + + if (!ret.second) { + // there was an entry already in the map + iter->second.second++; // increase the reference counter + return iter->second.first.get(); + } + + SCOPE_FAIL { + vrfs_.erase(iter); + }; + + auto newVrf = folly::make_unique(hw_, fbossVrfId); + auto vrfPtr = newVrf.get(); + iter->second.first = std::move(newVrf); + + return vrfPtr; +} + +SaiVrf* SaiVrfTable::derefSaiVrf(RouterID fbossVrfId) noexcept { + VLOG(4) << "Entering " << __FUNCTION__; + + auto iter = vrfs_.find(fbossVrfId); + + if (iter == vrfs_.end()) { + return nullptr; + } + + auto &entry = iter->second; + CHECK_GT(entry.second, 0); + + if (--entry.second == 0) { + vrfs_.erase(iter); + return nullptr; + } + + return entry.first.get(); +} + +}} // facebook::fboss diff --git a/fboss/agent/hw/sai/SaiVrfTable.h b/fboss/agent/hw/sai/SaiVrfTable.h new file mode 100644 index 0000000000000..c7cfe130fee29 --- /dev/null +++ b/fboss/agent/hw/sai/SaiVrfTable.h @@ -0,0 +1,74 @@ +/* + * Copyright (c) 2004-present, Facebook, Inc. + * Copyright (c) 2016, Cavium, Inc. + * All rights reserved. + * + * This source code is licensed under the BSD-style license found in the + * LICENSE file in the root directory of this source tree. An additional grant + * of patent rights can be found in the PATENTS file in the same directory. + * + */ +#pragma once + +#include +#include "fboss/agent/types.h" + +extern "C" { +#include "saitypes.h" +} + +namespace facebook { namespace fboss { + +class SaiSwitch; +class SaiVrf; + +class SaiVrfTable { +public: + explicit SaiVrfTable(const SaiSwitch *hw); + virtual ~SaiVrfTable(); + + /** + * Given fbossVrfId gets Sai Vrf ID of sai_object_id_t type. + * Throws an exception if there is no such Vrf found. + * + * @return Sai Vrf ID of sai_object_id_t type + */ + sai_object_id_t getSaiVrfId(RouterID fbossVrfId) const; + + /* + * The following functions will modify the object. They rely on the global + * HW update lock in SaiSwitch::lock_ for the protection. + * + * SaiVrfTable maintains a reference counter for each SaiVrf + * entry allocated. + */ + + /** + * Allocates a new SaiVrf if no such one exists. For the existing + * entry, incRefOrCreateSaiVrf() will increase the reference counter by 1. + * + * @return The SaiVrf pointer just created or found. + */ + SaiVrf* incRefOrCreateSaiVrf(RouterID fbossVrfId); + + /** + * Decrease an existing SaiVrf entry's reference counter by 1. + * Only until the reference counter is 0, the SaiVrf entry + * is deleted. + * + * @return nullptr, if the SaiVrf entry is deleted + * @retrun the SaiVrf object that has reference counter + * decreased by 1, but the object is still valid as it is + * still referred in somewhere else + */ + SaiVrf *derefSaiVrf(RouterID fbossVrfId) noexcept; + +private: + typedef std::pair, uint32_t> VrfMapNode; + typedef boost::container::flat_map VrfMap; + + const SaiSwitch *hw_; + VrfMap vrfs_; +}; + +}} // facebook::fboss diff --git a/fboss/agent/hw/sai/SaiWarmBootCache.cpp b/fboss/agent/hw/sai/SaiWarmBootCache.cpp new file mode 100644 index 0000000000000..da9924fa397e0 --- /dev/null +++ b/fboss/agent/hw/sai/SaiWarmBootCache.cpp @@ -0,0 +1,223 @@ +/* + * Copyright (c) 2004-present, Facebook, Inc. + * Copyright (c) 2016, Cavium, Inc. + * All rights reserved. + * + * This source code is licensed under the BSD-style license found in the + * LICENSE file in the root directory of this source tree. An additional grant + * of patent rights can be found in the PATENTS file in the same directory. + * + */ +#include "fboss/agent/state/Interface.h" +#include "fboss/agent/state/InterfaceMap.h" +#include "fboss/agent/state/Vlan.h" +#include "fboss/agent/state/VlanMap.h" + +#include "SaiWarmBootCache.h" +#include "SaiSwitch.h" +#include "SaiPortTable.h" +#include "SaiError.h" + +using std::make_pair; +using std::make_tuple; +using std::make_shared; +using std::numeric_limits; +using std::string; +using std::vector; +using std::shared_ptr; +using boost::container::flat_map; +using folly::ByteRange; +using folly::IPAddress; +using folly::MacAddress; +using boost::container::flat_map; +using boost::container::flat_set; +using namespace facebook::fboss; + +namespace { +struct AddrTables { + AddrTables() : arpTable(make_shared()), + ndpTable(make_shared()) {} + shared_ptr arpTable; + shared_ptr ndpTable; +}; +} + +namespace facebook { namespace fboss { + +SaiWarmBootCache::SaiWarmBootCache(const SaiSwitch *hw) + : hw_(hw) { + + VLOG(4) << "Entering " << __FUNCTION__; +} + +shared_ptr SaiWarmBootCache::reconstructInterfaceMap() const { + VLOG(4) << "Entering " << __FUNCTION__; + + auto intfMap = make_shared(); + + for (const auto& vlanMacAndIntf: vlanAndMac2Intf_) { + const auto &saiIntf = vlanMacAndIntf.second; + + sai_attribute_t saiAttr[3] = {}; + + saiAttr[0].id = SAI_ROUTER_INTERFACE_ATTR_VLAN_ID; + saiAttr[1].id = SAI_ROUTER_INTERFACE_ATTR_VIRTUAL_ROUTER_ID; + saiAttr[2].id = SAI_ROUTER_INTERFACE_TYPE_PORT; + + + hw_->getSaiRouterIntfApi()->get_router_interface_attribute(saiIntf, + sizeof(saiAttr)/sizeof(sai_attribute_t), saiAttr); + + /* + intfMap->addInterface(make_shared(InterfaceID(saiIntf), + RouterID(saiAttr[1].value.u32), + VlanID(saiAttr[0].value.u32), + "", + vlanMacAndIntf.first.second)); + */ + } + + return intfMap; +} + +shared_ptr SaiWarmBootCache::reconstructVlanMap() const { + VLOG(4) << "Entering " << __FUNCTION__; + + auto vlans = make_shared(); + flat_map vlan2VlanFields; + + // Get vlan and port mapping + for (auto vlanAndInfo: vlan2VlanInfo_) { + auto vlan = make_shared(vlanAndInfo.first, ""); + + for (uint32_t i = 0; i < vlanAndInfo.second.ports_.size(); ++i) { + + sai_vlan_port_t& port = vlanAndInfo.second.ports_[i]; + PortID portId {0}; + + try { + portId = hw_->getPortTable()->getPortId(port.port_id); + } catch (const SaiError &e) { + LOG(ERROR) << e.what(); + continue; + } + + vlan->addPort(portId, port.tagging_mode == SAI_VLAN_PORT_TAGGED); + } + + vlans->addVlan(vlan); + } + + flat_map vlan2AddrTables; + + for (auto vlanAndAddrTable: vlan2AddrTables) { + auto vlan = vlans->getVlanIf(vlanAndAddrTable.first); + + if(!vlan) { + LOG(FATAL) << "Vlan: " << vlanAndAddrTable.first << " not found"; + } + + vlan->setArpTable(vlanAndAddrTable.second.arpTable); + vlan->setNdpTable(vlanAndAddrTable.second.ndpTable); + } + + return vlans; +} + +void SaiWarmBootCache::populate() { +} + +void SaiWarmBootCache::addVlanInfo(VlanID vlan, + const vector &ports) { + + vlan2VlanInfo_.insert(make_pair(vlan, VlanInfo(vlan, ports))); +} + +bool SaiWarmBootCache::fillVlanPortInfo(Vlan *vlan) { + VLOG(4) << "Entering " << __FUNCTION__; + + auto vlanItr = vlan2VlanInfo_.find(vlan->getID()); + + if (vlanItr != vlan2VlanInfo_.end()) { + Vlan::MemberPorts memberPorts; + + for (uint32_t i = 0; i < vlanItr->second.ports_.size(); ++i) { + + sai_vlan_port_t& port = vlanItr->second.ports_[i]; + PortID portId {0}; + + try { + portId = hw_->getPortTable()->getPortId(port.port_id); + } catch (const SaiError &e) { + LOG(ERROR) << e.what(); + continue; + } + + memberPorts.insert(make_pair(portId, port.tagging_mode == SAI_VLAN_PORT_TAGGED)); + } + + vlan->setPorts(memberPorts); + + return true; + } + + return false; +} + +void SaiWarmBootCache::clear() { + VLOG(4) << "Entering " << __FUNCTION__; + // Get rid of all unclaimed entries. The order is important here + // since we want to delete entries only after there are no more + // references to them. + VLOG(1) << "Warm boot : removing unreferenced entries"; + + //TODO Add here clear of Warm boot cache entries + + // Get rid of all unclaimed entries. The order is important here + // since we want to delete entries only after there are no more + // references to them. + VLOG(1) << "Warm boot : removing unreferenced entries"; + + // Nothing references routes, but routes reference ecmp egress + // and egress entries which are deleted later + for (auto vrfPfxAndRoute : vrfPrefix2Route_) { + VLOG(1) << "Deleting unreferenced route in vrf:" << + std::get<0>(vrfPfxAndRoute.first) << " for prefix : " << + std::get<1>(vrfPfxAndRoute.first) << "/" << + std::get<2>(vrfPfxAndRoute.first); + + hw_->getSaiRouteApi()->remove_route(&vrfPfxAndRoute.second); + + // Delete sai host entries. + for (auto vrfIpAndHost : vrfIp2Host_) { + VLOG(1)<< "Deleting host entry in vrf: " << + vrfIpAndHost.first.first << " for : " << vrfIpAndHost.first.second; + + hw_->getSaiNeighborApi()->remove_neighbor_entry(&vrfIpAndHost.second); + } + + vrfIp2Host_.clear(); + + // Delete interfaces + for (auto vlanMacAndIntf : vlanAndMac2Intf_) { + VLOG(1) <<"Deletingl3 interface for vlan: " << vlanMacAndIntf.first.first + <<" and mac : " << vlanMacAndIntf.first.second; + + hw_->getSaiRouterIntfApi()->remove_router_interface(vlanMacAndIntf.second); + } + + vlanAndMac2Intf_.clear(); + + // Finally delete the vlans + for (auto vlanItr = vlan2VlanInfo_.begin(); + vlanItr != vlan2VlanInfo_.end();) { + VLOG(1) << "Deleting vlan : " << vlanItr->first; + + hw_->getSaiVlanApi()->remove_vlan(vlanItr->first); + vlanItr = vlan2VlanInfo_.erase(vlanItr); + } + } + +} +}} // facebook::fboss + diff --git a/fboss/agent/hw/sai/SaiWarmBootCache.h b/fboss/agent/hw/sai/SaiWarmBootCache.h new file mode 100644 index 0000000000000..b0d04bf7dc51c --- /dev/null +++ b/fboss/agent/hw/sai/SaiWarmBootCache.h @@ -0,0 +1,211 @@ +/* + * Copyright (c) 2004-present, Facebook, Inc. + * Copyright (c) 2016, Cavium, Inc. + * All rights reserved. + * + * This source code is licensed under the BSD-style license found in the + * LICENSE file in the root directory of this source tree. An additional grant + * of patent rights can be found in the PATENTS file in the same directory. + * + */ +#pragma once + +#include +#include + +#include +#include + +#include "fboss/agent/types.h" + +extern "C" { +#include "sai.h" +} + +namespace facebook { namespace fboss { + +class SaiSwitch; +class InterfaceMap; +class Vlan; +class VlanMap; + +class SaiWarmBootCache { +public: + explicit SaiWarmBootCache(const SaiSwitch *hw); + + void populate(); + + struct VlanInfo { + VlanInfo(VlanID vlan, const std::vector &ports): + vlan_(vlan), ports_(ports) { + } + + VlanInfo(const VlanInfo& info): + vlan_(info.vlan_), ports_(info.ports_) { + } + + VlanID vlan_; + std::vector ports_; + }; + + void addVlanInfo(VlanID vlan, const std::vector &ports); + + typedef sai_object_id_t EcmpEgressId; + typedef sai_object_id_t EgressId; + typedef boost::container::flat_set EgressIds; + static EgressIds toEgressIds(EgressId* egress, int count) { + EgressIds egressIds; + std::for_each(egress, egress + count, + [&](EgressId egress) { egressIds.insert(egress);}); + return egressIds; + } + static std::string toEgressIdsStr(const EgressIds& egressIds); + + /* + * Reconstruct interface map from contents of warm boot cache + */ + std::shared_ptr reconstructInterfaceMap() const; + /* + * Reconstruct vlan map from contents of warm boot cache + */ + std::shared_ptr reconstructVlanMap() const; + +private: + typedef std::pair EgressIdAndEgress; + typedef std::pair + EcmpEgressIdAndEgress; + typedef std::pair VlanAndMac; + typedef std::pair IntfIdAndMac; + /* + * VRF, IP, Mask + */ + typedef std::tuple VrfAndPrefix; + typedef std::pair VrfAndIP; + /* + * Cache containers + */ + typedef boost::container::flat_map Vlan2VlanInfo; + typedef boost::container::flat_map + VlanAndMac2Intf; + typedef boost::container::flat_map VrfAndIP2Egress; + typedef boost::container::flat_map EgressId2VrfAndIP; + typedef boost::container::flat_map EgressIds2Ecmp; + typedef boost::container::flat_map + VrfAndIP2Host; + typedef boost::container::flat_map + VrfAndPrefix2Route; + +public: + /* + * Iterators and find functions for finding VlanInfo + */ + typedef Vlan2VlanInfo::const_iterator Vlan2VlanInfoCitr; + Vlan2VlanInfoCitr vlan2VlanInfo_beg() const { + return vlan2VlanInfo_.begin(); + } + Vlan2VlanInfoCitr vlan2VlanInfo_end() const { + return vlan2VlanInfo_.end(); + } + Vlan2VlanInfoCitr findVlanInfo(VlanID vlan) const { + return vlan2VlanInfo_.find(vlan); + } + void programmed(Vlan2VlanInfoCitr vitr) { + VLOG(1) << "Programmed vlan: " << vitr->first + << " removing from warm boot cache"; + vlan2VlanInfo_.erase(vitr); + } + + /* + * Iterators and find functions for finding sai_router_interface_id_t + */ + typedef VlanAndMac2Intf::const_iterator VlanAndMac2IntfCitr; + VlanAndMac2IntfCitr vlanAndMac2Intf_beg() const { + return vlanAndMac2Intf_.begin(); + } + VlanAndMac2IntfCitr vlanAndMac2Intf_end() const { + return vlanAndMac2Intf_.end(); + } + VlanAndMac2IntfCitr findL3Intf(VlanID vlan, folly::MacAddress mac) { + return vlanAndMac2Intf_.find(VlanAndMac(vlan, mac)); + } + + /* + * Iterators and find functions for finding sai_neighbor_entry_t + */ + typedef VrfAndIP2Host::const_iterator VrfAndIP2HostCitr; + VrfAndIP2HostCitr vrfAndIP2Host_beg() const { + return vrfIp2Host_.begin(); + } + VrfAndIP2HostCitr vrfAndIP2Host_end() const { + return vrfIp2Host_.end(); + } + VrfAndIP2HostCitr findHost(sai_object_id_t vrf, + const folly::IPAddress &ip) const { + return vrfIp2Host_.find(VrfAndIP(vrf, ip)); + } + void programmed(VrfAndIP2HostCitr vrhitr) { + VLOG(1) << "Programmed host for vrf : " << vrhitr->first.first << " ip : " + << vrhitr->first.second << " removing from warm boot cache "; + vrfIp2Host_.erase(vrhitr); + } + + /* + * Iterators and find functions for finding sai_unicast_route_entry_t + */ + typedef VrfAndPrefix2Route::const_iterator VrfAndPfx2RouteCitr; + VrfAndPfx2RouteCitr vrfAndPrefix2Route_beg() const { + return vrfPrefix2Route_.begin(); + } + VrfAndPfx2RouteCitr vrfAndPrefix2Route_end() const { + return vrfPrefix2Route_.end(); + } + VrfAndPfx2RouteCitr findRoute(sai_object_id_t vrf, const folly::IPAddress &ip, + uint8_t mask) { + using folly::IPAddress; + using folly::IPAddressV4; + using folly::IPAddressV6; + + if (ip.isV6()) { + return vrfPrefix2Route_.find(VrfAndPrefix(vrf, ip, + IPAddress(IPAddressV6(IPAddressV6::fetchMask(mask))))); + } + + return vrfPrefix2Route_.find(VrfAndPrefix(vrf, ip, + IPAddress(IPAddressV4(IPAddressV4::fetchMask(mask))))); + } + void programmed(VrfAndPfx2RouteCitr vrpitr) { + VLOG(1) << "Programmed route in vrf : " << std::get<0>(vrpitr->first) + << " prefix: " << std::get<1>(vrpitr->first) << "/" + << std::get<2>(vrpitr->first) << " removing from warm boot cache "; + vrfPrefix2Route_.erase(vrpitr); + } + +public: + //TODO add function to check if VLANs exist and if it's changed + /* + * owner is done programming its entries remove any entries + * from hw that had owner as their only remaining owner + */ + void clear(); + bool fillVlanPortInfo(Vlan *vlan); + +private: + // No copy or assignment. + SaiWarmBootCache(const SaiWarmBootCache &) = delete; + SaiWarmBootCache &operator=(const SaiWarmBootCache &) = delete; + const SaiSwitch *hw_; + Vlan2VlanInfo vlan2VlanInfo_; + VlanAndMac2Intf vlanAndMac2Intf_; + VrfAndIP2Host vrfIp2Host_; + VrfAndPrefix2Route vrfPrefix2Route_; + EgressId2VrfAndIP egressId2VrfIp_; + VrfAndIP2Egress vrfIp2Egress_; + EgressIds2Ecmp egressIds2Ecmp_; + sai_object_id_t dropEgressId_; + sai_object_id_t toCPUEgressId_; +}; + +}} // facebook::fboss diff --git a/fboss/agent/platforms/sai/SaiPlatform.cpp b/fboss/agent/platforms/sai/SaiPlatform.cpp new file mode 100644 index 0000000000000..8e3b001a73153 --- /dev/null +++ b/fboss/agent/platforms/sai/SaiPlatform.cpp @@ -0,0 +1,100 @@ +/* + * Copyright (c) 2004-present, Facebook, Inc. + * Copyright (c) 2016, Cavium, Inc. + * All rights reserved. + * + * This source code is licensed under the BSD-style license found in the + * LICENSE file in the root directory of this source tree. An additional grant + * of patent rights can be found in the PATENTS file in the same directory. + * + */ +#include "SaiPlatform.h" +#include "SaiPort.h" +#include "fboss/agent/hw/sai/SaiSwitch.h" + +using std::string; +using folly::MacAddress; + +#ifdef DEBUG_ +DEFINE_string(volatile_state_dir, "/tmp/fboss", + "Directory for storing volatile state"); +DEFINE_string(persistent_state_dir, "/tmp/fboss", + "Directory for storing persistent state"); +#else +DEFINE_string(volatile_state_dir, "/dev/shm/fboss", + "Directory for storing volatile state"); +DEFINE_string(persistent_state_dir, "/var/facebook/fboss", + "Directory for storing persistent state"); +#endif + +DEFINE_string(mac, "", + "The local MAC address for this switch"); + +namespace facebook { namespace fboss { + +SaiPlatform::SaiPlatform() { + VLOG(4) << "Entering " << __FUNCTION__; + + initLocalMac(); + // TODO: Create SaiSwitch object here + hw_.reset(new SaiSwitch(this)); +} + +SaiPlatform::~SaiPlatform() { + VLOG(4) << "Entering " << __FUNCTION__; +} + +HwSwitch *SaiPlatform::getHwSwitch() const { + VLOG(4) << "Entering " << __FUNCTION__; + return hw_.get(); +} + +void SaiPlatform::initSwSwitch(SwSwitch *sw) { +} + +MacAddress SaiPlatform::getLocalMac() const { + return localMac_; +} + +string SaiPlatform::getVolatileStateDir() const { + return FLAGS_volatile_state_dir; +} + +string SaiPlatform::getPersistentStateDir() const { + return FLAGS_persistent_state_dir; +} + +SaiPlatform::InitPortMap SaiPlatform::initPorts() { + VLOG(4) << "Entering " << __FUNCTION__; + + InitPortMap ports; + + const sai_object_list_t &pl = hw_->getSaiPortList(); + + for (uint32_t nPort = 0; nPort < pl.count; ++nPort) { + PortID portID(nPort); + auto saiPort = folly::make_unique(portID); + + ports.emplace(pl.list[nPort], saiPort.get()); + ports_.emplace(portID, std::move(saiPort)); + } + + return ports; +} + +void SaiPlatform::initLocalMac() { + if (!FLAGS_mac.empty()) { + localMac_ = MacAddress(FLAGS_mac); + return; + } + + // TODO: Get the base MAC address from the SAI or autogenerate it here. + MacAddress eth0Mac("00:11:22:33:44:55"); + localMac_ = MacAddress::fromHBO(eth0Mac.u64HBO() | 0x0000020000000000); +} + +void SaiPlatform::getProductInfo(ProductInfo& info) { + +} + +}} // facebook::fboss diff --git a/fboss/agent/platforms/sai/SaiPlatform.h b/fboss/agent/platforms/sai/SaiPlatform.h new file mode 100644 index 0000000000000..582642839d3c6 --- /dev/null +++ b/fboss/agent/platforms/sai/SaiPlatform.h @@ -0,0 +1,62 @@ +/* + * Copyright (c) 2004-present, Facebook, Inc. + * Copyright (c) 2016, Cavium, Inc. + * All rights reserved. + * + * This source code is licensed under the BSD-style license found in the + * LICENSE file in the root directory of this source tree. An additional grant + * of patent rights can be found in the PATENTS file in the same directory. + * + */ +#pragma once + +#include +#include + +#include "fboss/agent/types.h" +#include "fboss/agent/hw/sai/SaiPlatformBase.h" + +namespace facebook { namespace fboss { + +class SaiSwitch; +class SaiPort; +class HwSwitch; +class SwSwitch; + +class SaiPlatform : public SaiPlatformBase { +public: + typedef boost::container::flat_map> SaiPortMap; + + SaiPlatform(); + ~SaiPlatform(); + + HwSwitch *getHwSwitch() const override; + void initSwSwitch(SwSwitch *sw); + std::unique_ptr createHandler(SwSwitch *sw) override; + + folly::MacAddress getLocalMac() const override; + std::string getVolatileStateDir() const override; + std::string getPersistentStateDir() const override; + + InitPortMap initPorts() override; + SaiPortMap &getPortMap() { + return ports_; + } + + void onHwInitialized(SwSwitch *sw) override; + void getProductInfo(ProductInfo& info) override; + +private: + // Forbidden copy constructor and assignment operator + SaiPlatform(SaiPlatform const &) = delete; + SaiPlatform &operator=(SaiPlatform const &) = delete; + + void initLocalMac(); + std::map loadConfig(); + + folly::MacAddress localMac_; + std::unique_ptr hw_; + SaiPortMap ports_; +}; + +}} // facebook::fboss diff --git a/fboss/agent/platforms/sai/SaiPort.cpp b/fboss/agent/platforms/sai/SaiPort.cpp new file mode 100644 index 0000000000000..fb1bc844e2660 --- /dev/null +++ b/fboss/agent/platforms/sai/SaiPort.cpp @@ -0,0 +1,46 @@ +/* + * Copyright (c) 2004-present, Facebook, Inc. + * Copyright (c) 2016, Cavium, Inc. + * All rights reserved. + * + * This source code is licensed under the BSD-style license found in the + * LICENSE file in the root directory of this source tree. An additional grant + * of patent rights can be found in the PATENTS file in the same directory. + * + */ +#include "SaiPort.h" +#include "fboss/agent/hw/sai/SaiPortBase.h" + +namespace facebook { namespace fboss { + +SaiPort::SaiPort(PortID id) + : id_(id) { +} + +void SaiPort::setPort(SaiPortBase *port) { + port_ = port; +} + +void SaiPort::preDisable(bool temporary) { +} + +void SaiPort::postDisable(bool temporary) { +} + +void SaiPort::preEnable() { +} + +void SaiPort::postEnable() { +} + +bool SaiPort::isMediaPresent() { + return false; +} + +void SaiPort::statusIndication(bool enabled, bool link, + bool ingress, bool egress, + bool discards, bool errors) { +} + +}} // facebook::fboss + diff --git a/fboss/agent/platforms/sai/SaiPort.h b/fboss/agent/platforms/sai/SaiPort.h new file mode 100644 index 0000000000000..c34e1617053bd --- /dev/null +++ b/fboss/agent/platforms/sai/SaiPort.h @@ -0,0 +1,51 @@ +/* + * Copyright (c) 2004-present, Facebook, Inc. + * Copyright (c) 2016, Cavium, Inc. + * All rights reserved. + * + * This source code is licensed under the BSD-style license found in the + * LICENSE file in the root directory of this source tree. An additional grant + * of patent rights can be found in the PATENTS file in the same directory. + * + */ +#pragma once + +#include "fboss/agent/hw/sai/SaiPlatformPort.h" + +namespace facebook { namespace fboss { + +class SaiPortBase; + +class SaiPort : public SaiPlatformPort { +public: + explicit SaiPort(PortID id); + + virtual PortID getPortID() const { + return id_; + } + + void setPort(SaiPortBase *port) override; + SaiPortBase *getPort() const override { + return port_; + } + + void preDisable(bool temporary) override; + void postDisable(bool temporary) override; + void preEnable() override; + void postEnable() override; + bool isMediaPresent() override; + void linkStatusChanged(bool up, bool adminUp) override; + void statusIndication(bool enabled, bool link, + bool ingress, bool egress, + bool discards, bool errors) override; + +private: + // Forbidden copy constructor and assignment operator + SaiPort(SaiPort const &) = delete; + SaiPort &operator=(SaiPort const &) = delete; + + PortID id_ {0}; + SaiPortBase *port_ {nullptr}; +}; + +}} // facebook::fboss diff --git a/fboss/agent/platforms/sai/Sai_ctrl.cpp b/fboss/agent/platforms/sai/Sai_ctrl.cpp new file mode 100644 index 0000000000000..f45a3f343c708 --- /dev/null +++ b/fboss/agent/platforms/sai/Sai_ctrl.cpp @@ -0,0 +1,29 @@ +/* + * Copyright (c) 2004-present, Facebook, Inc. + * Copyright (c) 2016, Cavium, Inc. + * All rights reserved. + * + * This source code is licensed under the BSD-style license found in the + * LICENSE file in the root directory of this source tree. An additional grant + * of patent rights can be found in the PATENTS file in the same directory. + * + */ +#include +#include "fboss/agent/Main.h" +#include "fboss/agent/platforms/sai/SaiPlatform.h" + +using namespace facebook::fboss; +using folly::make_unique; +using std::unique_ptr; + +namespace facebook { namespace fboss { + +unique_ptr initSaiPlatform() { + return make_unique(); +} + +}} // facebook::fboss + +int main(int argc, char *argv[]) { + return facebook::fboss::fbossMain(argc, argv, initSaiPlatform); +} diff --git a/fboss/agent/platforms/sai/oss/SaiPlatform.cpp b/fboss/agent/platforms/sai/oss/SaiPlatform.cpp new file mode 100644 index 0000000000000..04368e2847465 --- /dev/null +++ b/fboss/agent/platforms/sai/oss/SaiPlatform.cpp @@ -0,0 +1,28 @@ +/* + * Copyright (c) 2004-present, Facebook, Inc. + * Copyright (c) 2016, Cavium, Inc. + * All rights reserved. + * + * This source code is licensed under the BSD-style license found in the + * LICENSE file in the root directory of this source tree. An additional grant + * of patent rights can be found in the PATENTS file in the same directory. + * + */ +#include "fboss/agent/platforms/sai/SaiPlatform.h" +#include "fboss/agent/ThriftHandler.h" + +namespace facebook { namespace fboss { + +std::unique_ptr SaiPlatform::createHandler(SwSwitch *sw) { + return folly::make_unique(sw); +} + +std::map SaiPlatform::loadConfig() { + std::map config; + return std::move(config); +} + +void SaiPlatform::onHwInitialized(SwSwitch *sw) { +} + +}} // facebook::fboss diff --git a/fboss/agent/platforms/sai/oss/SaiPort.cpp b/fboss/agent/platforms/sai/oss/SaiPort.cpp new file mode 100644 index 0000000000000..eb5d55f2b60a1 --- /dev/null +++ b/fboss/agent/platforms/sai/oss/SaiPort.cpp @@ -0,0 +1,18 @@ +/* + * Copyright (c) 2004-present, Facebook, Inc. + * Copyright (c) 2016, Cavium, Inc. + * All rights reserved. + * + * This source code is licensed under the BSD-style license found in the + * LICENSE file in the root directory of this source tree. An additional grant + * of patent rights can be found in the PATENTS file in the same directory. + * + */ +#include "fboss/agent/platforms/sai/SaiPort.h" + +namespace facebook { namespace fboss { + +void SaiPort::linkStatusChanged(bool up, bool adminUp) { +} + +}} // facebook::fboss diff --git a/fboss/github/CMakeLists_SAI.txt b/fboss/github/CMakeLists_SAI.txt new file mode 100644 index 0000000000000..6771c601bff12 --- /dev/null +++ b/fboss/github/CMakeLists_SAI.txt @@ -0,0 +1,316 @@ +cmake_minimum_required(VERSION 2.8) +project(FBOSS) +include(CMakeParseArguments) + +# Ideally, we would use +# set_property(TARGET fboss_agent PROPERTY CXX_STANDARD 11) +# to define the C++ version, but we don't want to depend on +# cmake 3.1, as it's not currently widely available. We only support +# building on Linux under GCC right now, so we might as well just hard +# code the flags. +set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=gnu++14") +set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wall -Wno-sign-compare -Wno-bool-compare") +set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wno-unused-variable -Woverloaded-virtual -Wnon-virtual-dtor") +set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wno-maybe-uninitialized -Wdeprecated-declarations") +set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wno-error=deprecated-declarations") +set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -DINCLUDE_L3 -DLONGS_ARE_64BITS") +set(CMAKE_PROGRAM_PATH "${CMAKE_SOURCE_DIR}/external/fbthrift/thrift/compiler/;${CMAKE_PROGRAM_PATH}") + +include_directories(${CMAKE_SOURCE_DIR}) +include_directories(${CMAKE_BINARY_DIR}/gen) +include_directories(${CMAKE_BINARY_DIR}/gen/fboss/agent/if/gen-cpp2) +include_directories(${CMAKE_BINARY_DIR}/gen/common/fb303/if/gen-cpp2) +include_directories(${CMAKE_BINARY_DIR}/gen/common/network/if/gen-cpp2) + +# System libraries +find_library(GLOG glog) +find_library(BOOSTSER boost_serialization) +find_library(BOOSTFS boost_filesystem) +find_library(BOOSTSYS boost_system) +find_library(BOOSTTHREAD boost_thread) +find_library(DOUBLECONV double-conversion) +find_library(GFLAGS gflags) +find_library(GLOG glog) +find_library(PTHREAD pthread) +find_library(USB usb-1.0) +find_library(PCAP pcap) +find_library(SNAPPY snappy) +find_library(ZLIB z) +find_library(SSL ssl) +find_library(CRYPTO crypto) +find_library(DL dl) +find_library(EVENT event) + +# External libraries that are not generally available. Look in external/ +# for these. This is where getdeps.sh will toss them. +find_library(FOLLY folly PATHS ${CMAKE_SOURCE_DIR}/external/folly/folly/.libs) +find_library(WANGLE wangle PATHS ${CMAKE_SOURCE_DIR}/external/wangle/wangle/build/lib) +find_library(THRIFT thrift PATHS ${CMAKE_SOURCE_DIR}/external/fbthrift/thrift/lib/cpp/.libs) +find_library(THRIFTPROTO thriftprotocol PATHS ${CMAKE_SOURCE_DIR}/external/fbthrift/thrift/lib/cpp2/.libs) +find_library(THRIFTCPP2 thriftcpp2 PATHS ${CMAKE_SOURCE_DIR}/external/fbthrift/thrift/lib/cpp2/.libs) +find_library(SAIADAPTER SaiAdapter PATHS ${CMAKE_SOURCE_DIR}/external/SAI/lib) +find_library(IPROUTE2 netlink PATHS ${CMAKE_SOURCE_DIR}/external/iproute2/lib) + +include_directories(${CMAKE_SOURCE_DIR}/external/iproute2/include) +include_directories(${CMAKE_SOURCE_DIR}/external/folly) +include_directories(${CMAKE_SOURCE_DIR}/external/fbthrift) +include_directories(${CMAKE_SOURCE_DIR}/external/wangle) +include_directories(${CMAKE_SOURCE_DIR}/external/SAI/inc) + +add_executable(sai_agent + fboss/agent/platforms/sai/SaiPlatform.cpp + fboss/agent/platforms/sai/SaiPort.cpp + fboss/agent/platforms/sai/Sai_ctrl.cpp + fboss/agent/platforms/sai/oss/SaiPort.cpp + fboss/agent/platforms/sai/oss/SaiPlatform.cpp +) +target_link_libraries(sai_agent fboss_sai_agent) + +add_library(fboss_sai_agent STATIC + fboss/agent/hw/sai/SaiError.cpp + fboss/agent/hw/sai/SaiSwitch.cpp + fboss/agent/hw/sai/SaiPortBase.cpp + fboss/agent/hw/sai/SaiPortTable.cpp + fboss/agent/hw/sai/SaiStation.cpp + fboss/agent/hw/sai/SaiIntf.cpp + fboss/agent/hw/sai/SaiIntfTable.cpp + fboss/agent/hw/sai/SaiRoute.cpp + fboss/agent/hw/sai/SaiRouteTable.cpp + fboss/agent/hw/sai/SaiHost.cpp + fboss/agent/hw/sai/SaiHostTable.cpp + fboss/agent/hw/sai/SaiWarmBootCache.cpp + fboss/agent/hw/sai/SaiVrf.cpp + fboss/agent/hw/sai/SaiVrfTable.cpp + fboss/agent/hw/sai/SaiNextHop.cpp + fboss/agent/hw/sai/SaiNextHopTable.cpp + fboss/agent/hw/sai/SaiRxPacket.cpp + fboss/agent/hw/sai/SaiTxPacket.cpp + common/stats/ServiceData.cpp + fboss/agent/ApplyThriftConfig.cpp + fboss/agent/ArpCache.cpp + fboss/agent/ArpHandler.cpp + fboss/agent/capture/PcapFile.cpp + fboss/agent/capture/PcapPkt.cpp + fboss/agent/capture/PcapQueue.cpp + fboss/agent/capture/PcapWriter.cpp + fboss/agent/capture/PktCapture.cpp + fboss/agent/capture/PktCaptureManager.cpp + fboss/agent/DHCPv4Handler.cpp + fboss/agent/DHCPv6Handler.cpp + fboss/agent/HighresCounterSubscriptionHandler.cpp + fboss/agent/HighresCounterUtil.cpp + fboss/agent/hw/mock/MockRxPacket.cpp + fboss/agent/hw/mock/MockTxPacket.cpp + fboss/agent/hw/sim/SimHandler.cpp + fboss/agent/hw/sim/SimPlatform.cpp + fboss/agent/hw/sim/SimSwitch.cpp + fboss/agent/lldp/LinkNeighbor.cpp + fboss/agent/lldp/LinkNeighborDB.cpp + fboss/agent/ndp/IPv6RouteAdvertiser.cpp + fboss/agent/oss/ApplyThriftConfig.cpp + fboss/agent/oss/Main.cpp + fboss/agent/oss/SwSwitch.cpp + fboss/agent/HwSwitch.cpp + fboss/agent/I2c.cpp + fboss/agent/IPHeaderV4.cpp + fboss/agent/IPv4Handler.cpp + fboss/agent/IPv6Handler.cpp + fboss/agent/lldp/LinkNeighbor.cpp + fboss/agent/lldp/LinkNeighborDB.cpp + fboss/agent/LldpManager.cpp + fboss/agent/Main.cpp + fboss/agent/ndp/IPv6RouteAdvertiser.cpp + fboss/agent/NdpCache.cpp + fboss/agent/NeighborListenerClient.cpp + fboss/agent/NeighborUpdater.cpp + fboss/agent/NexthopToRouteCount.cpp + fboss/agent/oss/ApplyThriftConfig.cpp + fboss/agent/oss/Main.cpp + fboss/agent/oss/SwSwitch.cpp + fboss/agent/packet/ArpHdr.cpp + fboss/agent/packet/DHCPv4Packet.cpp + fboss/agent/packet/DHCPv6Packet.cpp + fboss/agent/packet/EthHdr.cpp + fboss/agent/packet/ICMPHdr.cpp + fboss/agent/packet/IPv4Hdr.cpp + fboss/agent/packet/IPv6Hdr.cpp + fboss/agent/packet/LlcHdr.cpp + fboss/agent/packet/NDPRouterAdvertisement.cpp + fboss/agent/packet/PktUtil.cpp + fboss/agent/Platform.cpp + fboss/agent/platforms/wedge/oss/WedgePort.cpp + fboss/agent/platforms/wedge/oss/WedgeProductInfo.cpp + fboss/agent/platforms/wedge/WedgeI2CBusLock.cpp + fboss/agent/platforms/wedge/WedgePort.cpp + fboss/agent/platforms/wedge/WedgeProductInfo.cpp + fboss/agent/platforms/wedge/WedgeQsfp.cpp + fboss/agent/PortStats.cpp + fboss/agent/QsfpModule.cpp + fboss/agent/RestClient.cpp + fboss/agent/SffFieldInfo.cpp + fboss/agent/SfpModule.cpp + fboss/agent/state/AclEntry.cpp + fboss/agent/state/AclMap.cpp + fboss/agent/state/ArpEntry.cpp + fboss/agent/state/ArpResponseTable.cpp + fboss/agent/state/ArpTable.cpp + fboss/agent/state/Interface.cpp + fboss/agent/state/InterfaceMap.cpp + fboss/agent/state/NdpEntry.cpp + fboss/agent/state/NdpResponseTable.cpp + fboss/agent/state/NdpTable.cpp + fboss/agent/state/NeighborResponseTable.cpp + fboss/agent/state/NodeBase.cpp + fboss/agent/state/Port.cpp + fboss/agent/state/PortMap.cpp + fboss/agent/state/Route.cpp + fboss/agent/state/RouteDelta.cpp + fboss/agent/state/RouteForwardInfo.cpp + fboss/agent/state/RouteTable.cpp + fboss/agent/state/RouteTableMap.cpp + fboss/agent/state/RouteTableRib.cpp + fboss/agent/state/RouteTypes.cpp + fboss/agent/state/RouteUpdater.cpp + fboss/agent/state/StateDelta.cpp + fboss/agent/state/SwitchState.cpp + fboss/agent/state/Vlan.cpp + fboss/agent/state/VlanMap.cpp + fboss/agent/state/VlanMapDelta.cpp + fboss/agent/SwitchStats.cpp + fboss/agent/SwSwitch.cpp + fboss/agent/ThriftHandler.cpp + fboss/agent/TransceiverMap.cpp + fboss/agent/TunIntf.cpp + fboss/agent/TunManager.cpp + fboss/agent/UDPHeader.cpp + fboss/agent/Utils.cpp + + fboss/lib/usb/BaseWedgeI2CBus.cpp + fboss/lib/usb/BaseWedgeI2CBus.h + fboss/lib/usb/CP2112.cpp + fboss/lib/usb/CP2112.h + fboss/lib/usb/TARGETS + fboss/lib/usb/TransceiverI2CApi.h + fboss/lib/usb/UsbDevice.cpp + fboss/lib/usb/UsbDevice.h + fboss/lib/usb/UsbError.h + fboss/lib/usb/UsbHandle.cpp + fboss/lib/usb/UsbHandle.h + fboss/lib/usb/Wedge100I2CBus.cpp + fboss/lib/usb/Wedge100I2CBus.h + fboss/lib/usb/WedgeI2CBus.cpp + fboss/lib/usb/WedgeI2CBus.h + + # generated sources + ${CMAKE_BINARY_DIR}/gen/common/fb303/if/gen-cpp2/FacebookService.cpp + ${CMAKE_BINARY_DIR}/gen/common/fb303/if/gen-cpp2/FacebookService_client.cpp + ${CMAKE_BINARY_DIR}/gen/common/fb303/if/gen-cpp2/fb303_types.cpp + ${CMAKE_BINARY_DIR}/gen/common/network/if/gen-cpp/Address_constants.cpp + ${CMAKE_BINARY_DIR}/gen/common/network/if/gen-cpp/Address_reflection.cpp + ${CMAKE_BINARY_DIR}/gen/common/network/if/gen-cpp/Address_types.cpp + ${CMAKE_BINARY_DIR}/gen/common/network/if/gen-cpp2/Address_constants.cpp + ${CMAKE_BINARY_DIR}/gen/common/network/if/gen-cpp2/Address_types.cpp + ${CMAKE_BINARY_DIR}/gen/fboss/agent/gen-cpp/switch_config_reflection.cpp + ${CMAKE_BINARY_DIR}/gen/fboss/agent/gen-cpp/switch_config_types.cpp + ${CMAKE_BINARY_DIR}/gen/fboss/agent/gen-cpp/switch_config_constants.cpp + ${CMAKE_BINARY_DIR}/gen/fboss/agent/hw/sim/gen-cpp2/SimCtrl.cpp + ${CMAKE_BINARY_DIR}/gen/fboss/agent/hw/sim/gen-cpp2/sim_ctrl_types.cpp + ${CMAKE_BINARY_DIR}/gen/fboss/agent/hw/sim/gen-cpp2/sim_ctrl_constants.cpp + ${CMAKE_BINARY_DIR}/gen/fboss/agent/if/gen-cpp2/ctrl_types.cpp + ${CMAKE_BINARY_DIR}/gen/fboss/agent/if/gen-cpp2/FbossCtrl.cpp + ${CMAKE_BINARY_DIR}/gen/fboss/agent/if/gen-cpp2/NeighborListenerClient_client.cpp + ${CMAKE_BINARY_DIR}/gen/fboss/agent/if/gen-cpp2/FbossHighresClient_client.cpp + ${CMAKE_BINARY_DIR}/gen/fboss/agent/if/gen-cpp2/fboss_types.cpp + ${CMAKE_BINARY_DIR}/gen/fboss/agent/if/gen-cpp2/optic_types.cpp + ${CMAKE_BINARY_DIR}/gen/fboss/agent/if/gen-cpp2/optic_constants.cpp + ${CMAKE_BINARY_DIR}/gen/fboss/agent/if/gen-cpp2/highres_types.cpp +) +target_link_libraries(fboss_sai_agent + ${GLOG} + ${SSL} + ${PCAP} + ${USB} + ${ZLIB} + ${SNAPPY} + ${CRYPTO} + ${FOLLY} + ${WANGLE} + ${GFLAGS} + ${GLOG} + ${PTHREAD} + ${THRIFT} + ${THRIFTPROTO} + ${THRIFTCPP2} + ${SAIADAPTER} + ${IPROUTE2} + ${BOOSTFS} + ${BOOSTSER} + ${BOOSTSYS} + ${BOOSTTHREAD} + ${DOUBLECONV} + ${DL} + ${EVENT} +) + +find_program(THRIFT1 thrift1) +find_program(PYTHON python) +set(THRIFTC2OPTS json) +set(THRIFTC ${THRIFT1} -I ${CMAKE_SOURCE_DIR} -gen cpp:json,templates -gen py:) +set(THRIFTC2 + ${PYTHON} -mthrift_compiler.main -I ${CMAKE_SOURCE_DIR} --gen cpp2:) + +function(fboss_add_thrift) + cmake_parse_arguments("arg" "" "OPTIONS;THRIFTSRC;REFLECT" "SERVICES" ${ARGN}) + get_cmake_property(_variableNames VARIABLES) + get_filename_component(dirname "${arg_THRIFTSRC}" PATH) + # NAME_WE = name without extension. + get_filename_component(basename "${arg_THRIFTSRC}" NAME_WE) + foreach(srv ${arg_SERVICES}) + set(services ${services} ${CMAKE_BINARY_DIR}/gen/${dirname}/gen-cpp/${srv}.cpp) + set(services ${services} ${CMAKE_BINARY_DIR}/gen/${dirname}/gen-cpp/${srv}.h) + set(services ${services} ${CMAKE_BINARY_DIR}/gen/${dirname}/gen-cpp2/${srv}_client.cpp) + set(services ${services} ${CMAKE_BINARY_DIR}/gen/${dirname}/gen-cpp2/${srv}.cpp) + set(services ${services} ${CMAKE_BINARY_DIR}/gen/${dirname}/gen-cpp2/${srv}.h) + endforeach(srv) + foreach(reflect ${arg_REFLECT}) + set(outputs ${outputs} + ${CMAKE_BINARY_DIR}/gen/${dirname}/gen-cpp/${reflect}_reflection.h + ${CMAKE_BINARY_DIR}/gen/${dirname}/gen-cpp/${reflect}_reflection.cpp) + endforeach(reflect) + foreach(gen cpp cpp2) + foreach(output constants types) + set(outputs ${outputs} + ${CMAKE_BINARY_DIR}/gen/${dirname}/gen-${gen}/${basename}_${output}.h + ${CMAKE_BINARY_DIR}/gen/${dirname}/gen-${gen}/${basename}_${output}.cpp) + endforeach(output) + endforeach(gen) + if (${arg_OPTIONS}) + set(THRIFTOPT ${arg_OPTIONS}) + else() + set(THRIFTOPT ${THRIFTC2OPTIONS}) + endif() + add_custom_command( + OUTPUT + ${outputs} + ${services} + COMMAND + export PYTHONPATH=${CMAKE_SOURCE_DIR}/external/fbthrift/thrift/.python-local/lib/python/\; + mkdir -p ${CMAKE_BINARY_DIR}/gen/${dirname}\; + ${THRIFTC} -o ${CMAKE_BINARY_DIR}/gen/${dirname} ${CMAKE_SOURCE_DIR}/${arg_THRIFTSRC}\; + ${THRIFTC2}${arg_OPTIONS} -o ${CMAKE_BINARY_DIR}/gen/${dirname} ${CMAKE_SOURCE_DIR}/${arg_THRIFTSRC}; + ) + +endfunction(fboss_add_thrift) + +fboss_add_thrift(THRIFTSRC common/fb303/if/fb303.thrift SERVICES FacebookService) +fboss_add_thrift(THRIFTSRC common/network/if/Address.thrift + OPTIONS json,compatibility,include_prefix=common/network/if + REFLECT Address) +fboss_add_thrift(THRIFTSRC fboss/agent/switch_config.thrift + REFLECT switch_config) +fboss_add_thrift(THRIFTSRC fboss/agent/hw/sim/sim_ctrl.thrift SERVICES SimCtrl) +fboss_add_thrift(THRIFTSRC fboss/agent/if/ctrl.thrift + SERVICES FbossCtrl NeighborListenerClient) +fboss_add_thrift(THRIFTSRC fboss/agent/if/fboss.thrift) +fboss_add_thrift(THRIFTSRC fboss/agent/if/optic.thrift) +fboss_add_thrift(THRIFTSRC fboss/agent/if/highres.thrift SERVICES FbossHighresClient) diff --git a/fboss/github/getdeps_SAI.sh b/fboss/github/getdeps_SAI.sh new file mode 100755 index 0000000000000..5652c8044e266 --- /dev/null +++ b/fboss/github/getdeps_SAI.sh @@ -0,0 +1,80 @@ +#!/usr/bin/env bash + +function update() { + repo=`basename $1 .git` + echo "updating $repo..." + if [ -d $repo ]; then + (cd $repo && git pull) + else + git clone $1 + fi + [ -z "$2" ] || (cd $repo && git checkout $2) +} + +function update_SAI() { + repo="SAI" + echo "updating $repo..." + if [ -d $repo ]; then + (cd $repo && git pull) + else + git clone https://github.com/opencomputeproject/SAI.git $repo + fi + (cd $repo && git checkout v0.9.3.0) + (cd $repo && mkdir -p lib) + (cd $repo && ln -s sai/inc inc) +} + +function update_branch() { + branch="$1" + if [ "$(git symbolic-ref -q --short HEAD)" != "OpenNSL_6.3" ]; then + git checkout -tb "$branch" "origin/$branch" + fi +} + +function build() { + ( + echo "building $1..." + cd $1 + if [ -e ./CMakeLists.txt ]; then + mkdir -p build + cd build + echo cmake .. $CMAKEFLAGS + cmake .. $CMAKEFLAGS + make + else + if [ ! -e ./configure ]; then + autoreconf --install + fi + ./configure + make -j8 + fi + ) +} + +echo "installing packages" +sudo apt-get install -yq autoconf automake libdouble-conversion-dev \ + libssl-dev make zip git autoconf libtool g++ libboost-all-dev \ + libevent-dev flex bison libgoogle-glog-dev scons libkrb5-dev \ + libsnappy-dev libsasl2-dev libnuma-dev libi2c-dev libcurl4-nss-dev \ + libusb-1.0-0-dev libpcap-dev libdb5.3-dev cmake + +echo "creating external..." +mkdir -p external +( + cd external + # We hard code OpenNSL to OpenNSL-6.4.6.6 release, later releases seem to + # SIGSEV in opennsl_pkt_alloc() + update https://github.com/Broadcom-Switch/OpenNSL.git 8e0b499f02dcef751a3703c9a18600901374b28a + update \ + git://git.kernel.org/pub/scm/linux/kernel/git/shemminger/iproute2.git v3.19.0 + update https://github.com/facebook/folly.git 08dba5714790020d2fa677e34e624eb4f34a20ca + update https://github.com/facebook/wangle.git d67b7632be2923de3695201c7ac361f50646bbbf + update https://github.com/facebook/fbthrift.git 1b2b03a472c41915a8c481a06edc630674377e77 + update_SAI + build iproute2 + build folly/folly + export CMAKEFLAGS=-D"FOLLY_INCLUDE_DIR=`pwd`/folly"\ -D"FOLLY_LIBRARY=`pwd`/folly/folly/.libs/libfolly.a"\ -D"BUILD_TESTS=OFF" + build wangle/wangle + export CPPFLAGS=" -I`pwd`/folly -I`pwd`/wangle" LDFLAGS="-L`pwd`/folly/folly/.libs/ -L`pwd`/wangle/wangle/build/lib" + build fbthrift/thrift +) diff --git a/getdeps.sh b/getdeps.sh index c4c9626c83019..7a8b373acf79e 100755 --- a/getdeps.sh +++ b/getdeps.sh @@ -18,6 +18,19 @@ function update_branch() { fi } +function update_SAI() { + repo="SAI" + echo "updating $repo..." + if [ -d $repo ]; then + (cd $repo && git pull) + else + git clone https://github.com/opencomputeproject/SAI.git $repo + fi + (cd $repo && git checkout v0.9.3.0) + (cd $repo && mkdir -p lib) + (cd $repo && ln -s sai/inc inc) +} + function build() { ( echo "building $1..." @@ -57,6 +70,7 @@ mkdir -p external update https://github.com/facebook/folly.git update https://github.com/facebook/wangle.git update https://github.com/facebook/fbthrift.git + update_SAI build iproute2 build folly/folly export CMAKEFLAGS=-D"FOLLY_INCLUDE_DIR=`pwd`/folly"\ -D"FOLLY_LIBRARY=`pwd`/folly/folly/.libs/libfolly.a"\ -D"BUILD_TESTS=OFF"