From 0ee4e3903cb62ee0eb8822c9e1b175ecf5ad80a7 Mon Sep 17 00:00:00 2001 From: Zubin Shah Date: Tue, 1 Mar 2016 19:29:12 -0800 Subject: [PATCH 01/24] updated BUILD.md instructions --- BUILD.md | 16 ++++++++++++++-- 1 file changed, 14 insertions(+), 2 deletions(-) diff --git a/BUILD.md b/BUILD.md index 9d1fb973a428e..17aa996caac90 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 [SAI-0.9.3](https://github.com/opencomputeproject/SAI/tree/v0.9.3.0) Once the prerequisites are available, take the following steps. @@ -26,7 +27,9 @@ 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 copy your platform specific libSaiAdapter.so to external/SAI/lib directory +or modify path to the library file. Build as follows: @@ -37,4 +40,13 @@ cmake .. make ``` -The produced executables are `sim_agent` and `wedge_agent` in the build directory. +Build as follows (for SAI Agent) +``` +mkdir fboss/build +cd fboss/build +cmake .. -DWITH_SAI:BOOL=ON +make +``` + +The produced executables are `sim_agent` and `wedge_agent` or 'sai_agent' (when +building with SAI) in the build directory. From e94c19d4acf4b984d7cdda945b7c4232e461511e Mon Sep 17 00:00:00 2001 From: Zubin Shah Date: Tue, 1 Mar 2016 20:06:35 -0800 Subject: [PATCH 02/24] add cmakelist, getdeps --- CMakeLists.txt | 67 ++++++++++++++++++++++++++++++++++++++++++++++++++ getdeps.sh | 14 +++++++++++ 2 files changed, 81 insertions(+) diff --git a/CMakeLists.txt b/CMakeLists.txt index c4fc3f89666b8..f777cf93b724c 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -2,12 +2,17 @@ cmake_minimum_required(VERSION 2.8) project(FBOSS) include(CMakeParseArguments) +SET(WITH_SAI OFF CACHE BOOL "Build with or without sai_agent (default is without)") + # 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_PROPERTY(GLOBAL PROPERTY RULE_LAUNCH_COMPILE ccache) +SET_PROPERTY(GLOBAL PROPERTY RULE_LAUNCH_LINK ccache) + set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=gnu++11") 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") @@ -48,15 +53,73 @@ find_library(WANGLE wangle PATHS ${CMAKE_SOURCE_DIR}/external/wangle/wangle/buil 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) +if(WITH_SAI) +find_library(SAIADAPTER SaiAdapter PATHS ${CMAKE_SOURCE_DIR}/external/SAI/lib/) +else() find_library(OPENNSL opennsl PATHS ${CMAKE_SOURCE_DIR}/external/OpenNSL/bin/wedge-trident) +endif() find_library(IPROUTE2 netlink PATHS ${CMAKE_SOURCE_DIR}/external/iproute2/lib) +if(WITH_SAI) +include_directories(${CMAKE_SOURCE_DIR/external/SAI/inc}) +else() include_directories(${CMAKE_SOURCE_DIR}/external/OpenNSL/include) +endif() 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) + +if(WITH_SAI) +add_executable(sai_agent + fboss/agent/platforms/sai/SaiPlatforms.cpp + fboss/agent/platforms/sai/SaiPort.cpp + fboss/agent/platforms/sai/Sai_ctrl.cpp + fboss/agent/platforms/sai/oss/SaiPlatform.cpp + fboss/agent/platforms/sai/oss/SaiPort.cpp +) +target_link_libraries(sai_agent fboss_sai_agent) + +add_executable(sai_agent_test + fboss/agent/platforms/sai/SaiPlatform.cpp + fboss/agent/platforms/sai/SaiPort.cpp + fboss/agent/platforms/sai/Sai_ctrl.cpp + fboss/agent/platforms/sai/oss/SaiPlatform.cpp + fboss/agent/platforms/sai/oss/SaiPort.cpp +) + +target_link_libraries(sai_agent_test + 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 +) + +target_link_libraries(fboss_sai_agent + fboss_agent + ${SAIADAPTER} +) +else() #WEDGE_AGENT + add_executable(wedge_agent fboss/agent/platforms/wedge/WedgePlatform.cpp fboss/agent/platforms/wedge/WedgeProductInfo.cpp @@ -66,7 +129,11 @@ add_executable(wedge_agent fboss/agent/platforms/wedge/oss/WedgePlatform.cpp ) target_link_libraries(wedge_agent fboss_agent) +endif() + + +## TODO: THIS NEEDS TO SEPRATE INTO WEDGE VS SAI add_library(fboss_agent STATIC common/stats/ServiceData.cpp 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" From 651775aff16532da03918d00ce3131b5b46ab591 Mon Sep 17 00:00:00 2001 From: Zubin Shah Date: Tue, 1 Mar 2016 20:23:13 -0800 Subject: [PATCH 03/24] first version of Sai*.* files --- fboss/agent/hw/sai/SaiError.cpp | 18 + fboss/agent/hw/sai/SaiError.h | 37 + fboss/agent/hw/sai/SaiHost.cpp | 122 +++ fboss/agent/hw/sai/SaiHost.h | 78 ++ fboss/agent/hw/sai/SaiHostTable.cpp | 118 +++ fboss/agent/hw/sai/SaiHostTable.h | 78 ++ fboss/agent/hw/sai/SaiInterfaceTests.cpp | 394 +++++++ fboss/agent/hw/sai/SaiIntf.cpp | 234 +++++ fboss/agent/hw/sai/SaiIntf.h | 71 ++ fboss/agent/hw/sai/SaiIntfTable.cpp | 144 +++ fboss/agent/hw/sai/SaiIntfTable.h | 56 + fboss/agent/hw/sai/SaiNextHop.cpp | 167 +++ fboss/agent/hw/sai/SaiNextHop.h | 85 ++ fboss/agent/hw/sai/SaiNextHopTable.cpp | 84 ++ fboss/agent/hw/sai/SaiNextHopTable.h | 75 ++ fboss/agent/hw/sai/SaiPlatformBase.h | 47 + fboss/agent/hw/sai/SaiPlatformPort.h | 37 + fboss/agent/hw/sai/SaiPortBase.cpp | 105 ++ fboss/agent/hw/sai/SaiPortBase.h | 121 +++ fboss/agent/hw/sai/SaiPortTable.cpp | 100 ++ fboss/agent/hw/sai/SaiPortTable.h | 78 ++ fboss/agent/hw/sai/SaiPortTests.cpp | 359 +++++++ fboss/agent/hw/sai/SaiRoute.cpp | 226 ++++ fboss/agent/hw/sai/SaiRoute.h | 75 ++ fboss/agent/hw/sai/SaiRouteTable.cpp | 117 +++ fboss/agent/hw/sai/SaiRouteTable.h | 55 + fboss/agent/hw/sai/SaiRouteTests.cpp | 1006 ++++++++++++++++++ fboss/agent/hw/sai/SaiRxPacket.cpp | 80 ++ fboss/agent/hw/sai/SaiRxPacket.h | 43 + fboss/agent/hw/sai/SaiStation.cpp | 62 ++ fboss/agent/hw/sai/SaiStation.h | 46 + fboss/agent/hw/sai/SaiSwitch.cpp | 1204 ++++++++++++++++++++++ fboss/agent/hw/sai/SaiSwitch.h | 303 ++++++ fboss/agent/hw/sai/SaiTestUtils.cpp | 266 +++++ fboss/agent/hw/sai/SaiTestUtils.h | 197 ++++ fboss/agent/hw/sai/SaiTxPacket.cpp | 38 + fboss/agent/hw/sai/SaiTxPacket.h | 34 + fboss/agent/hw/sai/SaiVlanTests.cpp | 398 +++++++ fboss/agent/hw/sai/SaiVrf.cpp | 74 ++ fboss/agent/hw/sai/SaiVrf.h | 82 ++ fboss/agent/hw/sai/SaiVrfTable.cpp | 81 ++ fboss/agent/hw/sai/SaiVrfTable.h | 74 ++ fboss/agent/hw/sai/SaiWarmBootCache.cpp | 223 ++++ fboss/agent/hw/sai/SaiWarmBootCache.h | 211 ++++ 44 files changed, 7503 insertions(+) create mode 100644 fboss/agent/hw/sai/SaiError.cpp create mode 100644 fboss/agent/hw/sai/SaiError.h create mode 100644 fboss/agent/hw/sai/SaiHost.cpp create mode 100644 fboss/agent/hw/sai/SaiHost.h create mode 100644 fboss/agent/hw/sai/SaiHostTable.cpp create mode 100644 fboss/agent/hw/sai/SaiHostTable.h create mode 100644 fboss/agent/hw/sai/SaiInterfaceTests.cpp create mode 100644 fboss/agent/hw/sai/SaiIntf.cpp create mode 100644 fboss/agent/hw/sai/SaiIntf.h create mode 100644 fboss/agent/hw/sai/SaiIntfTable.cpp create mode 100644 fboss/agent/hw/sai/SaiIntfTable.h create mode 100644 fboss/agent/hw/sai/SaiNextHop.cpp create mode 100644 fboss/agent/hw/sai/SaiNextHop.h create mode 100644 fboss/agent/hw/sai/SaiNextHopTable.cpp create mode 100644 fboss/agent/hw/sai/SaiNextHopTable.h create mode 100644 fboss/agent/hw/sai/SaiPlatformBase.h create mode 100644 fboss/agent/hw/sai/SaiPlatformPort.h create mode 100644 fboss/agent/hw/sai/SaiPortBase.cpp create mode 100644 fboss/agent/hw/sai/SaiPortBase.h create mode 100644 fboss/agent/hw/sai/SaiPortTable.cpp create mode 100644 fboss/agent/hw/sai/SaiPortTable.h create mode 100644 fboss/agent/hw/sai/SaiPortTests.cpp create mode 100644 fboss/agent/hw/sai/SaiRoute.cpp create mode 100644 fboss/agent/hw/sai/SaiRoute.h create mode 100644 fboss/agent/hw/sai/SaiRouteTable.cpp create mode 100644 fboss/agent/hw/sai/SaiRouteTable.h create mode 100644 fboss/agent/hw/sai/SaiRouteTests.cpp create mode 100644 fboss/agent/hw/sai/SaiRxPacket.cpp create mode 100644 fboss/agent/hw/sai/SaiRxPacket.h create mode 100644 fboss/agent/hw/sai/SaiStation.cpp create mode 100644 fboss/agent/hw/sai/SaiStation.h create mode 100644 fboss/agent/hw/sai/SaiSwitch.cpp create mode 100644 fboss/agent/hw/sai/SaiSwitch.h create mode 100644 fboss/agent/hw/sai/SaiTestUtils.cpp create mode 100644 fboss/agent/hw/sai/SaiTestUtils.h create mode 100644 fboss/agent/hw/sai/SaiTxPacket.cpp create mode 100644 fboss/agent/hw/sai/SaiTxPacket.h create mode 100644 fboss/agent/hw/sai/SaiVlanTests.cpp create mode 100644 fboss/agent/hw/sai/SaiVrf.cpp create mode 100644 fboss/agent/hw/sai/SaiVrf.h create mode 100644 fboss/agent/hw/sai/SaiVrfTable.cpp create mode 100644 fboss/agent/hw/sai/SaiVrfTable.h create mode 100644 fboss/agent/hw/sai/SaiWarmBootCache.cpp create mode 100644 fboss/agent/hw/sai/SaiWarmBootCache.h 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..0bb179ead4587 --- /dev/null +++ b/fboss/agent/hw/sai/SaiHost.cpp @@ -0,0 +1,122 @@ +/* + * 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__; + + pSaiNeighborApi_ = hw_->GetSaiNeighborApi(); +} + +SaiHost::~SaiHost() { + VLOG(4) << "Entering " << __FUNCTION__; + + if (!added_) { + return; + } + + pSaiNeighborApi_->remove_neighbor_entry(&neighborEntry_); + VLOG(3) << "deleted L3 host object for " << ip_; +} + +void SaiHost::Program(sai_packet_action_t action) { + 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 + sai_attribute_t attr_list[SAI_NEIGHBOR_ATTR_COUNT] = {0}; + + // MAC + attr_list[0].id = SAI_NEIGHBOR_ATTR_DST_MAC_ADDRESS; + memcpy(attr_list[0].value.mac, mac_.bytes(), sizeof(attr_list[0].value.mac)); + + // packet action + attr_list[1].id = SAI_NEIGHBOR_ATTR_PACKET_ACTION; + attr_list[1].value.u32 = action; + + // create neighbor + sai_status_t saiRetVal = pSaiNeighborApi_->create_neighbor_entry(&neighborEntry_, + SAI_NEIGHBOR_ATTR_COUNT, + attr_list); + 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; + + } else if (action == action_) { + // nothing to do more + return; + } + + // fill neighbor packet action attribute + sai_attribute_t action_attr = {0}; + action_attr.id = SAI_NEIGHBOR_ATTR_PACKET_ACTION; + action_attr.value.u32 = action; + + // set action attribute + sai_status_t saiRetVal = pSaiNeighborApi_->set_neighbor_attribute(&neighborEntry_, + &action_attr); + 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..c5272b7ee3289 --- /dev/null +++ b/fboss/agent/hw/sai/SaiHost.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 + +#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(); + + /** + * 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); + +private: + // no copy or assignment + SaiHost(SaiHost const &) = delete; + SaiHost &operator=(SaiHost const &) = delete; + + enum { + SAI_NEIGHBOR_ATTR_COUNT = 2 + }; + + const SaiSwitch *hw_; + const InterfaceID intf_; + const folly::IPAddress ip_; + const 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 *pSaiNeighborApi_ { 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..122626b5f7f32 --- /dev/null +++ b/fboss/agent/hw/sai/SaiHostTable.cpp @@ -0,0 +1,118 @@ +/* + * 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::IncRefOrCreateSaiHost(InterfaceID intf, + const folly::IPAddress &ip, + const folly::MacAddress &mac) { + VLOG(4) << "Entering " << __FUNCTION__; + + Key key(intf, ip, mac); + auto ret = hosts_.emplace(key, std::make_pair(nullptr, 1)); + auto &iter = ret.first; + + if (!ret.second) { + // there was an entry already there + iter->second.second++; // increase the reference counter + return iter->second.first.get(); + } + + SCOPE_FAIL { + hosts_.erase(iter); + }; + + auto newHost = folly::make_unique(hw_, intf, ip, mac); + auto hostPtr = newHost.get(); + + iter->second.first = std::move(newHost); + + return hostPtr; +} + +SaiHost *SaiHostTable::GetSaiHost(InterfaceID intf, + const folly::IPAddress &ip, + const folly::MacAddress &mac) const { + VLOG(4) << "Entering " << __FUNCTION__; + + Key key(intf, ip, mac); + auto iter = hosts_.find(key); + + if (iter == hosts_.end()) { + return nullptr; + } + + return iter->second.first.get(); +} + +SaiHost *SaiHostTable::DerefSaiHost(InterfaceID intf, + const folly::IPAddress &ip, + const folly::MacAddress &mac) noexcept { + VLOG(4) << "Entering " << __FUNCTION__; + + Key key(intf, ip, mac); + auto iter = hosts_.find(key); + + if (iter == hosts_.end()) { + return nullptr; + } + + auto &entry = iter->second; + CHECK_GT(entry.second, 0); + + if (--entry.second == 0) { + hosts_.erase(iter); + return nullptr; + } + + return entry.first.get(); +} + +SaiHostTable::Key::Key(InterfaceID intf, + const folly::IPAddress &ip, + const folly::MacAddress &mac) + : intf_(intf) + , ip_(ip) + , mac_(mac) { +} + +bool SaiHostTable::Key::operator<(const Key &k) const{ + if (intf_ < k.intf_) { + return true; + } + + if (intf_ > k.intf_) { + return false; + } + + if (ip_ < k.ip_) { + return true; + } + + if (ip_ > k.ip_) { + return false; + } + + return (mac_ < k.mac_); +} + +}} // facebook::fboss diff --git a/fboss/agent/hw/sai/SaiHostTable.h b/fboss/agent/hw/sai/SaiHostTable.h new file mode 100644 index 0000000000000..a69f19dbd049f --- /dev/null +++ b/fboss/agent/hw/sai/SaiHostTable.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 +#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 folly::MacAddress &mac) const; + + /** + * Allocates a new SaiHost if no such one exists. + * For the existing entry, incRefOrCreateSaiHost() will increase + * the reference counter by 1. + * + * @return The SaiHost pointer just created or found. + */ + SaiHost *IncRefOrCreateSaiHost(InterfaceID intf, + const folly::IPAddress &ip, + const folly::MacAddress &mac); + + /** + * Decrease an existing SaiHost entry's reference counter by 1. + * Only until the reference counter is 0, the SaiHost entry + * is deleted. + * + * @return nullptr, if the SaiHost entry is deleted + * @retrun the SaiHost object that has reference counter + * decreased by 1, but the object is still valid as it is + * still referred in somewhere else + */ + SaiHost *DerefSaiHost(InterfaceID intf, + const folly::IPAddress &ip, + const folly::MacAddress &mac) noexcept; + +private: + struct Key { + Key(InterfaceID intf, + const folly::IPAddress &ip, + const folly::MacAddress &mac); + bool operator<(const Key &k) const; + + InterfaceID intf_; + folly::IPAddress ip_; + folly::MacAddress mac_; + }; + + typedef std::pair, uint32_t> HostMapNode; + typedef boost::container::flat_map HostMap; + + const SaiSwitch *hw_; + HostMap hosts_; +}; + +}} // facebook::fboss diff --git a/fboss/agent/hw/sai/SaiInterfaceTests.cpp b/fboss/agent/hw/sai/SaiInterfaceTests.cpp new file mode 100644 index 0000000000000..49750781106d0 --- /dev/null +++ b/fboss/agent/hw/sai/SaiInterfaceTests.cpp @@ -0,0 +1,394 @@ +/* + * 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/FbossError.h" +#include "fboss/agent/ApplyThriftConfig.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/StateDelta.h" +#include "fboss/agent/state/SwitchState.h" +#include "fboss/agent/gen-cpp/switch_config_types.h" +#include "fboss/agent/hw/sai/SaiTestUtils.h" +#include "fboss/agent/platforms/sai/SaiPlatform.h" + +#include + +using namespace facebook::fboss; +using folly::IPAddress; +using folly::MacAddress; +using std::make_shared; +using std::shared_ptr; +using ::testing::Return; + +TEST(Interface, addrToReach) { + SaiPlatform platform; + cfg::SwitchConfig config; + config.vlans.resize(2); + config.vlans[0].id = 1; + config.vlans[1].id = 2; + config.interfaces.resize(2); + auto *intfConfig = &config.interfaces[0]; + intfConfig->intfID = 1; + intfConfig->vlanID = 1; + intfConfig->routerID = 1; + intfConfig->mac = "00:02:00:11:22:33"; + intfConfig->__isset.mac = true; + intfConfig->ipAddresses.resize(4); + intfConfig->ipAddresses[0] = "10.1.1.1/24"; + intfConfig->ipAddresses[1] = "20.1.1.2/24"; + intfConfig->ipAddresses[2] = "::22:33:44/120"; + intfConfig->ipAddresses[3] = "::11:11:11/120"; + + intfConfig = &config.interfaces[1]; + intfConfig->intfID = 2; + intfConfig->vlanID = 2; + intfConfig->routerID = 2; + intfConfig->mac = "00:02:00:11:22:33"; + intfConfig->__isset.mac = true; + intfConfig->ipAddresses.resize(4); + intfConfig->ipAddresses[0] = "10.1.1.1/24"; + intfConfig->ipAddresses[1] = "20.1.1.2/24"; + intfConfig->ipAddresses[2] = "::22:33:44/120"; + intfConfig->ipAddresses[3] = "::11:11:11/120"; + + InterfaceID id(1); + shared_ptr oldState = make_shared(); + auto state = publishAndApplyConfig(oldState, &config, &platform); + ASSERT_NE(nullptr, state); + const auto &intfs = state->getInterfaces(); + const auto &intf1 = intfs->getInterface(InterfaceID(1)); + const auto &intf2 = intfs->getInterface(InterfaceID(2)); + + EXPECT_TRUE(intf1->hasAddress(IPAddress("10.1.1.1"))); + EXPECT_FALSE(intf1->hasAddress(IPAddress("10.1.2.1"))); + EXPECT_TRUE(intf2->hasAddress(IPAddress("::11:11:11"))); + EXPECT_FALSE(intf2->hasAddress(IPAddress("::11:11:12"))); + + auto ret = intfs->getIntfAddrToReach(RouterID(1), IPAddress("20.1.1.100")); + EXPECT_EQ(intf1.get(), ret.intf); + EXPECT_EQ(IPAddress("20.1.1.2"), *ret.addr); + EXPECT_EQ(24, ret.mask); + + ret = intfs->getIntfAddrToReach(RouterID(2), IPAddress("::22:33:4f")); + EXPECT_EQ(intf2.get(), ret.intf); + EXPECT_EQ(IPAddress("::22:33:44"), *ret.addr); + EXPECT_EQ(120, ret.mask); + + ret = intfs->getIntfAddrToReach(RouterID(2), IPAddress("::22:34:5f")); + EXPECT_EQ(nullptr, ret.intf); + EXPECT_EQ(nullptr, ret.addr); + EXPECT_EQ(0, ret.mask); +} + +TEST(Interface, applyConfig) { + SaiPlatform platform; + cfg::SwitchConfig config; + config.vlans.resize(2); + config.vlans[0].id = 1; + config.vlans[1].id = 2; + config.interfaces.resize(1); + auto *intfConfig = &config.interfaces[0]; + intfConfig->intfID = 1; + intfConfig->vlanID = 1; + intfConfig->routerID = 0; + intfConfig->mac = "00:02:00:11:22:33"; + intfConfig->__isset.mac = true; + + InterfaceID id(1); + shared_ptr oldState; + shared_ptr state; + shared_ptr oldInterface; + shared_ptr interface; + auto updateState = [&]() { + oldState = state; + oldInterface = interface; + state = publishAndApplyConfig(oldState, &config, &platform); + EXPECT_NE(oldState, state); + ASSERT_NE(nullptr, state); + interface = state->getInterfaces()->getInterface(id); + EXPECT_NE(oldInterface, interface); + ASSERT_NE(nullptr, interface); + }; + + state = make_shared(); + updateState(); + NodeID nodeID = interface->getNodeID(); + EXPECT_EQ(0, interface->getGeneration()); + EXPECT_EQ(VlanID(1), interface->getVlanID()); + EXPECT_EQ(RouterID(0), interface->getRouterID()); + EXPECT_EQ("Interface 1", interface->getName()); + EXPECT_EQ(MacAddress("00:02:00:11:22:33"), interface->getMac()); + EXPECT_EQ(Interface::Addresses {}, interface->getAddresses()); + EXPECT_EQ(0, interface->getNdpConfig().routerAdvertisementSeconds); + + // same configuration cause nothing changed + EXPECT_EQ(nullptr, publishAndApplyConfig(state, &config, &platform)); + + // vlanID change + intfConfig->vlanID = 2; + updateState(); + EXPECT_EQ(nodeID, interface->getNodeID()); + EXPECT_EQ(oldInterface->getGeneration() + 1, interface->getGeneration()); + EXPECT_EQ(VlanID(2), interface->getVlanID()); + EXPECT_EQ(RouterID(0), interface->getRouterID()); + EXPECT_EQ(oldInterface->getName(), interface->getName()); + EXPECT_EQ(oldInterface->getMac(), interface->getMac()); + EXPECT_EQ(oldInterface->getAddresses(), interface->getAddresses()); + + // routerID change + intfConfig->routerID = 1; + updateState(); + EXPECT_EQ(nodeID, interface->getNodeID()); + EXPECT_EQ(oldInterface->getGeneration() + 1, interface->getGeneration()); + EXPECT_EQ(VlanID(2), interface->getVlanID()); + EXPECT_EQ(RouterID(1), interface->getRouterID()); + EXPECT_EQ(oldInterface->getName(), interface->getName()); + EXPECT_EQ(oldInterface->getMac(), interface->getMac()); + EXPECT_EQ(oldInterface->getAddresses(), interface->getAddresses()); + + // MAC address change + intfConfig->mac = "00:02:00:12:34:56"; + updateState(); + EXPECT_EQ(oldInterface->getGeneration() + 1, interface->getGeneration()); + EXPECT_EQ(VlanID(2), interface->getVlanID()); + EXPECT_EQ(RouterID(1), interface->getRouterID()); + EXPECT_EQ(oldInterface->getName(), interface->getName()); + EXPECT_EQ(MacAddress("00:02:00:12:34:56"), interface->getMac()); + EXPECT_EQ(oldInterface->getAddresses(), interface->getAddresses()); + // Use the platform supplied MAC + intfConfig->mac = ""; + intfConfig->__isset.mac = false; + MacAddress platformMac("00:02:00:ab:cd:ef"); +// EXPECT_CALL(platform, getLocalMac()).WillRepeatedly(Return(platformMac)); + updateState(); + EXPECT_EQ(nodeID, interface->getNodeID()); + EXPECT_EQ(oldInterface->getGeneration() + 1, interface->getGeneration()); + EXPECT_EQ(oldInterface->getVlanID(), interface->getVlanID()); + EXPECT_EQ(oldInterface->getRouterID(), interface->getRouterID()); + EXPECT_EQ(oldInterface->getName(), interface->getName()); + EXPECT_EQ(platformMac, interface->getMac()); + EXPECT_EQ(oldInterface->getAddresses(), interface->getAddresses()); + + // IP addresses change + intfConfig->ipAddresses.resize(4); + intfConfig->ipAddresses[0] = "10.1.1.1/24"; + intfConfig->ipAddresses[1] = "20.1.1.2/24"; + intfConfig->ipAddresses[2] = "::22:33:44/120"; + intfConfig->ipAddresses[3] = "::11:11:11/120"; + updateState(); + EXPECT_EQ(nodeID, interface->getNodeID()); + EXPECT_EQ(oldInterface->getGeneration() + 1, interface->getGeneration()); + EXPECT_EQ(VlanID(2), interface->getVlanID()); + EXPECT_EQ(RouterID(1), interface->getRouterID()); + EXPECT_EQ(oldInterface->getName(), interface->getName()); + EXPECT_EQ(oldInterface->getMac(), interface->getMac()); + EXPECT_EQ(4, interface->getAddresses().size()); + + // change the order of IP address shall not change the interface + intfConfig->ipAddresses[0] = "10.1.1.1/24"; + intfConfig->ipAddresses[1] = "::22:33:44/120"; + intfConfig->ipAddresses[2] = "20.1.1.2/24"; + intfConfig->ipAddresses[3] = "::11:11:11/120"; + EXPECT_EQ(nullptr, publishAndApplyConfig(state, &config, &platform)); + + // duplicate IP addresses causes throw + intfConfig->ipAddresses[1] = intfConfig->ipAddresses[0]; + EXPECT_THROW(publishAndApplyConfig(state, &config, &platform), FbossError); + // Should still throw even if the mask is different + intfConfig->ipAddresses[1] = "10.1.1.1/16"; + EXPECT_THROW(publishAndApplyConfig(state, &config, &platform), FbossError); + intfConfig->ipAddresses[1] = "::22:33:44/120"; + + // Name change + intfConfig->name = "myintf"; + intfConfig->__isset.name = true; + updateState(); + EXPECT_EQ(nodeID, interface->getNodeID()); + EXPECT_EQ(oldInterface->getGeneration() + 1, interface->getGeneration()); + EXPECT_EQ("myintf", interface->getName()); + EXPECT_EQ(oldInterface->getVlanID(), interface->getVlanID()); + EXPECT_EQ(oldInterface->getRouterID(), interface->getRouterID()); + EXPECT_EQ(oldInterface->getMac(), interface->getMac()); + EXPECT_EQ(oldInterface->getAddresses(), interface->getAddresses()); + // Reset the name back to it's default value + intfConfig->__isset.name = false; + updateState(); + EXPECT_EQ(nodeID, interface->getNodeID()); + EXPECT_EQ(oldInterface->getGeneration() + 1, interface->getGeneration()); + EXPECT_EQ("Interface 1", interface->getName()); + EXPECT_EQ(oldInterface->getVlanID(), interface->getVlanID()); + EXPECT_EQ(oldInterface->getRouterID(), interface->getRouterID()); + EXPECT_EQ(oldInterface->getMac(), interface->getMac()); + EXPECT_EQ(oldInterface->getAddresses(), interface->getAddresses()); + EXPECT_EQ(oldInterface->getNdpConfig(), interface->getNdpConfig()); + + // Change the NDP configuration + intfConfig->__isset.ndp = true; + intfConfig->ndp.routerAdvertisementSeconds = 4; + updateState(); + EXPECT_EQ(nodeID, interface->getNodeID()); + EXPECT_EQ(oldInterface->getGeneration() + 1, interface->getGeneration()); + EXPECT_EQ(oldInterface->getName(), interface->getName()); + EXPECT_EQ(oldInterface->getVlanID(), interface->getVlanID()); + EXPECT_EQ(oldInterface->getRouterID(), interface->getRouterID()); + EXPECT_EQ(oldInterface->getMac(), interface->getMac()); + EXPECT_EQ(oldInterface->getAddresses(), interface->getAddresses()); + EXPECT_NE(oldInterface->getNdpConfig(), interface->getNdpConfig()); + EXPECT_EQ(4, interface->getNdpConfig().routerAdvertisementSeconds); + // Update the RA interval to 30 seconds + intfConfig->ndp.routerAdvertisementSeconds = 30; + updateState(); + EXPECT_EQ(nodeID, interface->getNodeID()); + EXPECT_EQ(oldInterface->getGeneration() + 1, interface->getGeneration()); + EXPECT_NE(oldInterface->getNdpConfig(), interface->getNdpConfig()); + EXPECT_EQ(30, interface->getNdpConfig().routerAdvertisementSeconds); + // Drop the NDP configuration + intfConfig->__isset.ndp = false; + updateState(); + EXPECT_EQ(nodeID, interface->getNodeID()); + EXPECT_EQ(oldInterface->getGeneration() + 1, interface->getGeneration()); + EXPECT_NE(oldInterface->getNdpConfig(), interface->getNdpConfig()); + EXPECT_EQ(0, interface->getNdpConfig().routerAdvertisementSeconds); + + // Changing the ID creates a new interface + intfConfig->intfID = 2; + id = InterfaceID(2); + updateState(); + // The generation number for the new interface will be 0 + EXPECT_NE(nodeID, interface->getNodeID()); + EXPECT_EQ(0, interface->getGeneration()); + EXPECT_EQ(oldInterface->getVlanID(), interface->getVlanID()); + EXPECT_EQ(oldInterface->getRouterID(), interface->getRouterID()); + EXPECT_EQ("Interface 2", interface->getName()); + EXPECT_EQ(oldInterface->getMac(), interface->getMac()); + EXPECT_EQ(oldInterface->getAddresses(), interface->getAddresses()); +} + +/* + * Test that forEachChanged(StateDelta::getIntfsDelta(), ...) invokes the + * callback for the specified list of changed interfaces. + */ +void checkChangedIntfs(const shared_ptr &oldIntfs, + const shared_ptr &newIntfs, + const std::set changedIDs, + const std::set addedIDs, + const std::set removedIDs) { + auto oldState = make_shared(); + oldState->resetIntfs(oldIntfs); + auto newState = make_shared(); + newState->resetIntfs(newIntfs); + + std::set foundChanged; + std::set foundAdded; + std::set foundRemoved; + StateDelta delta(oldState, newState); + DeltaFunctions::forEachChanged(delta.getIntfsDelta(), + [&] (const shared_ptr &oldIntf, + const shared_ptr &newIntf) { + EXPECT_EQ(oldIntf->getID(), newIntf->getID()); + EXPECT_NE(oldIntf, newIntf); + + auto ret = foundChanged.insert(oldIntf->getID()); + EXPECT_TRUE(ret.second); + }, + [&] (const shared_ptr &intf) { + auto ret = foundAdded.insert(intf->getID()); + EXPECT_TRUE(ret.second); + }, + [&] (const shared_ptr &intf) { + auto ret = foundRemoved.insert(intf->getID()); + EXPECT_TRUE(ret.second); + }); + + EXPECT_EQ(changedIDs, foundChanged); + EXPECT_EQ(addedIDs, foundAdded); + EXPECT_EQ(removedIDs, foundRemoved); +} + +TEST(InterfaceMap, applyConfig) { + SaiPlatform platform; + auto stateV0 = make_shared(); + auto intfsV0 = stateV0->getInterfaces(); + + cfg::SwitchConfig config; + config.vlans.resize(3); + config.vlans[0].id = 1; + config.vlans[1].id = 2; + config.vlans[2].id = 3; + config.interfaces.resize(2); + config.interfaces[0].intfID = 1; + config.interfaces[0].vlanID = 1; + config.interfaces[0].__isset.mac = true; + config.interfaces[0].mac = "00:00:00:00:00:11"; + config.interfaces[1].intfID = 2; + config.interfaces[1].vlanID = 2; + config.interfaces[1].__isset.mac = true; + config.interfaces[1].mac = "00:00:00:00:00:22"; + + auto stateV1 = publishAndApplyConfig(stateV0, &config, &platform); + ASSERT_NE(nullptr, stateV1); + auto intfsV1 = stateV1->getInterfaces(); + EXPECT_NE(intfsV1, intfsV0); + EXPECT_EQ(1, intfsV1->getGeneration()); + EXPECT_EQ(2, intfsV1->size()); + + // verify interface intfID==1 + auto intf1 = intfsV1->getInterface(InterfaceID(1)); + ASSERT_NE(nullptr, intf1); + EXPECT_EQ(VlanID(1), intf1->getVlanID()); + EXPECT_EQ("00:00:00:00:00:11", intf1->getMac().toString()); + EXPECT_EQ(0, intf1->getGeneration()); + + checkChangedIntfs(intfsV0, intfsV1, {}, {1, 2}, {}); + + // getInterface() should throw on a non-existent interface + EXPECT_THROW(intfsV1->getInterface(InterfaceID(99)), FbossError); + // getInterfaceIf() should return nullptr on a non-existent interface + EXPECT_EQ(nullptr, intfsV1->getInterfaceIf(InterfaceID(99))); + + // applying the same configure results in no change + EXPECT_EQ(nullptr, publishAndApplyConfig(stateV1, &config, &platform)); + + // adding some IP addresses + config.interfaces[1].ipAddresses.resize(2); + config.interfaces[1].ipAddresses[0] = "192.168.1.1/16"; + config.interfaces[1].ipAddresses[1] = "::1/48"; + auto stateV2 = publishAndApplyConfig(stateV1, &config, &platform); + ASSERT_NE(nullptr, stateV2); + auto intfsV2 = stateV2->getInterfaces(); + EXPECT_NE(intfsV1, intfsV2); + EXPECT_EQ(2, intfsV2->getGeneration()); + EXPECT_EQ(2, intfsV2->size()); + auto intf2 = intfsV2->getInterface(InterfaceID(2)); + EXPECT_EQ(2, intf2->getAddresses().size()); + + checkChangedIntfs(intfsV1, intfsV2, {2}, {}, {}); + + // add a new one together with deleting an existing one + config.interfaces[0].intfID = 3; + config.interfaces[0].vlanID = 3; + config.interfaces[0].__isset.mac = true; + config.interfaces[0].mac = "00:00:00:00:00:33"; + auto stateV3 = publishAndApplyConfig(stateV2, &config, &platform); + ASSERT_NE(nullptr, stateV3); + auto intfsV3 = stateV3->getInterfaces(); + EXPECT_NE(intfsV2, intfsV3); + EXPECT_EQ(3, intfsV3->getGeneration()); + EXPECT_EQ(2, intfsV3->size()); + auto intf3 = intfsV3->getInterface(InterfaceID(3)); + EXPECT_EQ(0, intf3->getAddresses().size()); + EXPECT_EQ(config.interfaces[0].mac, intf3->getMac().toString()); + // intf 1 should not be there anymroe + EXPECT_EQ(nullptr, intfsV3->getInterfaceIf(InterfaceID(1))); + + checkChangedIntfs(intfsV2, intfsV3, {}, {3}, {1}); +} diff --git a/fboss/agent/hw/sai/SaiIntf.cpp b/fboss/agent/hw/sai/SaiIntf.cpp new file mode 100644 index 0000000000000..db317e8b0be8f --- /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__; + + pSaiRouterInterfaceApi_ = 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(); + } + + pSaiRouterInterfaceApi_->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 = pSaiRouterInterfaceApi_->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 = pSaiRouterInterfaceApi_->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 = pSaiRouterInterfaceApi_->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()->IncRefOrCreateSaiHost(intf->getID(), + addr, + intf->getMac()); + + try { + host->Program(SAI_PACKET_ACTION_TRAP); + } catch (const FbossError &e) { + hw_->WritableHostTable()->DerefSaiHost(intf->getID(), addr, intf->getMac()); + // let handle this in the caller. + throw; + } + + auto ret = hosts_.insert(addr); + CHECK(ret.second); + SCOPE_FAIL { + hw_->WritableHostTable()->DerefSaiHost(intf->getID(), addr, intf->getMac()); + 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()->DerefSaiHost(intf_->getID(), addr, intf_->getMac()); + 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..9dbdac7a1e00f --- /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 *pSaiRouterInterfaceApi_ { 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..597f67296ff58 --- /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..045e7348167b0 --- /dev/null +++ b/fboss/agent/hw/sai/SaiIntfTable.h @@ -0,0 +1,56 @@ +/* + * 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 + 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..44538dc120c14 --- /dev/null +++ b/fboss/agent/hw/sai/SaiNextHop.cpp @@ -0,0 +1,167 @@ +/* + * 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" + +namespace facebook { namespace fboss { + +SaiNextHop::SaiNextHop(const SaiSwitch *hw, const RouteForwardInfo &fwdInfo) + : hw_(hw), + fwdInfo_(fwdInfo) { + + VLOG(4) << "Entering " << __FUNCTION__; + + pSaiNextHopApi_ = hw->GetSaiNextHopApi(); + pSaiNextHopGroupApi_ = 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 iter = nextHops_.begin(); iter != nextHops_.end(); ++iter) { + + if (nhGroupId_ != SAI_NULL_OBJECT_ID) { + + saiRetVal = pSaiNextHopGroupApi_->remove_next_hop_from_group(nhGroupId_, 1, &iter->first); + if (saiRetVal != SAI_STATUS_SUCCESS) { + LOG(ERROR) << "Could not remove next hop " << iter->second.str() << " from next hop group: " + << nhGroupId_ << " of next hops: " << fwdInfo_.str() << "Error: " << saiRetVal; + } + } + + saiRetVal = pSaiNextHopApi_->remove_next_hop(iter->first); + if (saiRetVal != SAI_STATUS_SUCCESS) { + LOG(ERROR) << "Could not remove next hop " << iter->second.str() + << " from HW. Error: " << saiRetVal; + } + } + + if (nhGroupId_ != SAI_NULL_OBJECT_ID) { + saiRetVal = pSaiNextHopGroupApi_->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 (nhGroupId_ != SAI_NULL_OBJECT_ID) { + return nhGroupId_; + } + + auto iter = nextHops_.begin(); + if (iter == nextHops_.end()) { + throw SaiError("Attempt to get SAI ID of the next hop: ", + fwdInfo_, "which is not programmed on hardware"); + } + + return iter->first; +} + +void SaiNextHop::Program() { + + VLOG(4) << "Entering " << __FUNCTION__; + + sai_status_t saiRetVal = SAI_STATUS_FAILURE; + RouteForwardInfo::Nexthops nextHops = fwdInfo_.getNexthops(); + + if (nextHops.empty()) { + throw SaiError("Next hop list to be programmed on HW is empty"); + } + + if ((nhGroupId_ != SAI_NULL_OBJECT_ID) || + (nextHops_.size() != 0)) { + + throw SaiError("Attempt to configure for the second time the next hop ", + fwdInfo_.str().c_str(), "which was already programmed in HW"); + } + + uint8_t nh_count = nextHops.size(); + + // an array of object IDs passed to create_next_hop_group() + std::unique_ptr nextHopIds(new sai_object_id_t[nh_count]); + + // Create all next hops and fill the nextHops_ map and nextHopIds array + uint8_t i = 0; + for (auto iter = nextHops.begin(); iter != nextHops.end(); ++iter, ++i) { + + sai_object_id_t nhId = SAI_NULL_OBJECT_ID; + sai_attribute_t attr_list[SAI_NH_ATTR_COUNT] = {0}; + + // Fill atributes + // NH type + attr_list[0].id = SAI_NEXT_HOP_ATTR_TYPE; + attr_list[0].value.u32 = SAI_NEXT_HOP_IP; + + // IP address + attr_list[1].id = SAI_NEXT_HOP_ATTR_IP; + + if (iter->nexthop.isV4()) { + attr_list[1].value.ipaddr.addr_family = SAI_IP_ADDR_FAMILY_IPV4; + memcpy(&attr_list[1].value.ipaddr.addr.ip4, iter->nexthop.bytes(), sizeof(attr_list[1].value.ip4)); + } else { + attr_list[1].value.ipaddr.addr_family = SAI_IP_ADDR_FAMILY_IPV6; + memcpy(attr_list[1].value.ipaddr.addr.ip6, iter->nexthop.bytes(), sizeof(attr_list[1].value.ip6)); + } + + // Router interface ID + attr_list[2].id = SAI_NEXT_HOP_ATTR_ROUTER_INTERFACE_ID; + attr_list[2].value.oid = hw_->GetIntfTable()->GetIntfIf(iter->intf)->GetIfId(); + + // Create NH in SAI + saiRetVal = pSaiNextHopApi_->create_next_hop(&nhId, SAI_NH_ATTR_COUNT, attr_list); + if (saiRetVal != SAI_STATUS_SUCCESS) { + throw SaiError("Could not create next hop ", iter->str().c_str(), " on HW. Error: ", saiRetVal); + } + + nextHops_.emplace(nhId, *iter); + nextHopIds[i] = nhId; + } + + if (nh_count == 1) { + // Nothing more to do for a single host. + return; + } + + // Now we will create NH group with the next hops previously created + sai_attribute_t attr_list[SAI_NH_GROUP_ATTR_COUNT] = {0}; + sai_object_list_t nhList = {nh_count, nextHopIds.get()}; + sai_object_id_t nhGroupId = SAI_NULL_OBJECT_ID; + + // Fill atributes + // NH group type + attr_list[0].id = SAI_NEXT_HOP_GROUP_ATTR_TYPE; + attr_list[0].value.u32 = SAI_NEXT_HOP_GROUP_ECMP; + + // NH list + attr_list[1].id = SAI_NEXT_HOP_ATTR_IP; + attr_list[1].value.objlist = nhList; + + // Create NH group in SAI + saiRetVal = pSaiNextHopGroupApi_->create_next_hop_group(&nhGroupId, SAI_NH_GROUP_ATTR_COUNT, attr_list); + if (saiRetVal != SAI_STATUS_SUCCESS) { + throw SaiError("Could not create next hop group", fwdInfo_.str().c_str(), " on HW. Error: ", saiRetVal); + } + + nhGroupId_ = 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..d65bbcc0d8d67 --- /dev/null +++ b/fboss/agent/hw/sai/SaiNextHop.h @@ -0,0 +1,85 @@ +/* + * 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" +#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. + * 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 GetSaiNextHopId() const; + + /** + * 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. + * If one of SAI HW calls fails it throws an exception; + * + * @return none + */ + void Program(); + +private: + // no copy or assignment + SaiNextHop(SaiNextHop const &) = delete; + SaiNextHop &operator=(SaiNextHop const &) = delete; + + enum { + SAI_NH_GROUP_ATTR_COUNT = 2, + SAI_NH_ATTR_COUNT = 3 + }; + + const SaiSwitch *hw_ {nullptr}; + + RouteForwardInfo fwdInfo_; + std::map nextHops_; + sai_object_id_t nhGroupId_ {SAI_NULL_OBJECT_ID}; + + sai_next_hop_api_t *pSaiNextHopApi_ {nullptr}; + sai_next_hop_group_api_t *pSaiNextHopGroupApi_ {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..e5a3a837f729a --- /dev/null +++ b/fboss/agent/hw/sai/SaiNextHopTable.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 "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()) { + throw SaiError("Cannot find SaiNextHop object for forwarding info: ", fwdInfo); + } + + 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(); +} + +}} // facebook::fboss diff --git a/fboss/agent/hw/sai/SaiNextHopTable.h b/fboss/agent/hw/sai/SaiNextHopTable.h new file mode 100644 index 0000000000000..2745c6ea2c948 --- /dev/null +++ b/fboss/agent/hw/sai/SaiNextHopTable.h @@ -0,0 +1,75 @@ +/* + * 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. + * Throws an exception if there is no such Next Hop found. + * + * @return Sai Vrf ID of sai_object_id_t type + */ + 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; + +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..837d8ee1471b3 --- /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..a468e0bc5aa68 --- /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..53695c2674e9b --- /dev/null +++ b/fboss/agent/hw/sai/SaiPortBase.cpp @@ -0,0 +1,105 @@ +/* + * 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 *pSwitch, sai_object_id_t saiPortId, PortID fbossPortId, SaiPlatformPort *pPlatformPort) + : pHw_(pSwitch), + pPlatformPort_(pPlatformPort), + saiPortId_(saiPortId), + fbossPortId_(fbossPortId) { + VLOG(6) << "Entering " << __FUNCTION__; + + sai_api_query(SAI_API_PORT, (void **) &pSaiPortApi_); +} + +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(); + } + + SetPortStatus(true); + + initDone_ = true; +} + +void SaiPortBase::SetPortStatus(bool linkStatus) { + VLOG(6) << "Entering " << __FUNCTION__; + + if (initDone_ && (linkStatus_ == linkStatus)) { + return; + } + + sai_attribute_t attr {0}; + attr.id = SAI_PORT_ATTR_ADMIN_STATE; + attr.value.booldata = linkStatus; + + sai_status_t saiStatus = pSaiPortApi_->set_port_attribute(saiPortId_, &attr); + if(SAI_STATUS_SUCCESS != saiStatus) { + LOG(ERROR) << "failed to set port status: " << linkStatus << "error: " << saiStatus; + } + + 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. + pPlatformPort_->linkStatusChanged(linkStatus, true); +} + +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 = pSaiPortApi_->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; +} + +std::string SaiPortBase::StatName(folly::StringPiece name) const { + VLOG(6) << "Entering " << __FUNCTION__; + + return folly::to("port", pPlatformPort_->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..c7fae0d7d800a --- /dev/null +++ b/fboss/agent/hw/sai/SaiPortBase.h @@ -0,0 +1,121 @@ +/* + * 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 pSwitch, pointer to SaiSwitch object + * @param saiPortId, sai_port_id_t port ID + * @param fbossPortId, fboss port ID + * @param pPlatform, pointer to SaiPlatformPort object + */ + SaiPortBase(SaiSwitch *pSwitch, sai_object_id_t saiPortId, PortID fbossPortId, SaiPlatformPort *pPlatformPort); + virtual ~SaiPortBase(); + + /** + * @brief Initialize port + * @param warmBoot, switch boot type + */ + void Init(bool warmBoot); + + /* + * Getters. + */ + SaiPlatformPort *GetPlatformPort() const { + return pPlatformPort_; + } + + SaiSwitch *GetHwSwitch() const { + return pHw_; + } + + sai_object_id_t GetSaiPortId() const { + return saiPortId_; + } + + PortID GetFbossPortId() const { + return fbossPortId_; + } + + VlanID GetIngressVlan() { + return pvId_; + } + /* + * Setters. + */ + 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 pHw_ { + nullptr + }; // Pointer to HW Switch + + SaiPlatformPort *const pPlatformPort_ { + nullptr + }; // Pointer to Platform port + + sai_object_id_t saiPortId_ {0}; + PortID fbossPortId_ {0}; + VlanID pvId_ {0}; + bool linkStatus_ {true}; + bool initDone_ {false}; + + sai_port_api_t *pSaiPortApi_ { 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..fd5c2e5997d9e --- /dev/null +++ b/fboss/agent/hw/sai/SaiPortTable.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 "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 *pSwitch) + :pHw_(pSwitch) { + VLOG(4) << "Entering " << __FUNCTION__; +} + +SaiPortTable::~SaiPortTable() { + VLOG(4) << "Entering " << __FUNCTION__; +} + +void SaiPortTable::InitPorts(bool warmBoot) { + VLOG(4) << "Entering " << __FUNCTION__; + + auto platformPorts = pHw_->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(pHw_, 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()) { + throw SaiError("Cannot find the SAI port object for SAI port ", id); + } + + 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..430ec0ef14834 --- /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 *pSwitch); + 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 *pHw_ {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/SaiPortTests.cpp b/fboss/agent/hw/sai/SaiPortTests.cpp new file mode 100644 index 0000000000000..8b2f71a417bff --- /dev/null +++ b/fboss/agent/hw/sai/SaiPortTests.cpp @@ -0,0 +1,359 @@ +/* + * 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/ApplyThriftConfig.h" +#include "fboss/agent/FbossError.h" +#include "fboss/agent/state/DeltaFunctions.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/hw/sai/SaiTestUtils.h" +#include "fboss/agent/platforms/sai/SaiPlatform.h" + +#include + +using namespace facebook::fboss; +using std::make_pair; +using std::make_shared; +using std::shared_ptr; + +TEST(Port, applyConfig) { + SaiPlatform platform; + auto stateV0 = make_shared(); + stateV0->registerPort(PortID(1), "port1"); + auto portV0 = stateV0->getPort(PortID(1)); + EXPECT_EQ(0, portV0->getGeneration()); + EXPECT_FALSE(portV0->isPublished()); + EXPECT_EQ(PortID(1), portV0->getID()); + EXPECT_EQ("port1", portV0->getName()); + EXPECT_EQ(cfg::PortState::DOWN, portV0->getState()); + Port::VlanMembership emptyVlans; + EXPECT_EQ(emptyVlans, portV0->getVlans()); + + portV0->publish(); + EXPECT_TRUE(portV0->isPublished()); + + cfg::SwitchConfig config; + config.ports.resize(1); + config.ports[0].logicalID = 1; + config.ports[0].state = cfg::PortState::UP; + config.vlans.resize(2); + config.vlans[0].id = 2; + config.vlans[1].id = 5; + config.vlanPorts.resize(2); + config.vlanPorts[0].logicalPort = 1; + config.vlanPorts[0].vlanID = 2; + config.vlanPorts[0].emitTags = false; + config.vlanPorts[1].logicalPort = 1; + config.vlanPorts[1].vlanID = 5; + config.vlanPorts[1].emitTags = true; + + auto stateV1 = publishAndApplyConfig(stateV0, &config, &platform); + auto portV1 = stateV1->getPort(PortID(1)); + ASSERT_NE(nullptr, portV1); + EXPECT_NE(portV0, portV1); + + EXPECT_EQ(PortID(1), portV1->getID()); + EXPECT_EQ("port1", portV1->getName()); + EXPECT_EQ(1, portV1->getGeneration()); + EXPECT_EQ(cfg::PortState::UP, portV1->getState()); + EXPECT_FALSE(portV1->isPublished()); + Port::VlanMembership expectedVlans; + expectedVlans.insert(make_pair(VlanID(2), Port::VlanInfo(false))); + expectedVlans.insert(make_pair(VlanID(5), Port::VlanInfo(true))); + EXPECT_EQ(expectedVlans, portV1->getVlans()); + + // Applying the same config again should result in no changes + EXPECT_EQ(nullptr, publishAndApplyConfig(stateV1, &config, &platform)); + + // Applying the same config with a new VLAN list should result in changes + config.vlanPorts.resize(1); + config.vlanPorts[0].logicalPort = 1; + config.vlanPorts[0].vlanID = 2021; + config.vlanPorts[0].emitTags = false; + + Port::VlanMembership expectedVlansV2; + expectedVlansV2.insert(make_pair(VlanID(2021), Port::VlanInfo(false))); + auto stateV2 = publishAndApplyConfig(stateV1, &config, &platform); + auto portV2 = stateV2->getPort(PortID(1)); + ASSERT_NE(nullptr, portV2); + EXPECT_NE(portV1, portV2); + + EXPECT_EQ(PortID(1), portV2->getID()); + EXPECT_EQ("port1", portV2->getName()); + EXPECT_EQ(2, portV2->getGeneration()); + EXPECT_EQ(cfg::PortState::UP, portV2->getState()); + EXPECT_FALSE(portV2->isPublished()); + EXPECT_EQ(expectedVlansV2, portV2->getVlans()); + + // Applying the same config with a different speed should result in changes + config.ports[0].speed = cfg::PortSpeed::GIGE; + + auto stateV3 = publishAndApplyConfig(stateV2, &config, &platform); + auto portV3 = stateV3->getPort(PortID(1)); + ASSERT_NE(nullptr, portV3); + EXPECT_NE(portV2, portV3); + EXPECT_EQ(cfg::PortSpeed::GIGE, portV3->getSpeed()); + + // Attempting to apply a config with a non-existent PortID should fail. + config.ports[0].logicalID = 2; + EXPECT_THROW(publishAndApplyConfig(stateV3, &config, &platform), FbossError); +} + +TEST(Port, initDefaultConfig) { + SaiPlatform platform; + PortID portID(1); + auto state = make_shared(); + state->registerPort(portID, "port1"); + + // Applying an empty config should result in no changes. + cfg::SwitchConfig config; + EXPECT_EQ(nullptr, publishAndApplyConfig(state, &config, &platform)); + + // Adding a port entry in the config and initializing it with + // initDefaultConfig() should also result in no changes. + config.ports.resize(1); + state->getPort(portID)->initDefaultConfig(&config.ports[0]); + EXPECT_EQ(nullptr, publishAndApplyConfig(state, &config, &platform)); +} + +TEST(PortMap, registerPorts) { + auto ports = make_shared(); + EXPECT_EQ(0, ports->getGeneration()); + EXPECT_FALSE(ports->isPublished()); + EXPECT_EQ(0, ports->numPorts()); + + ports->registerPort(PortID(1), "port1"); + ports->registerPort(PortID(2), "port2"); + ports->registerPort(PortID(3), "port3"); + ports->registerPort(PortID(4), "port4"); + EXPECT_EQ(4, ports->numPorts()); + + auto port1 = ports->getPort(PortID(1)); + auto port2 = ports->getPort(PortID(2)); + auto port3 = ports->getPort(PortID(3)); + auto port4 = ports->getPort(PortID(4)); + EXPECT_EQ(PortID(1), port1->getID()); + EXPECT_EQ("port1", port1->getName()); + EXPECT_EQ(PortID(4), port4->getID()); + EXPECT_EQ("port4", port4->getName()); + + // Attempting to register a duplicate port ID should fail + EXPECT_THROW(ports->registerPort(PortID(2), "anotherPort2"), + FbossError); + + // Registering non-sequential IDs should work + ports->registerPort(PortID(10), "port10"); + EXPECT_EQ(5, ports->numPorts()); + auto port10 = ports->getPort(PortID(10)); + EXPECT_EQ(PortID(10), port10->getID()); + EXPECT_EQ("port10", port10->getName()); + + // Getting non-existent ports should fail + EXPECT_THROW(ports->getPort(PortID(0)), FbossError); + EXPECT_THROW(ports->getPort(PortID(7)), FbossError); + EXPECT_THROW(ports->getPort(PortID(300)), FbossError); + + // Publishing the PortMap should also mark all ports as published + ports->publish(); + EXPECT_TRUE(ports->isPublished()); + EXPECT_TRUE(port1->isPublished()); + EXPECT_TRUE(port2->isPublished()); + EXPECT_TRUE(port3->isPublished()); + EXPECT_TRUE(port4->isPublished()); + EXPECT_TRUE(port10->isPublished()); + + // Attempting to register new ports after the PortMap has been published + // should crash. + ASSERT_DEATH(ports->registerPort(PortID(5), "port5"), + "Check failed: !isPublished()"); +} + +/* + * Test that forEachChanged(StateDelta::getPortsDelta(), ...) invokes the + * callback for the specified list of changed ports. + */ +void checkChangedPorts(const shared_ptr &oldPorts, + const shared_ptr &newPorts, + const std::set changedIDs) { + auto oldState = make_shared(); + oldState->resetPorts(oldPorts); + auto newState = make_shared(); + newState->resetPorts(newPorts); + + std::set invokedPorts; + StateDelta delta(oldState, newState); + DeltaFunctions::forEachChanged(delta.getPortsDelta(), + [&] (const shared_ptr &oldPort, + const shared_ptr &newPort) { + EXPECT_EQ(oldPort->getID(), newPort->getID()); + EXPECT_NE(oldPort, newPort); + + auto ret = invokedPorts.insert(oldPort->getID()); + EXPECT_TRUE(ret.second); + }); + + EXPECT_EQ(changedIDs, invokedPorts); +} + +TEST(PortMap, applyConfig) { + SaiPlatform platform; + auto stateV0 = make_shared(); + auto portsV0 = stateV0->getPorts(); + portsV0->registerPort(PortID(1), "port1"); + portsV0->registerPort(PortID(2), "port2"); + portsV0->registerPort(PortID(3), "port3"); + portsV0->registerPort(PortID(4), "port4"); + portsV0->publish(); + EXPECT_EQ(0, portsV0->getGeneration()); + auto port1 = portsV0->getPort(PortID(1)); + auto port2 = portsV0->getPort(PortID(2)); + auto port3 = portsV0->getPort(PortID(3)); + auto port4 = portsV0->getPort(PortID(4)); + + // Applying an empty config shouldn't change a newly-constructed PortMap + cfg::SwitchConfig config; + EXPECT_EQ(nullptr, publishAndApplyConfig(stateV0, &config, &platform)); + + // Enable port 2 + config.ports.resize(1); + config.ports[0].logicalID = 2; + config.ports[0].state = cfg::PortState::UP; + auto stateV1 = publishAndApplyConfig(stateV0, &config, &platform); + auto portsV1 = stateV1->getPorts(); + ASSERT_NE(nullptr, portsV1); + EXPECT_EQ(1, portsV1->getGeneration()); + EXPECT_EQ(4, portsV1->numPorts()); + + // Only port 2 should have changed + EXPECT_EQ(port1, portsV1->getPort(PortID(1))); + EXPECT_NE(port2, portsV1->getPort(PortID(2))); + EXPECT_EQ(port3, portsV1->getPort(PortID(3))); + EXPECT_EQ(port4, portsV1->getPort(PortID(4))); + checkChangedPorts(portsV0, portsV1, {2}); + + auto newPort2 = portsV1->getPort(PortID(2)); + EXPECT_EQ(cfg::PortState::UP, newPort2->getState()); + EXPECT_EQ(cfg::PortState::DOWN, port1->getState()); + EXPECT_EQ(cfg::PortState::DOWN, port3->getState()); + EXPECT_EQ(cfg::PortState::DOWN, port4->getState()); + + // The new PortMap and port 2 should still be unpublished. + // The remaining other ports are the same and were previously published + EXPECT_FALSE(portsV1->isPublished()); + EXPECT_FALSE(newPort2->isPublished()); + EXPECT_TRUE(port1->isPublished()); + // Publish portsV1 now. + portsV1->publish(); + EXPECT_TRUE(portsV1->isPublished()); + EXPECT_TRUE(newPort2->isPublished()); + EXPECT_TRUE(port1->isPublished()); + + // Applying the same config again should do nothing. + EXPECT_EQ(nullptr, publishAndApplyConfig(stateV1, &config, &platform)); + + // Now mark all ports up + config.ports.resize(4); + config.ports[0].logicalID = 1; + config.ports[0].state = cfg::PortState::UP; + config.ports[1].logicalID = 2; + config.ports[1].state = cfg::PortState::UP; + config.ports[2].logicalID = 3; + config.ports[2].state = cfg::PortState::UP; + config.ports[3].logicalID = 4; + config.ports[3].state = cfg::PortState::UP; + + auto stateV2 = publishAndApplyConfig(stateV1, &config, &platform); + auto portsV2 = stateV2->getPorts(); + ASSERT_NE(nullptr, portsV2); + EXPECT_EQ(2, portsV2->getGeneration()); + + EXPECT_NE(port1, portsV2->getPort(PortID(1))); + EXPECT_EQ(newPort2, portsV2->getPort(PortID(2))); + EXPECT_NE(port3, portsV2->getPort(PortID(3))); + EXPECT_NE(port4, portsV2->getPort(PortID(4))); + + EXPECT_EQ(cfg::PortState::UP, portsV2->getPort(PortID(1))->getState()); + EXPECT_EQ(cfg::PortState::UP, portsV2->getPort(PortID(2))->getState()); + EXPECT_EQ(cfg::PortState::UP, portsV2->getPort(PortID(3))->getState()); + EXPECT_EQ(cfg::PortState::UP, portsV2->getPort(PortID(4))->getState()); + checkChangedPorts(portsV1, portsV2, {1, 3, 4}); + + EXPECT_FALSE(portsV2->getPort(PortID(1))->isPublished()); + EXPECT_TRUE(portsV2->getPort(PortID(2))->isPublished()); + EXPECT_FALSE(portsV2->getPort(PortID(3))->isPublished()); + EXPECT_FALSE(portsV2->getPort(PortID(4))->isPublished()); + portsV2->publish(); + EXPECT_TRUE(portsV2->getPort(PortID(1))->isPublished()); + EXPECT_TRUE(portsV2->getPort(PortID(2))->isPublished()); + EXPECT_TRUE(portsV2->getPort(PortID(3))->isPublished()); + EXPECT_TRUE(portsV2->getPort(PortID(4))->isPublished()); + + // Applying a config with ports that don't already exist should fail + config.ports[0].logicalID = 10; + config.ports[0].state = cfg::PortState::UP; + EXPECT_THROW(publishAndApplyConfig(stateV2, &config, &platform), FbossError); + + // If we remove port3 from the config, it should be marked down + config.ports.resize(3); + config.ports[0].logicalID = 1; + config.ports[2].logicalID = 4; + auto stateV3 = publishAndApplyConfig(stateV2, &config, &platform); + auto portsV3 = stateV3->getPorts(); + ASSERT_NE(nullptr, portsV3); + EXPECT_EQ(3, portsV3->getGeneration()); + + EXPECT_EQ(4, portsV3->numPorts()); + EXPECT_EQ(cfg::PortState::UP, portsV3->getPort(PortID(1))->getState()); + EXPECT_EQ(cfg::PortState::UP, portsV3->getPort(PortID(2))->getState()); + EXPECT_EQ(cfg::PortState::DOWN, portsV3->getPort(PortID(3))->getState()); + EXPECT_EQ(cfg::PortState::UP, portsV3->getPort(PortID(4))->getState()); + checkChangedPorts(portsV2, portsV3, {3}); +} + +TEST(PortMap, iterateOrder) { + // The NodeMapDelta::Iterator code assumes that the PortMap iterator walks + // through the ports in sorted order (sorted by PortID). + // + // Add a test to ensure that this always remains true. (If we ever change + // the underlying map data structure used for PortMap, we will need to update + // the StateDelta code.) + auto ports = make_shared(); + ports->registerPort(PortID(99), "a"); + ports->registerPort(PortID(37), "b"); + ports->registerPort(PortID(88), "c"); + ports->registerPort(PortID(4), "d"); + ports->publish(); + + auto it = ports->begin(); + ASSERT_NE(ports->end(), it); + EXPECT_EQ(PortID(4), (*it)->getID()); + EXPECT_EQ("d", (*it)->getName()); + + ++it; + ASSERT_NE(ports->end(), it); + EXPECT_EQ(PortID(37), (*it)->getID()); + EXPECT_EQ("b", (*it)->getName()); + + ++it; + ASSERT_NE(ports->end(), it); + EXPECT_EQ(PortID(88), (*it)->getID()); + EXPECT_EQ("c", (*it)->getName()); + + ++it; + ASSERT_NE(ports->end(), it); + EXPECT_EQ(PortID(99), (*it)->getID()); + EXPECT_EQ("a", (*it)->getName()); + + ++it; + EXPECT_EQ(ports->end(), it); +} diff --git a/fboss/agent/hw/sai/SaiRoute.cpp b/fboss/agent/hw/sai/SaiRoute.cpp new file mode 100644 index 0000000000000..949c16f77a7f6 --- /dev/null +++ b/fboss/agent/hw/sai/SaiRoute.cpp @@ -0,0 +1,226 @@ +/* + * 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); + } + + pSaiRouteApi_ = hw->GetSaiRouteApi(); +} + +SaiRoute::~SaiRoute() { + VLOG(4) << "Entering " << __FUNCTION__; + + if(!added_) { + return; + } + + sai_status_t saiRetVal = pSaiRouteApi_->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 = pSaiRouteApi_->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: + routeAttr.value.u32 = SAI_PACKET_ACTION_DROP; + break; + + case RouteForwardAction::TO_CPU: + 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 = pSaiRouteApi_->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(); + + // Set next hop ID route attribute in SAI + saiRetVal = pSaiRouteApi_->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); + } + } + + fwd_ = fwd; +} + +}} // facebook::fboss diff --git a/fboss/agent/hw/sai/SaiRoute.h b/fboss/agent/hw/sai/SaiRoute.h new file mode 100644 index 0000000000000..331fd05ec237f --- /dev/null +++ b/fboss/agent/hw/sai/SaiRoute.h @@ -0,0 +1,75 @@ +/* + * 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); + +private: + SaiRoute(const SaiRoute &rhs); + SaiRoute &operator=(const SaiRoute &rhs); + + const SaiSwitch *hw_; + RouterID vrf_; + folly::IPAddress ipAddr_; + uint8_t prefixLen_; + RouteForwardInfo fwd_; + + sai_unicast_route_entry_t routeEntry_; + bool added_ {false}; + bool isLocal_ {false}; + + sai_route_api_t *pSaiRouteApi_ { 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..82ec8effb53f9 --- /dev/null +++ b/fboss/agent/hw/sai/SaiRouteTable.cpp @@ -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. + * + */ +#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)); + } + + ret.first->second->Program(route->getForwardInfo()); +} + +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()); + } + + fib_.erase(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..b4c1f04ad6775 --- /dev/null +++ b/fboss/agent/hw/sai/SaiRouteTable.h @@ -0,0 +1,55 @@ +/* + * 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); +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_; + +}; + +}} // facebook::fboss diff --git a/fboss/agent/hw/sai/SaiRouteTests.cpp b/fboss/agent/hw/sai/SaiRouteTests.cpp new file mode 100644 index 0000000000000..d66d09e84907e --- /dev/null +++ b/fboss/agent/hw/sai/SaiRouteTests.cpp @@ -0,0 +1,1006 @@ +/* + * 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/FbossError.h" +#include "fboss/agent/ApplyThriftConfig.h" +#include "fboss/agent/state/DeltaFunctions.h" +#include "fboss/agent/state/Interface.h" +#include "fboss/agent/state/InterfaceMap.h" +#include "fboss/agent/state/Route.h" +#include "fboss/agent/state/RouteTableRib.h" +#include "fboss/agent/state/RouteTable.h" +#include "fboss/agent/state/RouteUpdater.h" +#include "fboss/agent/state/RouteTableMap.h" +#include "fboss/agent/state/RouteDelta.h" +#include "fboss/agent/state/NodeMapDelta.h" +#include "fboss/agent/state/StateDelta.h" +#include "fboss/agent/state/SwitchState.h" +#include "fboss/agent/hw/sai/SaiTestUtils.h" +#include "fboss/agent/platforms/sai/SaiPlatform.h" +#include "fboss/agent/gen-cpp/switch_config_types.h" + +#include + +using namespace facebook::fboss; +using folly::IPAddress; +using folly::IPAddressV4; +using folly::IPAddressV6; +using std::make_shared; +using std::shared_ptr; +using ::testing::Return; + +TEST(RouteUpdater, dedup) { + SaiPlatform platform; + auto stateV0 = make_shared(); + auto tablesV0 = stateV0->getRouteTables(); + + cfg::SwitchConfig config; + config.vlans.resize(1); + config.vlans[0].id = 1; + + config.interfaces.resize(2); + config.interfaces[0].intfID = 1; + config.interfaces[0].vlanID = 1; + config.interfaces[0].routerID = 0; + config.interfaces[0].__isset.mac = true; + config.interfaces[0].mac = "00:00:00:00:00:11"; + config.interfaces[0].ipAddresses.resize(2); + config.interfaces[0].ipAddresses[0] = "1.1.1.1/24"; + config.interfaces[0].ipAddresses[1] = "1::1/48"; + config.interfaces[1].intfID = 2; + config.interfaces[1].vlanID = 1; + config.interfaces[1].routerID = 0; + config.interfaces[1].__isset.mac = true; + config.interfaces[1].mac = "00:00:00:00:00:22"; + config.interfaces[1].ipAddresses.resize(2); + config.interfaces[1].ipAddresses[0] = "2.2.2.2/24"; + config.interfaces[1].ipAddresses[1] = "2::1/48"; + + auto stateV1 = publishAndApplyConfig(stateV0, &config, &platform); + ASSERT_NE(nullptr, stateV1); + stateV1->publish(); + auto rid = RouterID(0); + // 2 different nexthops + RouteNextHops nhop1; + nhop1.emplace(IPAddress("1.1.1.10")); // resolved by intf 1 + RouteNextHops nhop2; + nhop2.emplace(IPAddress("2.2.2.10")); // resolved by intf 2 + // 4 prefixes + RouteV4::Prefix r1 {IPAddressV4("10.1.1.0"), 24}; + RouteV4::Prefix r2 {IPAddressV4("20.1.1.0"), 24}; + RouteV6::Prefix r3 {IPAddressV6("1001::0"), 48}; + RouteV6::Prefix r4 {IPAddressV6("2001::0"), 48}; + + const auto &tables1 = stateV1->getRouteTables(); + RouteUpdater u2(tables1); + u2.addRoute(rid, IPAddress(r1.network), r1.mask, nhop1); + u2.addRoute(rid, IPAddress(r2.network), r2.mask, nhop2); + u2.addRoute(rid, IPAddress(r3.network), r3.mask, nhop1); + u2.addRoute(rid, IPAddress(r4.network), r4.mask, nhop2); + auto tables2 = u2.updateDone(); + ASSERT_NE(nullptr, tables2); + tables2->publish(); + + // start from empty table now, and then re-add the same routes + RouteUpdater u3(tables2, true); + u3.addInterfaceAndLinkLocalRoutes(stateV1->getInterfaces()); + u3.addRoute(rid, IPAddress(r1.network), r1.mask, nhop1); + u3.addRoute(rid, IPAddress(r2.network), r2.mask, nhop2); + u3.addRoute(rid, IPAddress(r3.network), r3.mask, nhop1); + u3.addRoute(rid, IPAddress(r4.network), r4.mask, nhop2); + auto tables3 = u3.updateDone(); + EXPECT_EQ(nullptr, tables3); + + // start from empty table now, and then re-add the same routes, + // except for one difference + RouteUpdater u4(tables2, true); + u4.addInterfaceAndLinkLocalRoutes(stateV1->getInterfaces()); + u4.addRoute(rid, IPAddress(r1.network), r1.mask, nhop1); + u4.addRoute(rid, IPAddress(r2.network), r2.mask, nhop1); // different nexthop + u4.addRoute(rid, IPAddress(r3.network), r3.mask, nhop1); + u4.addRoute(rid, IPAddress(r4.network), r4.mask, nhop2); + auto tables4 = u4.updateDone(); + ASSERT_NE(nullptr, tables4); + tables4->publish(); + + // get all 4 routes from table2 + auto t2 = tables2->getRouteTableIf(rid); + ASSERT_NE(nullptr, t2); + auto rib24 = t2->getRibV4(); + ASSERT_NE(nullptr, rib24); + auto t2r1 = rib24->exactMatch(r1); + auto t2r2 = rib24->exactMatch(r2); + auto rib26 = t2->getRibV6(); + ASSERT_NE(nullptr, rib26); + auto t2r3 = rib26->exactMatch(r3); + auto t2r4 = rib26->exactMatch(r4); + ASSERT_NE(nullptr, t2r1); + ASSERT_NE(nullptr, t2r2); + ASSERT_NE(nullptr, t2r3); + ASSERT_NE(nullptr, t2r4); + + // get all 4 routes from table4 + auto t4 = tables4->getRouteTableIf(rid); + ASSERT_NE(nullptr, t4); + auto rib44 = t4->getRibV4(); + ASSERT_NE(nullptr, rib44); + auto t4r1 = rib44->exactMatch(r1); + auto t4r2 = rib44->exactMatch(r2); + auto rib46 = t4->getRibV6(); + ASSERT_NE(nullptr, rib46); + auto t4r3 = rib46->exactMatch(r3); + auto t4r4 = rib46->exactMatch(r4); + ASSERT_NE(nullptr, t4r1); + ASSERT_NE(nullptr, t4r2); + ASSERT_NE(nullptr, t4r3); + ASSERT_NE(nullptr, t4r4); + + EXPECT_EQ(t2r1, t4r1); + EXPECT_NE(t2r2, t4r2); // different routes + EXPECT_EQ(t2r2->getGeneration() + 1, t4r2->getGeneration()); + EXPECT_EQ(t2r3, t4r3); + EXPECT_EQ(t2r4, t4r4); +} + +TEST(Route, resolve) { + SaiPlatform platform; + auto stateV0 = make_shared(); + auto tablesV0 = stateV0->getRouteTables(); + + cfg::SwitchConfig config; + config.vlans.resize(1); + config.vlans[0].id = 1; + + config.interfaces.resize(2); + config.interfaces[0].intfID = 1; + config.interfaces[0].vlanID = 1; + config.interfaces[0].routerID = 0; + config.interfaces[0].__isset.mac = true; + config.interfaces[0].mac = "00:00:00:00:00:11"; + config.interfaces[0].ipAddresses.resize(2); + config.interfaces[0].ipAddresses[0] = "1.1.1.1/24"; + config.interfaces[0].ipAddresses[1] = "1::1/48"; + config.interfaces[1].intfID = 2; + config.interfaces[1].vlanID = 1; + config.interfaces[1].routerID = 0; + config.interfaces[1].__isset.mac = true; + config.interfaces[1].mac = "00:00:00:00:00:22"; + config.interfaces[1].ipAddresses.resize(2); + config.interfaces[1].ipAddresses[0] = "2.2.2.2/24"; + config.interfaces[1].ipAddresses[1] = "2::1/48"; + + auto stateV1 = publishAndApplyConfig(stateV0, &config, &platform); + stateV1->publish(); + ASSERT_NE(nullptr, stateV1); + + auto rid = RouterID(0); + // recursive lookup + { + RouteUpdater u1(stateV1->getRouteTables()); + RouteNextHops nexthops1; + nexthops1.emplace(IPAddress("1.1.1.10")); // resolved by intf 1 + u1.addRoute(rid, IPAddress("1.1.3.0"), 24, nexthops1); + RouteNextHops nexthops2; + nexthops2.emplace(IPAddress("1.1.3.10")); // resolved by '1.1.3/24' + u1.addRoute(rid, IPAddress("8.8.8.0"), 24, nexthops2); + auto tables2 = u1.updateDone(); + ASSERT_NE(nullptr, tables2); + tables2->publish(); + auto t2 = tables2->getRouteTableIf(rid); + ASSERT_NE(nullptr, t2); + auto rib2 = t2->getRibV4(); + RouteV4::Prefix p2 {IPAddressV4("1.1.3.0"), 24}; + auto r21 = rib2->exactMatch(p2); + ASSERT_NE(nullptr, r21); + EXPECT_TRUE(r21->isResolved()); + EXPECT_FALSE(r21->isUnresolvable()); + EXPECT_FALSE(r21->isConnected()); + EXPECT_TRUE(r21->isWithNexthops()); + EXPECT_FALSE(r21->needResolve()); + p2 = RouteV4::Prefix {IPAddressV4("8.8.8.0"), 24}; + auto r22 = rib2->exactMatch(p2); + ASSERT_NE(nullptr, r22); + EXPECT_TRUE(r22->isResolved()); + EXPECT_FALSE(r22->isUnresolvable()); + EXPECT_FALSE(r22->isConnected()); + EXPECT_FALSE(r22->needResolve()); + // r21 and r22 are different routes + EXPECT_NE(r21, r22); + EXPECT_NE(r21->prefix(), r22->prefix()); + // check the forwarding info + RouteForwardNexthops expFwd2; + expFwd2.emplace(InterfaceID(1), IPAddress("1.1.1.10")); + EXPECT_EQ(expFwd2, r21->getForwardInfo().getNexthops()); + EXPECT_EQ(expFwd2, r22->getForwardInfo().getNexthops()); + } + // recursive lookup loop + { + // create a route table w/ the following 3 routes + // 1. 30/8 -> 20.1.1.1 + // 2. 20/8 -> 10.1.1.1 + // 3. 10/8 -> 30.1.1.1 + // The above 3 routes causes lookup loop, which should result in + // all unresolvable. + RouteUpdater u1(stateV1->getRouteTables()); + RouteNextHops nexthops1; + nexthops1.emplace(IPAddress("20.1.1.1")); + u1.addRoute(rid, IPAddress("30.0.0.0"), 8, nexthops1); + RouteNextHops nexthops2; + nexthops2.emplace(IPAddress("10.1.1.1")); + u1.addRoute(rid, IPAddress("20.0.0.0"), 8, nexthops2); + RouteNextHops nexthops3; + nexthops3.emplace(IPAddress("30.1.1.1")); + u1.addRoute(rid, IPAddress("10.0.0.0"), 8, nexthops3); + auto tables2 = u1.updateDone(); + ASSERT_NE(nullptr, tables2); + tables2->publish(); + auto t2 = tables2->getRouteTableIf(rid); + ASSERT_NE(nullptr, t2); + auto rib2 = t2->getRibV4(); + auto verifyPrefix = [&](std::string network) { + RouteV4::Prefix prefix {IPAddressV4(network), 8}; + auto route = rib2->exactMatch(prefix); + ASSERT_NE(nullptr, route); + EXPECT_FALSE(route->isResolved()); + EXPECT_TRUE(route->isUnresolvable()); + EXPECT_FALSE(route->isConnected()); + EXPECT_TRUE(route->isWithNexthops()); + EXPECT_FALSE(route->needResolve()); + EXPECT_FALSE(route->isProcessing()); + }; + verifyPrefix("10.0.0.0"); + verifyPrefix("20.0.0.0"); + verifyPrefix("30.0.0.0"); + } + // recursive lookup across 2 updates + { + RouteUpdater u1(stateV1->getRouteTables()); + RouteNextHops nexthops1; + nexthops1.emplace(IPAddress("50.0.0.1")); + u1.addRoute(rid, IPAddress("40.0.0.0"), 8, nexthops1); + auto tables2 = u1.updateDone(); + ASSERT_NE(nullptr, tables2); + tables2->publish(); + auto t2 = tables2->getRouteTableIf(rid); + ASSERT_NE(nullptr, t2); + auto rib2 = t2->getRibV4(); + RouteV4::Prefix p1 {IPAddressV4("40.0.0.0"), 8}; + auto r21 = rib2->exactMatch(p1); + ASSERT_NE(nullptr, r21); + // 40.0.0.0/8 should be unresolved + EXPECT_FALSE(r21->isResolved()); + EXPECT_TRUE(r21->isUnresolvable()); + EXPECT_FALSE(r21->isConnected()); + EXPECT_FALSE(r21->needResolve()); + // Resolve 50.0.0.1 this should also resolve 40.0.0.0/8 + RouteNextHops nexthops2; + nexthops2.emplace(IPAddress("1.1.1.1")); // intf 1 + RouteUpdater u2(tables2); + u2.addRoute(rid, IPAddress("50.0.0.0"), 8, nexthops2); + auto tables3 = u2.updateDone(); + ASSERT_NE(nullptr, tables3); + tables3->publish(); + auto t3 = tables3->getRouteTableIf(rid); + ASSERT_NE(nullptr, t3); + auto rib3 = t3->getRibV4(); + auto r31 = rib3->exactMatch(p1); + ASSERT_NE(nullptr, r31); + // 40.0.0.0/8 should be resolved + EXPECT_TRUE(r31->isResolved()); + EXPECT_FALSE(r31->isUnresolvable()); + EXPECT_FALSE(r31->isConnected()); + EXPECT_FALSE(r31->needResolve()); + auto r31NextHops = r31->nexthops(); + EXPECT_EQ(1, r31NextHops.size()); + auto r32 = rib3->longestMatch(r31NextHops.begin()->asV4()); + // 50.0.0.1/32 should be resolved + ASSERT_NE(nullptr, r32); + EXPECT_TRUE(r32->isResolved()); + EXPECT_FALSE(r32->isUnresolvable()); + EXPECT_FALSE(r32->isConnected()); + EXPECT_FALSE(r32->needResolve()); + RouteV4::Prefix p2 {IPAddressV4("50.0.0.0"), 8}; + auto r33 = rib3->exactMatch(p2); + ASSERT_NE(nullptr, r33); + // 50.0.0.0/8 should be resolved + EXPECT_TRUE(r33->isResolved()); + EXPECT_FALSE(r33->isUnresolvable()); + EXPECT_FALSE(r33->isConnected()); + EXPECT_FALSE(r33->needResolve()); + + } +} + +// Testing add and delete ECMP routes +TEST(Route, addDel) { + SaiPlatform platform; + auto stateV0 = make_shared(); + auto tablesV0 = stateV0->getRouteTables(); + + cfg::SwitchConfig config; + config.vlans.resize(1); + config.vlans[0].id = 1; + + config.interfaces.resize(2); + config.interfaces[0].intfID = 1; + config.interfaces[0].vlanID = 1; + config.interfaces[0].routerID = 0; + config.interfaces[0].__isset.mac = true; + config.interfaces[0].mac = "00:00:00:00:00:11"; + config.interfaces[0].ipAddresses.resize(2); + config.interfaces[0].ipAddresses[0] = "1.1.1.1/24"; + config.interfaces[0].ipAddresses[1] = "1::1/48"; + config.interfaces[1].intfID = 2; + config.interfaces[1].vlanID = 1; + config.interfaces[1].routerID = 0; + config.interfaces[1].__isset.mac = true; + config.interfaces[1].mac = "00:00:00:00:00:22"; + config.interfaces[1].ipAddresses.resize(2); + config.interfaces[1].ipAddresses[0] = "2.2.2.2/24"; + config.interfaces[1].ipAddresses[1] = "2::1/48"; + + auto stateV1 = publishAndApplyConfig(stateV0, &config, &platform); + ASSERT_NE(nullptr, stateV1); + stateV1->publish(); + + auto rid = RouterID(0); + RouteNextHops nexthops; + nexthops.emplace(IPAddress("1.1.1.10")); // resolved by intf 1 + nexthops.emplace(IPAddress("2::2")); // resolved by intf 2 + nexthops.emplace(IPAddress("1.1.2.10")); // un-resolvable + RouteNextHops nexthops2; + nexthops2.emplace(IPAddress("1.1.3.10")); // un-resolvable + nexthops2.emplace(IPAddress("11:11::1")); // un-resolvable + + RouteUpdater u1(stateV1->getRouteTables()); + u1.addRoute(rid, IPAddress("10.1.1.1"), 24, nexthops); + u1.addRoute(rid, IPAddress("2001::1"), 48, nexthops); + auto tables2 = u1.updateDone(); + ASSERT_NE(nullptr, tables2); + tables2->publish(); + auto t2 = tables2->getRouteTableIf(rid); + ASSERT_NE(nullptr, t2); + // v4 route + auto rib2v4 = t2->getRibV4(); + RouteV4::Prefix p2 {IPAddressV4("10.1.1.0"), 24}; + auto r2 = rib2v4->exactMatch(p2); + ASSERT_NE(nullptr, r2); + EXPECT_TRUE(r2->isResolved()); + EXPECT_FALSE(r2->isDrop()); + EXPECT_FALSE(r2->isToCPU()); + EXPECT_FALSE(r2->isUnresolvable()); + EXPECT_FALSE(r2->isConnected()); + EXPECT_FALSE(r2->needResolve()); + // v6 route + auto rib2v6 = t2->getRibV6(); + RouteV6::Prefix p2v6 {IPAddressV6("2001::0"), 48}; + auto r2v6 = rib2v6->exactMatch(p2v6); + ASSERT_NE(nullptr, r2v6); + EXPECT_TRUE(r2v6->isResolved()); + EXPECT_FALSE(r2v6->isDrop()); + EXPECT_FALSE(r2v6->isToCPU()); + EXPECT_FALSE(r2v6->isUnresolvable()); + EXPECT_FALSE(r2v6->isConnected()); + EXPECT_FALSE(r2v6->needResolve()); + // forwarding info + EXPECT_EQ(RouteForwardAction::NEXTHOPS, r2->getForwardInfo().getAction()); + EXPECT_EQ(RouteForwardAction::NEXTHOPS, r2v6->getForwardInfo().getAction()); + const auto &fwd2 = r2->getForwardInfo().getNexthops(); + const auto &fwd2v6 = r2v6->getForwardInfo().getNexthops(); + EXPECT_EQ(2, fwd2.size()); + EXPECT_EQ(2, fwd2v6.size()); + RouteForwardNexthops expFwd2; + expFwd2.emplace(InterfaceID(1), IPAddress("1.1.1.10")); + expFwd2.emplace(InterfaceID(2), IPAddress("2::2")); + EXPECT_EQ(expFwd2, fwd2); + EXPECT_EQ(expFwd2, fwd2v6); + + // change the nexthops of the V4 route + RouteUpdater u2(tables2); + u2.addRoute(rid, IPAddress("10.1.1.1"), 24, nexthops2); + auto tables3 = u2.updateDone(); + ASSERT_NE(nullptr, tables3); + tables3->publish(); + auto t3 = tables3->getRouteTableIf(rid); + ASSERT_NE(nullptr, t3); + auto rib3v4 = t3->getRibV4(); + RouteV4::Prefix p3 {IPAddressV4("10.1.1.0"), 24}; + auto r3 = rib3v4->exactMatch(p3); + ASSERT_NE(nullptr, r3); + EXPECT_FALSE(r3->isResolved()); + EXPECT_TRUE(r3->isUnresolvable()); + EXPECT_FALSE(r3->isConnected()); + EXPECT_FALSE(r3->needResolve()); + + // re-add the same route does not cause change + RouteUpdater u3(tables3); + u3.addRoute(rid, IPAddress("10.1.1.1"), 24, nexthops2); + auto tables4 = u3.updateDone(); + EXPECT_EQ(nullptr, tables4); + + // now delete the V4 route + RouteUpdater u4(tables3); + u4.delRoute(rid, IPAddress("10.1.1.1"), 24); + auto tables5 = u4.updateDone(); + ASSERT_NE(nullptr, tables5); + tables5->publish(); + auto t5 = tables5->getRouteTableIf(rid); + ASSERT_NE(nullptr, t5); + auto rib5 = t5->getRibV4(); + RouteV4::Prefix p5 {IPAddressV4("10.1.1.0"), 24}; + auto r5 = rib5->exactMatch(p5); + EXPECT_EQ(nullptr, r5); + + // change an old route to punt to CPU, add a new route to DROP + RouteUpdater u5(tables3); + u5.addRoute(rid, IPAddress("10.1.1.0"), 24, RouteForwardAction::TO_CPU); + u5.addRoute(rid, IPAddress("10.1.2.0"), 24, RouteForwardAction::DROP); + auto tables6 = u5.updateDone(); + EXPECT_NE(nullptr, tables6); + auto t6 = tables6->getRouteTableIf(rid); + EXPECT_NE(nullptr, t6); + auto rib6 = t6->getRibV4(); + RouteV4::Prefix p6_1 {IPAddressV4("10.1.1.0"), 24}; + auto r6_1 = rib6->exactMatch(p6_1); + EXPECT_NE(nullptr, r6_1); + EXPECT_TRUE(r6_1->isResolved()); + EXPECT_FALSE(r6_1->isConnected()); + EXPECT_FALSE(r6_1->isWithNexthops()); + EXPECT_TRUE(r6_1->isToCPU()); + EXPECT_FALSE(r6_1->isDrop()); + EXPECT_EQ(RouteForwardAction::TO_CPU, r6_1->getForwardInfo().getAction()); + RouteV4::Prefix p6_2 {IPAddressV4("10.1.2.0"), 24}; + auto r6_2 = rib6->exactMatch(p6_2); + EXPECT_NE(nullptr, r6_2); + EXPECT_TRUE(r6_2->isResolved()); + EXPECT_FALSE(r6_2->isConnected()); + EXPECT_FALSE(r6_2->isWithNexthops()); + EXPECT_FALSE(r6_2->isToCPU()); + EXPECT_TRUE(r6_2->isDrop()); + EXPECT_EQ(RouteForwardAction::DROP, r6_2->getForwardInfo().getAction()); +} + +// Test interface routes +TEST(Route, Interface) { + SaiPlatform platform; + auto stateV0 = make_shared(); + auto tablesV0 = stateV0->getRouteTables(); + + cfg::SwitchConfig config; + config.vlans.resize(1); + config.vlans[0].id = 1; + + config.interfaces.resize(2); + config.interfaces[0].intfID = 1; + config.interfaces[0].vlanID = 1; + config.interfaces[0].routerID = 0; + config.interfaces[0].__isset.mac = true; + config.interfaces[0].mac = "00:00:00:00:00:11"; + config.interfaces[0].ipAddresses.resize(2); + config.interfaces[0].ipAddresses[0] = "1.1.1.1/24"; + config.interfaces[0].ipAddresses[1] = "1::1/48"; + config.interfaces[1].intfID = 2; + config.interfaces[1].vlanID = 1; + config.interfaces[1].routerID = 0; + config.interfaces[1].__isset.mac = true; + config.interfaces[1].mac = "00:00:00:00:00:22"; + config.interfaces[1].ipAddresses.resize(2); + config.interfaces[1].ipAddresses[0] = "2.2.2.2/24"; + config.interfaces[1].ipAddresses[1] = "2::1/48"; + + auto stateV1 = publishAndApplyConfig(stateV0, &config, &platform); + ASSERT_NE(nullptr, stateV1); + stateV1->publish(); + auto tablesV1 = stateV1->getRouteTables(); + EXPECT_NE(tablesV0, tablesV1); + EXPECT_EQ(1, tablesV1->getGeneration()); + EXPECT_EQ(1, tablesV1->size()); + + auto table1 = tablesV1->getRouteTableIf(RouterID(0)); + ASSERT_NE(nullptr, table1); + auto rib4 = table1->getRibV4(); + auto rib6 = table1->getRibV6(); + ASSERT_NE(nullptr, rib4); + ASSERT_NE(nullptr, rib6); + EXPECT_FALSE(rib4->empty()); + EXPECT_FALSE(rib6->empty()); + EXPECT_FALSE(table1->empty()); + EXPECT_EQ(2, rib4->size()); + EXPECT_EQ(3, rib6->size()); + // verify the ipv4 route + { + auto rt = rib4->exactMatch(RoutePrefixV4 {IPAddressV4("1.1.1.0"), 24}); + ASSERT_NE(nullptr, rt); + EXPECT_EQ(0, rt->getGeneration()); + EXPECT_TRUE(rt->isResolved()); + EXPECT_TRUE(rt->isConnected()); + EXPECT_FALSE(rt->isWithNexthops()); + EXPECT_FALSE(rt->isToCPU()); + EXPECT_FALSE(rt->isDrop()); + EXPECT_EQ(RouteForwardAction::NEXTHOPS, rt->getForwardInfo().getAction()); + const auto &fwds = rt->getForwardInfo().getNexthops(); + EXPECT_EQ(1, fwds.size()); + const auto &fwd = *fwds.begin(); + EXPECT_EQ(InterfaceID(1), fwd.intf); + EXPECT_EQ(IPAddress("1.1.1.1"), fwd.nexthop); + } + // verify the ipv6 route + { + auto rt = rib6->exactMatch(RoutePrefixV6 {IPAddressV6("2::0"), 48}); + ASSERT_NE(nullptr, rt); + EXPECT_EQ(0, rt->getGeneration()); + EXPECT_TRUE(rt->isResolved()); + EXPECT_TRUE(rt->isConnected()); + EXPECT_FALSE(rt->isWithNexthops()); + EXPECT_FALSE(rt->isToCPU()); + EXPECT_FALSE(rt->isDrop()); + EXPECT_EQ(RouteForwardAction::NEXTHOPS, rt->getForwardInfo().getAction()); + const auto &fwds = rt->getForwardInfo().getNexthops(); + EXPECT_EQ(1, fwds.size()); + const auto &fwd = *fwds.begin(); + EXPECT_EQ(InterfaceID(2), fwd.intf); + EXPECT_EQ(IPAddress("2::1"), fwd.nexthop); + } + + { + // verify the link local route + auto rt = rib6->exactMatch(RoutePrefixV6 {IPAddressV6("fe80::"), 64}); + ASSERT_NE(nullptr, rt); + EXPECT_EQ(0, rt->getGeneration()); + EXPECT_TRUE(rt->isResolved()); + EXPECT_FALSE(rt->isConnected()); + EXPECT_FALSE(rt->isWithNexthops()); + EXPECT_TRUE(rt->isToCPU()); + EXPECT_EQ(RouteForwardAction::TO_CPU, rt->getForwardInfo().getAction()); + const auto &fwds = rt->getForwardInfo().getNexthops(); + EXPECT_EQ(0, fwds.size()); + } + + // swap the interface addresses which causes route change + config.interfaces[1].ipAddresses[0] = "1.1.1.1/24"; + config.interfaces[1].ipAddresses[1] = "1::1/48"; + config.interfaces[0].ipAddresses[0] = "2.2.2.2/24"; + config.interfaces[0].ipAddresses[1] = "2::1/48"; + + auto stateV2 = publishAndApplyConfig(stateV1, &config, &platform); + ASSERT_NE(nullptr, stateV2); + stateV2->publish(); + auto tablesV2 = stateV2->getRouteTables(); + EXPECT_NE(tablesV1, tablesV2); + EXPECT_EQ(2, tablesV2->getGeneration()); + EXPECT_EQ(1, tablesV2->size()); + + auto table2 = tablesV2->getRouteTableIf(RouterID(0)); + ASSERT_NE(nullptr, table1); + auto rib4V2 = table2->getRibV4(); + auto rib6V2 = table2->getRibV6(); + ASSERT_NE(nullptr, rib4); + ASSERT_NE(nullptr, rib6); + EXPECT_NE(rib4, rib4V2); + EXPECT_NE(rib6, rib6V2); + EXPECT_FALSE(rib4V2->empty()); + EXPECT_FALSE(rib6V2->empty()); + EXPECT_FALSE(table1->empty()); + EXPECT_EQ(2, rib4V2->size()); + EXPECT_EQ(3, rib6V2->size()); + // verify the ipv4 route + { + auto rt = rib4V2->exactMatch(RoutePrefixV4 {IPAddressV4("1.1.1.0"), 24}); + ASSERT_NE(nullptr, rt); + EXPECT_EQ(1, rt->getGeneration()); + const auto &fwds = rt->getForwardInfo().getNexthops(); + EXPECT_EQ(1, fwds.size()); + const auto &fwd = *fwds.begin(); + EXPECT_EQ(InterfaceID(2), fwd.intf); + EXPECT_EQ(IPAddress("1.1.1.1"), fwd.nexthop); + } + // verify the ipv6 route + { + auto rt = rib6V2->exactMatch(RoutePrefixV6 {IPAddressV6("2::0"), 48}); + ASSERT_NE(nullptr, rt); + EXPECT_EQ(1, rt->getGeneration()); + const auto &fwds = rt->getForwardInfo().getNexthops(); + EXPECT_EQ(1, fwds.size()); + const auto &fwd = *fwds.begin(); + EXPECT_EQ(InterfaceID(1), fwd.intf); + EXPECT_EQ(IPAddress("2::1"), fwd.nexthop); + } +} + +namespace TEMP { +struct Route { + uint32_t vrf; + IPAddress prefix; + uint8_t len; + Route(uint32_t vrf, IPAddress prefix, uint8_t len) + : vrf(vrf), prefix(prefix), len(len) {} + bool operator<(const Route &rt) const { + if (vrf < rt.vrf) { + return true; + } else if (vrf > rt.vrf) { + return false; + } + + if (len < rt.len) { + return true; + } else if (len > rt.len) { + return false; + } + + return prefix < rt.prefix; + } + bool operator==(const Route &rt) const { + return vrf == rt.vrf && len == rt.len && prefix == rt.prefix; + } +}; +} + +void checkChangedRoute(const shared_ptr &oldTables, + const shared_ptr &newTables, + const std::set changedIDs, + const std::set addedIDs, + const std::set removedIDs) { + auto oldState = make_shared(); + oldState->resetRouteTables(oldTables); + auto newState = make_shared(); + newState->resetRouteTables(newTables); + + std::set foundChanged; + std::set foundAdded; + std::set foundRemoved; + StateDelta delta(oldState, newState); + + for (auto const& rtDelta : delta.getRouteTablesDelta()) { + RouterID id; + + if (!rtDelta.getOld()) { + id = rtDelta.getNew()->getID(); + } else { + id = rtDelta.getOld()->getID(); + } + + DeltaFunctions::forEachChanged( + rtDelta.getRoutesV4Delta(), + [&] (const shared_ptr &oldRt, + const shared_ptr &newRt) { + EXPECT_EQ(oldRt->prefix(), newRt->prefix()); + EXPECT_NE(oldRt, newRt); + const auto prefix = newRt->prefix(); + auto ret = foundChanged.insert( + TEMP::Route(id, IPAddress(prefix.network), prefix.mask)); + EXPECT_TRUE(ret.second); + }, + [&] (const shared_ptr &rt) { + const auto prefix = rt->prefix(); + auto ret = foundAdded.insert( + TEMP::Route(id, IPAddress(prefix.network), prefix.mask)); + EXPECT_TRUE(ret.second); + }, + [&] (const shared_ptr &rt) { + const auto prefix = rt->prefix(); + auto ret = foundRemoved.insert( + TEMP::Route(id, IPAddress(prefix.network), prefix.mask)); + EXPECT_TRUE(ret.second); + }); + DeltaFunctions::forEachChanged( + rtDelta.getRoutesV6Delta(), + [&] (const shared_ptr &oldRt, + const shared_ptr &newRt) { + EXPECT_EQ(oldRt->prefix(), newRt->prefix()); + EXPECT_NE(oldRt, newRt); + const auto prefix = newRt->prefix(); + auto ret = foundChanged.insert( + TEMP::Route(id, IPAddress(prefix.network), prefix.mask)); + EXPECT_TRUE(ret.second); + }, + [&] (const shared_ptr &rt) { + const auto prefix = rt->prefix(); + auto ret = foundAdded.insert( + TEMP::Route(id, IPAddress(prefix.network), prefix.mask)); + EXPECT_TRUE(ret.second); + }, + [&] (const shared_ptr &rt) { + const auto prefix = rt->prefix(); + auto ret = foundRemoved.insert( + TEMP::Route(id, IPAddress(prefix.network), prefix.mask)); + EXPECT_TRUE(ret.second); + }); + } + + EXPECT_EQ(changedIDs, foundChanged); + EXPECT_EQ(addedIDs, foundAdded); + EXPECT_EQ(removedIDs, foundRemoved); +} + +void checkChangedRouteTable(const shared_ptr &oldTables, + const shared_ptr &newTables, + const std::set changedIDs, + const std::set addedIDs, + const std::set removedIDs) { + auto oldState = make_shared(); + oldState->resetRouteTables(oldTables); + auto newState = make_shared(); + newState->resetRouteTables(newTables); + + std::set foundChanged; + std::set foundAdded; + std::set foundRemoved; + StateDelta delta(oldState, newState); + DeltaFunctions::forEachChanged( + delta.getRouteTablesDelta(), + [&] (const shared_ptr &oldTable, + const shared_ptr &newTable) { + EXPECT_EQ(oldTable->getID(), newTable->getID()); + EXPECT_NE(oldTable, newTable); + auto ret = foundChanged.insert(oldTable->getID()); + EXPECT_TRUE(ret.second); + }, + [&] (const shared_ptr &table) { + auto ret = foundAdded.insert(table->getID()); + EXPECT_TRUE(ret.second); + }, + [&] (const shared_ptr &table) { + auto ret = foundRemoved.insert(table->getID()); + EXPECT_TRUE(ret.second); + }); + + EXPECT_EQ(changedIDs, foundChanged); + EXPECT_EQ(addedIDs, foundAdded); + EXPECT_EQ(removedIDs, foundRemoved); +} + +TEST(RouteTableMap, applyConfig) { + SaiPlatform platform; + auto stateV0 = make_shared(); + auto tablesV0 = stateV0->getRouteTables(); + + cfg::SwitchConfig config; + config.vlans.resize(3); + config.vlans[0].id = 1; + config.vlans[1].id = 2; + config.vlans[2].id = 3; + config.interfaces.resize(2); + config.interfaces[0].intfID = 1; + config.interfaces[0].vlanID = 1; + config.interfaces[0].routerID = 0; + config.interfaces[0].__isset.mac = true; + config.interfaces[0].mac = "00:00:00:00:00:11"; + config.interfaces[1].intfID = 2; + config.interfaces[1].vlanID = 2; + config.interfaces[1].routerID = 1; + config.interfaces[1].__isset.mac = true; + config.interfaces[1].mac = "00:00:00:00:00:22"; + + auto stateV1 = publishAndApplyConfig(stateV0, &config, &platform); + ASSERT_NE(nullptr, stateV1); + stateV1->publish(); + auto tablesV1 = stateV1->getRouteTables(); + EXPECT_EQ(tablesV0, tablesV1); + EXPECT_EQ(0, tablesV1->getGeneration()); + EXPECT_EQ(0, tablesV1->size()); + + config.interfaces[0].ipAddresses.resize(4); + config.interfaces[0].ipAddresses[0] = "1.1.1.1/24"; + config.interfaces[0].ipAddresses[1] = "1.1.1.2/24"; + config.interfaces[0].ipAddresses[2] = "1.1.1.10/24"; + config.interfaces[0].ipAddresses[3] = "::1/48"; + config.interfaces[1].ipAddresses.resize(2); + config.interfaces[1].ipAddresses[0] = "1.1.1.1/24"; + config.interfaces[1].ipAddresses[1] = "::1/48"; + + auto stateV2 = publishAndApplyConfig(stateV1, &config, &platform); + ASSERT_NE(nullptr, stateV2); + stateV2->publish(); + auto tablesV2 = stateV2->getRouteTables(); + EXPECT_NE(tablesV1, tablesV2); + EXPECT_EQ(1, tablesV2->getGeneration()); + EXPECT_EQ(2, tablesV2->size()); + EXPECT_NE(nullptr, tablesV2->getRouteTable(RouterID(0))); + EXPECT_NE(nullptr, tablesV2->getRouteTable(RouterID(1))); + + checkChangedRouteTable(tablesV1, tablesV2, {}, {0,1}, {}); + checkChangedRoute(tablesV1, tablesV2, + {}, { + TEMP::Route{0, IPAddress("1.1.1.0"), 24}, + TEMP::Route{0, IPAddress("::0"), 48}, + TEMP::Route{1, IPAddress("1.1.1.0"), 24}, + TEMP::Route{1, IPAddress("::0"), 48}, + }, + {}); + + // change an interface address + config.interfaces[0].ipAddresses[3] = "11::11/48"; + + auto stateV3 = publishAndApplyConfig(stateV2, &config, &platform); + ASSERT_NE(nullptr, stateV3); + stateV3->publish(); + auto tablesV3 = stateV3->getRouteTables(); + EXPECT_NE(tablesV2, tablesV3); + EXPECT_EQ(2, tablesV3->getGeneration()); + EXPECT_EQ(2, tablesV3->size()); + EXPECT_NE(nullptr, tablesV2->getRouteTable(RouterID(0))); + EXPECT_NE(nullptr, tablesV2->getRouteTable(RouterID(1))); + + checkChangedRouteTable(tablesV2, tablesV3, {0}, {}, {}); + checkChangedRoute(tablesV2, tablesV3, + {}, + {TEMP::Route{0, IPAddress("11::0"), 48}}, + {TEMP::Route{0, IPAddress("::0"), 48}}); + + // move one interface to cause same route prefix conflict + config.interfaces[1].routerID = 0; + EXPECT_THROW(publishAndApplyConfig(stateV3, &config, &platform), FbossError); + + // add a new interface in a new VRF + config.interfaces.resize(3); + config.interfaces[2].intfID = 3; + config.interfaces[2].vlanID = 3; + config.interfaces[2].routerID = 2; + config.interfaces[2].__isset.mac = true; + config.interfaces[2].mac = "00:00:00:00:00:33"; + config.interfaces[2].ipAddresses.resize(2); + config.interfaces[2].ipAddresses[0] = "1.1.1.1/24"; + config.interfaces[2].ipAddresses[1] = "::1/48"; + // and move one interface to another vrf and fix the address conflict + config.interfaces[1].routerID = 0; + config.interfaces[1].ipAddresses.resize(2); + config.interfaces[1].ipAddresses[0] = "2.2.2.1/24"; + config.interfaces[1].ipAddresses[1] = "1::2/48"; + + auto stateV4 = publishAndApplyConfig(stateV3, &config, &platform); + ASSERT_NE(nullptr, stateV4); + stateV4->publish(); + auto tablesV4 = stateV4->getRouteTables(); + EXPECT_NE(tablesV3, tablesV4); + EXPECT_EQ(3, tablesV4->getGeneration()); + EXPECT_EQ(2, tablesV4->size()); + EXPECT_NE(nullptr, tablesV4->getRouteTable(RouterID(0))); + EXPECT_EQ(nullptr, tablesV4->getRouteTableIf(RouterID(1))); + EXPECT_NE(nullptr, tablesV4->getRouteTable(RouterID(2))); + + checkChangedRouteTable(tablesV3, tablesV4, {0}, {2}, {1}); + checkChangedRoute(tablesV3, tablesV4, + {}, { + TEMP::Route{0, IPAddress("2.2.2.0"), 24}, + TEMP::Route{0, IPAddress("1::0"), 48}, + TEMP::Route{2, IPAddress("1.1.1.0"), 24}, + TEMP::Route{2, IPAddress("::0"), 48}, + TEMP::Route{2, IPAddress("fe80::"), 64}, + }, { + TEMP::Route{1, IPAddress("1.1.1.0"), 24}, + TEMP::Route{1, IPAddress("::0"), 48} + }); + + // re-apply the same configure generates no change + EXPECT_EQ(nullptr, publishAndApplyConfig(stateV4, &config, &platform)); +} + +TEST(Route, changedRoutesPostUpdate) { + SaiPlatform platform; + auto stateV0 = make_shared(); + auto tablesV0 = stateV0->getRouteTables(); + + cfg::SwitchConfig config; + config.vlans.resize(1); + config.vlans[0].id = 1; + + config.interfaces.resize(1); + config.interfaces[0].intfID = 1; + config.interfaces[0].vlanID = 1; + config.interfaces[0].routerID = 0; + config.interfaces[0].__isset.mac = true; + config.interfaces[0].mac = "00:00:00:00:00:11"; + config.interfaces[0].ipAddresses.resize(2); + config.interfaces[0].ipAddresses[0] = "1.1.1.1/24"; + config.interfaces[0].ipAddresses[1] = "1::1/48"; + + auto stateV1 = publishAndApplyConfig(stateV0, &config, &platform); + ASSERT_NE(nullptr, stateV1); + stateV1->publish(); + auto rid = RouterID(0); + RouteNextHops nexthops; + nexthops.emplace(IPAddress("1.1.1.10")); // resolved by intf 1 + nexthops.emplace(IPAddress("2::2")); // resolved by intf 2 + + auto numChangedRoutes = [=] (const RTMapDelta& delta) { + auto cnt = 0; + + for (auto itr = delta.begin(); itr != delta.end(); ++itr) { + const auto &v4Delta = itr->getRoutesV4Delta(); + const auto &v6Delta = itr->getRoutesV6Delta(); + + for (auto v4Itr = v4Delta.begin(); v4Itr != v4Delta.end(); + ++v4Itr, ++cnt); + + for (auto v6Itr = v6Delta.begin(); v6Itr != v6Delta.end(); + ++v6Itr, ++cnt); + } + + return cnt; + }; + // Add a couple of routes + auto tables1 = stateV1->getRouteTables(); + stateV1->publish(); + RouteUpdater u1(tables1); + u1.addRoute(rid, IPAddress("10.1.1.0"), 24, nexthops); + u1.addRoute(rid, IPAddress("2001::0"), 48, nexthops); + auto tables2 = u1.updateDone(); + ASSERT_NE(nullptr, tables2); + auto t2 = tables2->getRouteTableIf(rid); + ASSERT_NE(nullptr, t2); + // v4 route + auto rib2v4 = t2->getRibV4(); + RouteV4::Prefix p2 {IPAddressV4("10.1.1.0"), 24}; + auto r2 = rib2v4->exactMatch(p2); + ASSERT_NE(nullptr, r2); + EXPECT_TRUE(r2->isResolved()); + EXPECT_FALSE(r2->isUnresolvable()); + EXPECT_FALSE(r2->isConnected()); + EXPECT_FALSE(r2->needResolve()); + // v6 route + auto rib2v6 = t2->getRibV6(); + RouteV6::Prefix p2v6 {IPAddressV6("2001::0"), 48}; + auto r2v6 = rib2v6->exactMatch(p2v6); + ASSERT_NE(nullptr, r2v6); + EXPECT_TRUE(r2v6->isResolved()); + EXPECT_FALSE(r2v6->isUnresolvable()); + EXPECT_FALSE(r2v6->isConnected()); + EXPECT_FALSE(r2v6->needResolve()); + auto stateV2 = stateV1->clone(); + stateV2->resetRouteTables(tables2); + StateDelta delta12(stateV1, stateV2); + EXPECT_EQ(2, numChangedRoutes(delta12.getRouteTablesDelta())); + checkChangedRouteTable(tables1, tables2, {0}, {}, {}); + checkChangedRoute(tables1, tables2, + {}, { + TEMP::Route{0, IPAddress("10.1.1.0"), 24}, + TEMP::Route{0, IPAddress("2001::0"), 48}, + }, + {}); + stateV2->publish(); + // Add 2 more routes + RouteUpdater u2(stateV2->getRouteTables()); + u2.addRoute(rid, IPAddress("10.10.1.0"), 24, nexthops); + u2.addRoute(rid, IPAddress("2001:10::0"), 48, nexthops); + auto tables3 = u2.updateDone(); + ASSERT_NE(nullptr, tables3); + auto t3 = tables3->getRouteTableIf(rid); + ASSERT_NE(nullptr, t3); + // v4 route + auto rib3v4 = t3->getRibV4(); + RouteV4::Prefix p3 {IPAddressV4("10.10.1.0"), 24}; + auto r3 = rib3v4->exactMatch(p3); + ASSERT_NE(nullptr, r3); + EXPECT_TRUE(r3->isResolved()); + EXPECT_FALSE(r3->isUnresolvable()); + EXPECT_FALSE(r3->isConnected()); + EXPECT_FALSE(r3->needResolve()); + // v6 route + auto rib3v6 = t3->getRibV6(); + RouteV6::Prefix p3v6 {IPAddressV6("2001:10::0"), 48}; + auto r3v6 = rib3v6->exactMatch(p3v6); + ASSERT_NE(nullptr, r3v6); + EXPECT_TRUE(r3v6->isResolved()); + EXPECT_FALSE(r3v6->isUnresolvable()); + EXPECT_FALSE(r3v6->isConnected()); + EXPECT_FALSE(r3v6->needResolve()); + auto stateV3 = stateV2->clone(); + stateV3->resetRouteTables(tables3); + StateDelta delta23(stateV2, stateV3); + EXPECT_EQ(2, numChangedRoutes(delta23.getRouteTablesDelta())); + checkChangedRouteTable(tables2, tables3, {0}, {}, {}); + checkChangedRoute(tables2, tables3, + {}, { + TEMP::Route{0, IPAddress("10.10.1.0"), 24}, + TEMP::Route{0, IPAddress("2001:10::0"), 48}, + }, + {}); + stateV3->publish(); +} diff --git a/fboss/agent/hw/sai/SaiRxPacket.cpp b/fboss/agent/hw/sai/SaiRxPacket.cpp new file mode 100644 index 0000000000000..eb988e212dde6 --- /dev/null +++ b/fboss/agent/hw/sai/SaiRxPacket.cpp @@ -0,0 +1,80 @@ +/* + * 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."); + } + + 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); + } + } + + len_ = buf_size; + + void *pkt_buf = new uint8_t[buf_size]; + memcpy(pkt_buf, buf, buf_size); + + buf_ = IOBuf::takeOwnership(pkt_buf, // void* buf + buf_size, // 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..272cced7f9676 --- /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..c6aa7d8803777 --- /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..47c86b1dfd73f --- /dev/null +++ b/fboss/agent/hw/sai/SaiSwitch.cpp @@ -0,0 +1,1204 @@ +/* + * 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 { + +// 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__; + + 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 **) &pSaiSwitchApi_); + sai_api_query(SAI_API_VLAN, (void **) &pSaiVlanApi_); + //sai_api_query(SAI_API_PORT, (void **) &pSaiPortApi_); + sai_api_query(SAI_API_ROUTER_INTERFACE, (void **) &pSaiRouterIntfApi_); + sai_api_query(SAI_API_ROUTE, (void **) &pSaiRouteApi_); + //sai_api_query(SAI_API_ACL, (void **) &pSaiAclApi_); + sai_api_query(SAI_API_VIRTUAL_ROUTER, (void **) &pSaiVrfApi_); + sai_api_query(SAI_API_NEIGHBOR, (void **) &pSaiNeighborApi_); + sai_api_query(SAI_API_NEXT_HOP, (void **) &pSaiNextHopApi_); + sai_api_query(SAI_API_NEXT_HOP_GROUP, (void **) &pSaiNextHopGroupApi_); + sai_api_query(SAI_API_HOST_INTERFACE, (void **) &pSaiHostIntfApi_); + + // TODO: remove this when SAI callbacks support pointer to user data storage. + SaiSwitch::instance_ = this; +} + +SaiSwitch::~SaiSwitch() { + VLOG(4) << "Entering " << __FUNCTION__; + + pSaiNextHopApi_ = nullptr; + pSaiAclApi_ = nullptr; + pSaiHostInterfaceApi_ = nullptr; + pSaiNeighborApi_ = nullptr; + pSaiRouterIntfApi_ = nullptr; + pSaiRouteApi_ = nullptr; + pSaiVrfApi_ = nullptr; + pSaiPortApi_ = nullptr; + pSaiVlanApi_ = nullptr; + pSaiSwitchApi_ = nullptr; + pSaiHostIntfApi_ = 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 = pSaiSwitchApi_->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 = pSaiHostIntfApi_->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 = pSaiHostIntfApi_->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 = pSaiHostIntfApi_->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. + forEachChanged(delta.getPortsDelta(), + [&] (const shared_ptr &oldPort, const shared_ptr &newPort) { + if (oldPort->getState() == newPort->getState()) { + return; + } + + if (newPort->getState() == cfg::PortState::DOWN || + newPort->getState() == cfg::PortState::POWER_DOWN) { + ChangePortState(oldPort, newPort); + } + }); + + // 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); + + AddHostsForDemo(); + + // 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. + forEachChanged(delta.getPortsDelta(), + [&] (const shared_ptr &oldPort, const shared_ptr &newPort) { + if (oldPort->getState() == newPort->getState()) { + return; + } + + if (newPort->getState() != cfg::PortState::DOWN && + newPort->getState() != cfg::PortState::POWER_DOWN) { + ChangePortState(oldPort, newPort); + } + }); + } 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); +#if 0 + sai_status_t status = pSaiHostIntfApi_->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; + } +#endif + 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); +#if 0 + sai_status_t status = pSaiHostIntfApi_->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; + } +#endif + return true; +} + +void SaiSwitch::ChangePortState(const shared_ptr &oldPort, + const shared_ptr &newPort) { + VLOG(4) << "Entering " << __FUNCTION__; + + try { + uint16_t o = portTable_->GetSaiPortId(oldPort->getID()); + uint16_t n = portTable_->GetSaiPortId(newPort->getID()); + VLOG(3) << "changePortState(" << o << ", " << n << ")"; + } catch (SaiError &e) { + LOG(ERROR) << e.what(); + } +} + +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::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(); + + BOOST_FOREACH(auto& it, *portStatsMap) { + PortID portId = it.first; + PortStats *portStats = it.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 refNewEntry = [&]() { + 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_->IncRefOrCreateSaiHost(newEntry->getIntfID(), + IPAddress(newEntry->getIP()), + newEntry->getMac()); + try { + host->Program(action); + } catch (const SaiError &e) { + hostTable_->DerefSaiHost(newEntry->getIntfID(), + IPAddress(newEntry->getIP()), + newEntry->getMac()); + LOG(ERROR) << e.what(); + return; + } + }; + + auto unrefOldEntry = [&]() { + VLOG(3) << "Deleting neighbor entry"; + auto host = hostTable_->DerefSaiHost(oldEntry->getIntfID(), + IPAddress(oldEntry->getIP()), + oldEntry->getMac()); + if (host) { + try { + host->Program(SAI_PACKET_ACTION_TRAP); + } catch (const SaiError &e) { + LOG(ERROR) << e.what(); + return; + } + } + }; + + if (!oldEntry) { + refNewEntry(); + } else if (!newEntry) { + unrefOldEntry(); + } else if ((oldEntry->getIntfID() != newEntry->getIntfID()) || + (oldEntry->getIP() != newEntry->getIP()) || + (oldEntry->getMac() != newEntry->getMac())) { + refNewEntry(); + unrefOldEntry(); + } +} + +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 { + routeTable_->AddRoute(id, newRoute.get()); + } +} + +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; + } + + routeTable_->AddRoute(id, route.get()); +} + +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; + } + + routeTable_->DeleteRoute(id, route.get()); +} + +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(); + auto oldIter = oldPorts.begin(); + auto newIter = newPorts.begin(); + + while ((oldIter != oldPorts.end()) && + (newIter != newPorts.end())) { + + if ((oldIter == oldPorts.end()) || + ((newIter != newPorts.end()) && (newIter->first < oldIter->first))) { + + sai_vlan_port_t vlanPort; + + try { + vlanPort.port_id = portTable_->GetSaiPortId(newIter->first); + } catch (const SaiError &e) { + LOG(ERROR) << e.what(); + ++newIter; + continue; + } + + vlanPort.tagging_mode = newIter->second.tagged ? SAI_VLAN_PORT_TAGGED : + SAI_VLAN_PORT_UNTAGGED; + addedPorts.push_back(vlanPort); + ++newIter; + } else if (newIter == newPorts.end() || + (oldIter != oldPorts.end() && (oldIter->first < newIter->first))) { + + sai_vlan_port_t vlanPort; + + try { + vlanPort.port_id = portTable_->GetSaiPortId(oldIter->first); + } catch (const SaiError &e) { + LOG(ERROR) << e.what(); + ++oldIter; + continue; + } + + removedPorts.push_back(vlanPort); + + } else { + ++oldIter; + ++newIter; + } + } + + VLOG(2) << "updating VLAN " << newVlan->getID() << ": " << addedPorts.size() + << " ports added, " << removedPorts.size() << " ports removed"; + + if (removedPorts.size() > 0) { + saiStatus = pSaiVlanApi_->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) { + saiStatus = pSaiVlanApi_->add_ports_to_vlan(vlanId, addedPorts.size(), + addedPorts.data()); + + if (saiStatus != SAI_STATUS_SUCCESS) { + LOG(ERROR) << "Failed to add ports to VLAN " << vlanId; + } + } + + if ((addedPorts.size() == 0) && + (removedPorts.size() == 0)) + { + // Nothing changed means that it's new VLAN + ProcessAddedVlan(newVlan); + } +} + +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 = pSaiVlanApi_->create_vlan(vlanId); + if (saiStatus != SAI_STATUS_SUCCESS) { + throw SaiError("Failed to create VLAN ", vlanId); + } + + saiStatus = pSaiVlanApi_->add_ports_to_vlan(vlanId, portList.size(), portList.data()); + if (saiStatus != SAI_STATUS_SUCCESS) { + throw SaiError("Failed to add ports to VLAN ", vlanId); + } + + 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 = pSaiVlanApi_->remove_ports_from_vlan(vlanId, portList.size(), portList.data()); + if (saiStatus != SAI_STATUS_SUCCESS) { + LOG(ERROR) << "Failed to remove VLAN " << vlanId; + } + + saiStatus = pSaiVlanApi_->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 = pSaiVlanApi_->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); +} + + +void SaiSwitch::AddHostsForDemo() { + VLOG(4) << "Entering " << __FUNCTION__; + + static bool added {false}; + + if (added) { + return; + } + + sai_fdb_api_t *pSaiFdbApi {nullptr}; + sai_api_query(SAI_API_FDB, (void **) &pSaiFdbApi); + + // iterate trough all L3 interfaces and create + // static FDB entries for neighbor hosts and the + // neighbor hosts themselves which will be used in demo + auto intfPtr = GetIntfTable()->GetFirstIntfIf(); + while (intfPtr != nullptr) { + + auto mac = intfPtr->GetInterface()->getMac(); + // host mac will be intf mac + 1 + const_cast(mac.bytes())[5] += 1; + + // Pick the same port number as interface ID. + auto egressPortId = PortID(intfPtr->GetInterface()->getID().t); + sai_object_id_t saiEgressPortId {SAI_NULL_OBJECT_ID}; + + try { + saiEgressPortId = GetPortTable()->GetSaiPortId(egressPortId); + } catch (const SaiError &e) { + LOG(ERROR) << e.what(); + intfPtr = GetIntfTable()->GetNextIntfIf(intfPtr); + continue; + } + + // Add static FDB entry for a host + sai_fdb_entry_t fdb_entry; + + memcpy(fdb_entry.mac_address, mac.bytes(), sizeof(fdb_entry.mac_address)); + fdb_entry.vlan_id = intfPtr->GetInterface()->getVlanID().t; + + // FDB attributes + std::vector attr_list; + sai_attribute_t attr; + + // entry type + attr.id = SAI_FDB_ENTRY_ATTR_TYPE; + attr.value.u32 = SAI_FDB_ENTRY_STATIC; + attr_list.push_back(attr); + + // port id + attr.id = SAI_FDB_ENTRY_ATTR_PORT_ID; + attr.value.oid = saiEgressPortId; + attr_list.push_back(attr); + + // packet action + attr.id = SAI_FDB_ENTRY_ATTR_PACKET_ACTION; + attr.value.oid = SAI_PACKET_ACTION_FORWARD; + attr_list.push_back(attr); + + // create fdb entry + sai_status_t saiRetVal = pSaiFdbApi->create_fdb_entry(&fdb_entry, attr_list.size(), attr_list.data()); + if (saiRetVal != SAI_STATUS_SUCCESS) { + LOG(ERROR) << "Could not create static fdb entry with VLAN: " << fdb_entry.vlan_id + << ", MAC: " << mac.toString() << ", port: " << egressPortId.t + << ". Error: " << saiRetVal; + + intfPtr = GetIntfTable()->GetNextIntfIf(intfPtr); + continue; + } + + VLOG(2) << "Created static fdb entry with VLAN: " << fdb_entry.vlan_id << ", MAC: " + << mac << ", port: " << egressPortId.t; + + // create hosts + for (const auto& addrPair : intfPtr->GetInterface()->getAddresses()) { + // host IP will be intf IP + 1 + auto addr = addrPair.first; + const_cast(addr.bytes())[3] += 1; + + // Compose ARP reply + uint32_t pktLen = 64; + auto pkt = allocatePacket(pktLen); + folly::io::RWPrivateCursor cursor(pkt->buf()); + + pkt->writeEthHeader(&cursor, intfPtr->GetInterface()->getMac(), + mac, ArpHandler::ETHERTYPE_ARP); + cursor.writeBE(ARP_HTYPE_ETHERNET); + cursor.writeBE(ARP_PTYPE_IPV4); + cursor.writeBE(ARP_HLEN_ETHERNET); + cursor.writeBE(ARP_PLEN_IPV4); + cursor.writeBE(ARP_OPER_REPLY); + // sender MAC/IP + cursor.push(mac.bytes(), MacAddress::SIZE); + cursor.write(addr.asV4().toLong()); + // target MAC/IP + cursor.push(intfPtr->GetInterface()->getMac().bytes(), MacAddress::SIZE); + cursor.write(addrPair.first.asV4().toLong()); + // Fill the padding with 0s + memset(cursor.writableData(), 0, cursor.length()); + + // Pkt attributes + std::vector pkt_attr_list; + sai_attribute_t pkt_attr; + + // entry type + pkt_attr.id = SAI_HOSTIF_PACKET_INGRESS_PORT; + pkt_attr.value.oid = saiEgressPortId; + pkt_attr_list.push_back(pkt_attr); + + // Simulate ARP replay received in order to install neighbour hosts + PacketRxCallback(pkt->buf()->data(), pktLen, pkt_attr_list.size(), pkt_attr_list.data()); + + VLOG(2) << "Created host with Intf: " << intfPtr->GetInterface()->getID().t + << ", IP: " << addr << ", MAC: " << mac; + } + intfPtr = GetIntfTable()->GetNextIntfIf(intfPtr); + } // while (intfPtr != nullptr) + + added = true; +} + +}} // facebook::fboss diff --git a/fboss/agent/hw/sai/SaiSwitch.h b/fboss/agent/hw/sai/SaiSwitch.h new file mode 100644 index 0000000000000..a7c6cffe110d4 --- /dev/null +++ b/fboss/agent/hw/sai/SaiSwitch.h @@ -0,0 +1,303 @@ +/* + * 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 pSaiSwitchApi_; + } + + sai_vlan_api_t *GetSaiVlanApi() const { + return pSaiVlanApi_; + } + + sai_port_api_t *GetSaiPortApi() const { + return pSaiPortApi_; + } + + sai_virtual_router_api_t *GetSaiVrfApi() const { + return pSaiVrfApi_; + } + + sai_route_api_t *GetSaiRouteApi() const { + return pSaiRouteApi_; + } + + sai_router_interface_api_t *GetSaiRouterIntfApi() const { + return pSaiRouterIntfApi_; + } + + sai_neighbor_api_t *GetSaiNeighborApi() const { + return pSaiNeighborApi_; + } + + sai_hostif_api_t *GetSaiHostInterfaceApi() const { + return pSaiHostInterfaceApi_; + } + + sai_acl_api_t *GetSaiAclApi() const { + return pSaiAclApi_; + } + + sai_next_hop_api_t *GetSaiNextHopApi() const { + return pSaiNextHopApi_; + } + + sai_next_hop_group_api_t *GetSaiNextHopGroupApi() const { + return pSaiNextHopGroupApi_; + } + +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 ChangePortState(const std::shared_ptr &oldPort, + const std::shared_ptr &newPort); + /* + * 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; + + void AddHostsForDemo(); + + 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 *pSaiSwitchApi_ {nullptr}; + sai_vlan_api_t *pSaiVlanApi_ {nullptr}; + sai_port_api_t *pSaiPortApi_ {nullptr}; + sai_virtual_router_api_t *pSaiVrfApi_ {nullptr}; + sai_route_api_t *pSaiRouteApi_ {nullptr}; + sai_router_interface_api_t *pSaiRouterIntfApi_ {nullptr}; + sai_neighbor_api_t *pSaiNeighborApi_ {nullptr}; + sai_hostif_api_t *pSaiHostInterfaceApi_ {nullptr}; + sai_acl_api_t *pSaiAclApi_ {nullptr}; + sai_next_hop_api_t *pSaiNextHopApi_ {nullptr}; + sai_next_hop_group_api_t *pSaiNextHopGroupApi_ {nullptr}; + sai_hostif_api_t *pSaiHostIntfApi_ {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/SaiTestUtils.cpp b/fboss/agent/hw/sai/SaiTestUtils.cpp new file mode 100644 index 0000000000000..c7735895f0719 --- /dev/null +++ b/fboss/agent/hw/sai/SaiTestUtils.cpp @@ -0,0 +1,266 @@ +/* + * 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/hw/sai/SaiTestUtils.h" +#include "fboss/agent/hw/sai/SaiSwitch.h" + +#include "fboss/agent/ApplyThriftConfig.h" +#include "fboss/agent/SwSwitch.h" +#include "fboss/agent/HwSwitch.h" +#include "fboss/agent/state/SwitchState.h" +#include "fboss/agent/state/Vlan.h" +#include "fboss/agent/state/VlanMap.h" +#include "fboss/agent/state/Interface.h" +#include "fboss/agent/state/Port.h" +#include "fboss/agent/state/RouteUpdater.h" +#include "fboss/agent/platforms/sai/SaiPlatform.h" + +#include "fboss/agent/gen-cpp/switch_config_types.h" + +#include +#include + +using folly::MacAddress; +using folly::IPAddress; +using folly::ByteRange; +using folly::IOBuf; +using folly::io::Cursor; +using folly::make_unique; +using folly::StringPiece; +using std::make_shared; +using std::shared_ptr; +using std::string; +using std::unique_ptr; +using ::testing::_; +using ::testing::Return; + +namespace facebook { namespace fboss { + +shared_ptr publishAndApplyConfig( + shared_ptr &state, + const cfg::SwitchConfig *config, + const Platform *platform) { + state->publish(); + return applyThriftConfig(state, config, platform); +} + +shared_ptr publishAndApplyConfigFile( + shared_ptr &state, + StringPiece path, + const Platform *platform) { + state->publish(); + return applyThriftConfigFile(state, path, platform); +} + +unique_ptr createSaiSw(const shared_ptr &state) { + auto sw = make_unique(make_unique()); + auto stateAndBootType = std::make_pair(state, BootType::COLD_BOOT); +// EXPECT_HW_CALL(sw, init(_)).WillOnce(Return(stateAndBootType)); + sw->init(); + return sw; +} + +unique_ptr createSaiSw(const shared_ptr &state, + const MacAddress &mac) { + auto platform = make_unique(); +// EXPECT_CALL(*platform.get(), getLocalMac()).WillRepeatedly(Return(mac)); + auto sw = make_unique(std::move(platform)); + auto stateAndBootType = std::make_pair(state, BootType::COLD_BOOT); +// EXPECT_HW_CALL(sw, init(_)).WillOnce(Return(stateAndBootType)); + sw->init(); + return sw; +} + +unique_ptr createSaiSw(cfg::SwitchConfig *config, + MacAddress mac, + uint32_t maxPort) { + // Create the initial state, which only has ports + auto initialState = make_shared(); + + if (maxPort == 0) { + for (const auto& port : config->ports) { + maxPort = std::max(static_cast(maxPort), port.logicalID); + } + } + + for (uint32_t idx = 1; idx <= maxPort; ++idx) { + initialState->registerPort(PortID(idx), folly::to("port", idx)); + } + + // Create the SwSwitch + auto platform = make_unique(); +// EXPECT_CALL(*platform.get(), getLocalMac()).WillRepeatedly(Return(mac)); + auto sw = make_unique(std::move(platform)); + auto stateAndBootType = std::make_pair(initialState, BootType::COLD_BOOT); +// EXPECT_HW_CALL(sw, init(_)).WillOnce(Return(stateAndBootType)); + sw->init(); + + // Apply the thrift config + auto updateFn = [&](const shared_ptr &state) { + return applyThriftConfig(state, config, sw->getPlatform()); + }; + sw->updateStateBlocking("test_setup", updateFn); + return sw; +} + +unique_ptr createSaiSw(cfg::SwitchConfig *config, + uint32_t maxPort) { + return createSaiSw(config, MacAddress("02:00:00:00:00:01"), maxPort); +} + +SaiSwitch *getSaiHw(SwSwitch *sw) { + return boost::polymorphic_downcast(sw->getHw()); +} + +SaiPlatform *getSaiPlatform(SwSwitch *sw) { + return boost::polymorphic_downcast(sw->getPlatform()); +} + +void waitForStateUpdates(SwSwitch *sw) { + // All StateUpdates scheduled from this thread will be applied in order, + // so we can simply perform a blocking no-op update. When it is done + // we can be sure that all previously scheduled updates have also been + // applied. + auto noopUpdate = [](const shared_ptr &state) { + return shared_ptr(); + }; + sw->updateStateBlocking("waitForStateUpdates", noopUpdate); +} + + +shared_ptr testStateA() { + // Setup a default state object + auto state = make_shared(); + + // Add VLAN 1, and ports 1-9 which belong to it. + auto vlan1 = make_shared(VlanID(1), "Vlan1"); + state->addVlan(vlan1); + + for (int idx = 1; idx < 10; ++idx) { + state->registerPort(PortID(idx), folly::to("port", idx)); + vlan1->addPort(PortID(idx), false); + } + + // Add VLAN 55, and ports 10-19 which belong to it. + auto vlan55 = make_shared(VlanID(55), "Vlan55"); + state->addVlan(vlan55); + + for (int idx = 10; idx < 20; ++idx) { + state->registerPort(PortID(idx), folly::to("port", idx)); + vlan55->addPort(PortID(idx), false); + } + + // Add Interface 1 to VLAN 1 +/* auto intf1 = make_shared + (InterfaceID(1), RouterID(0), VlanID(1), + "interface1", MacAddress("00:02:00:00:00:01"), InterfaceID(5), PortID(1)); + Interface::Addresses addrs1; + addrs1.emplace(IPAddress("10.0.0.1"), 24); + addrs1.emplace(IPAddress("192.168.0.1"), 24); + addrs1.emplace(IPAddress("2401:db00:2110:3001::0001"), 64); + intf1->setAddresses(addrs1); + state->addIntf(intf1); + // Add Interface 55 to VLAN 55 + auto intf55 = make_shared + (RouterID(0), VlanID(55), + "interface55", MacAddress("00:02:00:00:00:55"), InterfaceID(5), PortID(55)); + Interface::Addresses addrs55; + addrs55.emplace(IPAddress("10.0.55.1"), 24); + addrs55.emplace(IPAddress("192.168.55.1"), 24); + addrs55.emplace(IPAddress("2401:db00:2110:3055::0001"), 64); + intf55->setAddresses(addrs55); + state->addIntf(intf55); +*/ + RouteUpdater updater(state->getRouteTables()); + updater.addInterfaceAndLinkLocalRoutes(state->getInterfaces()); + + RouteNextHops nexthops; + nexthops.emplace(IPAddress("10.0.0.22")); // resolved by intf 1 + nexthops.emplace(IPAddress("10.0.0.23")); // resolved by intf 1 + nexthops.emplace(IPAddress("1.1.2.10")); // un-resolvable + + updater.addRoute(RouterID(0), IPAddress("10.1.1.0"), 24, nexthops); + + auto newRt = updater.updateDone(); + state->resetRouteTables(newRt); + + return state; +} + +std::string fbossHexDump(const IOBuf *buf) { + Cursor cursor(buf); + size_t length = cursor.totalLength(); + + if (length == 0) { + return ""; + } + + string result; + result.reserve(length * 3); + folly::format(&result, "{:02x}", cursor.read()); + + for (size_t n = 1; n < length; ++n) { + folly::format(&result, " {:02x}", cursor.read()); + } + + return result; +} + +std::string fbossHexDump(const IOBuf &buf) { + return fbossHexDump(&buf); +} + +std::string fbossHexDump(folly::ByteRange buf) { + IOBuf iobuf(IOBuf::WRAP_BUFFER, buf); + return fbossHexDump(&iobuf); +} + +std::string fbossHexDump(folly::StringPiece buf) { + IOBuf iobuf(IOBuf::WRAP_BUFFER, ByteRange(buf)); + return fbossHexDump(&iobuf); +} + +std::string fbossHexDump(const string &buf) { + IOBuf iobuf(IOBuf::WRAP_BUFFER, ByteRange(StringPiece(buf))); + return fbossHexDump(&iobuf); +} + +TxPacketMatcher::TxPacketMatcher(StringPiece name, TxMatchFn fn) + : name_(name.str()), + fn_(std::move(fn)) { +} + +::testing::Matcher> TxPacketMatcher::createMatcher( + folly::StringPiece name, +TxMatchFn &&fn) { + return ::testing::MakeMatcher(new TxPacketMatcher(name, fn)); +} + +bool TxPacketMatcher::MatchAndExplain( + shared_ptr pkt, + ::testing::MatchResultListener *l) const { + try { + fn_(pkt.get()); + return true; + } catch (const std::exception &ex) { + *l << ex.what(); + return false; + } +} + +void TxPacketMatcher::DescribeTo(std::ostream *os) const { + *os << name_; +} + +void TxPacketMatcher::DescribeNegationTo(std::ostream *os) const { + *os << "not " << name_; +} + +}} // facebook::fboss diff --git a/fboss/agent/hw/sai/SaiTestUtils.h b/fboss/agent/hw/sai/SaiTestUtils.h new file mode 100644 index 0000000000000..79efeeea4040c --- /dev/null +++ b/fboss/agent/hw/sai/SaiTestUtils.h @@ -0,0 +1,197 @@ +/* + * 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 +#include "fboss/agent/FbossError.h" + +namespace facebook { namespace fboss { + +class SaiSwitch; +class SaiPlatform; +class SwitchState; +class SwSwitch; +class TxPacket; +class Platform; + +namespace cfg { +class SwitchConfig; +} + +/* + * In the non unit test code state passed to apply*Config is the state + * returned from SwSwitch init, which is always published. However this + * is not the case for unit test code where we are constructing config + * by hand and applying it to a (often empty) initial state. Now in a rare + * case if such a config had only route updates in it, since the previous + * state was not published, we would apply these route updates inline + * and now when we compare the old and new route tables to determine + * if anything changed we would come up empty. The end result in such + * a case would be to return a null new state falsely signifying no change. + * To avoid this, provide convenience wrappers for unit test code to call. + */ +std::shared_ptr publishAndApplyConfig( + std::shared_ptr &state, + const cfg::SwitchConfig *config, + const Platform *platform); + +std::shared_ptr publishAndApplyConfigFile( + std::shared_ptr &state, + folly::StringPiece path, + const Platform *platform); + +/* + * Create a SwSwitch for testing purposes, with the specified initial state. + * + * This will use a SaiHwSwitch for the HwSwitch implementation, with the + * specified initial state. The returned SwSwitch will have already been + * initialized. + */ +std::unique_ptr createSaiSw(const std::shared_ptr &); +std::unique_ptr createSaiSw(const std::shared_ptr &, + const folly::MacAddress &); + +std::unique_ptr createSaiSw(cfg::SwitchConfig *cfg, + folly::MacAddress mac, + uint32_t maxPorts = 0); +std::unique_ptr createSaiSw(cfg::SwitchConfig *cfg, + uint32_t maxPorts = 0); + +/* + * Get the SaiSwitch from a SwSwitch. + */ +SaiSwitch *getSaiHw(SwSwitch *sw); + +/* + * Get the SaiPlatform from a SwSwitch. + */ +SaiPlatform *getSaiPlatform(SwSwitch *sw); + +/* + * Wait until all pending StateUpdates have been applied. + */ +void waitForStateUpdates(SwSwitch *sw); + +/** + * check the field value + */ +template +void checkField(const ExpectedType &expected, const ActualType &actual, + folly::StringPiece fieldName) { + if (expected != actual) { + throw FbossError("expected ", fieldName, " to be ", expected, + ", but found ", actual); + return; + } +} + +/* + * Create a SwitchState for testing. + * + * Profile A: + * - 20 ports, 2 VLANS, 2 interfaces + * - Ports 1-10 are in VLAN 1 + * - Ports 11-20 are in VLAN 55 + * - Interface 1: + * - VLAN 1 + * - MAC 00:02:00:00:00:01 + * - IPs: + * 10.0.0.1/24 + * 192.168.0.1/24 + * - Interface 55: + * - VLAN 55 + * - MAC 00:02:00:00:00:55 + * - IPs: + * 10.0.55.1/24 + * 192.168.55.1/24 + */ +std::shared_ptr testStateA(); + + +/* + * Convenience macro that wraps EXPECT_CALL() on the underlying Saiwitch + */ +#define EXPECT_HW_CALL(sw, method) \ + EXPECT_CALL(*getSaiHw((sw).get()), method) + +/* + * Convenience macro that wraps EXPECT_CALL() on the underlying SaiPlatform + */ +#define EXPECT_PLATFORM_CALL(sw, method) \ + EXPECT_CALL(*getSaiPlatform((sw).get()), method) + +/* + * Helper functions for comparing buffer-like objects + */ +#define EXPECT_BUF_EQ(a, b) EXPECT_EQ(fbossHexDump(a), fbossHexDump(b)) +#define ASSERT_BUF_EQ(a, b) ASSERT_EQ(fbossHexDump(a), fbossHexDump(b)) + +std::string fbossHexDump(const folly::IOBuf *buf); +std::string fbossHexDump(const folly::IOBuf &buf); +std::string fbossHexDump(folly::ByteRange buf); +std::string fbossHexDump(folly::StringPiece buf); +std::string fbossHexDump(const std::string &buf); + +/* + * Convenience macro for expecting a packet to be transmitted by the switch + * + * The name should be a brief description of the match being performed. + * + * The matchFn should be a function that accepts a single "const TxPacket*" + * argument. If the packet matches it should return void, and if it doesn't + * match it should throw an exception describing why the packet did not match. + * + * Note that you should not use gtest EXPECT_* macros when checking the + * packet. Just because one packet is not a match does not mean that another + * packet will not arrive and match the filter. For instance, consider the + * following code: + * + * EXPECT_PKT(sw, "ARP reply", arpReplyChecker).Times(1); + * EXPECT_PKT(sw, "ICMP packet", icmpChecker).Times(1); + * + * The code expects to see one ARP reply and one ICMP packet, but it may have + * to call both filters on a packet to see which one it is. + */ +#define EXPECT_PKT(sw, name, matchFn) \ + EXPECT_HW_CALL(sw, sendPacketSwitched_( \ + TxPacketMatcher::createMatcher(name, matchFn))); + +typedef std::function TxMatchFn; + +/* + * A gmock MatcherInterface for matching TxPacket objects. + */ +class TxPacketMatcher : + public ::testing::MatcherInterface> { +public: + TxPacketMatcher(folly::StringPiece name, TxMatchFn fn); + + static ::testing::Matcher> createMatcher( + folly::StringPiece name, + TxMatchFn &&fn); + + bool MatchAndExplain(std::shared_ptr pkt, + ::testing::MatchResultListener *l) const override; + + void DescribeTo(std::ostream *os) const override; + void DescribeNegationTo(std::ostream *os) const override; + +private: + std::string name_; + TxMatchFn fn_; +}; + +}} // 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/SaiVlanTests.cpp b/fboss/agent/hw/sai/SaiVlanTests.cpp new file mode 100644 index 0000000000000..028f7ca78354e --- /dev/null +++ b/fboss/agent/hw/sai/SaiVlanTests.cpp @@ -0,0 +1,398 @@ +/* + * 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/ApplyThriftConfig.h" +#include "fboss/agent/FbossError.h" +#include "fboss/agent/state/ArpResponseTable.h" +#include "fboss/agent/state/DeltaFunctions.h" +#include "fboss/agent/state/NdpResponseTable.h" +#include "fboss/agent/state/NodeMapDelta.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/hw/sai/SaiTestUtils.h" +#include "fboss/agent/platforms/sai/SaiPlatform.h" +#include "fboss/agent/gen-cpp/switch_config_types.h" + +#include +#include + +using namespace facebook::fboss; +using folly::IPAddressV4; +using folly::IPAddressV6; +using folly::MacAddress; +using std::make_pair; +using std::make_shared; +using std::shared_ptr; +using std::string; +using testing::Return; + +static const string kVlan1234("Vlan1234"); +static const string kVlan99("Vlan99"); + +TEST(Vlan, applyConfig) { + SaiPlatform platform; + auto stateV0 = make_shared(); + auto vlanV0 = make_shared(VlanID(1234), kVlan1234); + stateV0->addVlan(vlanV0); + stateV0->registerPort(PortID(1), "port1"); + stateV0->registerPort(PortID(99), "port99"); + + NodeID nodeID = vlanV0->getNodeID(); + EXPECT_EQ(0, vlanV0->getGeneration()); + EXPECT_FALSE(vlanV0->isPublished()); + EXPECT_EQ(VlanID(1234), vlanV0->getID()); + Vlan::MemberPorts emptyPorts; + EXPECT_EQ(emptyPorts, vlanV0->getPorts()); + + vlanV0->publish(); + EXPECT_TRUE(vlanV0->isPublished()); + + cfg::SwitchConfig config; + config.ports.resize(2); + config.ports[0].logicalID = 1; + config.ports[0].state = cfg::PortState::UP; + config.ports[1].logicalID = 99; + config.ports[1].state = cfg::PortState::UP; + config.vlans.resize(1); + config.vlans[0].id = 1234; + config.vlans[0].name = kVlan1234; + config.vlans[0].dhcpRelayOverridesV4["02:00:00:00:00:02"] = "1.2.3.4"; + config.vlans[0].dhcpRelayOverridesV6["02:00:00:00:00:02"] = + "2a03:2880:10:1f07:face:b00c:0:0"; + config.vlanPorts.resize(2); + config.vlanPorts[0].logicalPort = 1; + config.vlanPorts[0].vlanID = 1234; + config.vlanPorts[0].emitTags = false; + config.vlanPorts[1].logicalPort = 99; + config.vlanPorts[1].vlanID = 1234; + config.vlanPorts[1].emitTags = true; + + Vlan::MemberPorts expectedPorts; + expectedPorts.insert(make_pair(PortID(1), Vlan::PortInfo(false))); + expectedPorts.insert(make_pair(PortID(99), Vlan::PortInfo(true))); + + auto stateV1 = publishAndApplyConfig(stateV0, &config, &platform); + auto vlanV1 = stateV1->getVlans()->getVlan(VlanID(1234)); + ASSERT_NE(nullptr, vlanV1); + auto vlanV1_byName = stateV1->getVlans()->getVlanSlow(kVlan1234); + EXPECT_EQ(vlanV1, vlanV1_byName); + EXPECT_EQ(nodeID, vlanV1->getNodeID()); + EXPECT_EQ(1, vlanV1->getGeneration()); + EXPECT_FALSE(vlanV1->isPublished()); + EXPECT_EQ(VlanID(1234), vlanV1->getID()); + EXPECT_EQ(kVlan1234, vlanV1->getName()); + EXPECT_EQ(expectedPorts, vlanV1->getPorts()); + EXPECT_EQ(0, vlanV1->getArpResponseTable()->getTable().size()); + + auto map4 = vlanV1->getDhcpV4RelayOverrides(); + EXPECT_EQ(IPAddressV4("1.2.3.4"), + IPAddressV4(map4[MacAddress("02:00:00:00:00:02")])); + auto map6 = vlanV1->getDhcpV6RelayOverrides(); + EXPECT_EQ(IPAddressV6("2a03:2880:10:1f07:face:b00c:0:0"), + IPAddressV6(map6[MacAddress("02:00:00:00:00:02")])); + + // Applying the same config again should return null + EXPECT_EQ(nullptr, publishAndApplyConfig(stateV1, &config, &platform)); + + // Add an interface + config.interfaces.resize(1); + config.interfaces[0].intfID = 1; + config.interfaces[0].routerID = 0; + config.interfaces[0].vlanID = 1234; + config.interfaces[0].ipAddresses.resize(2); + config.interfaces[0].ipAddresses[0] = "10.1.1.1/24"; + config.interfaces[0].ipAddresses[1] = "2a03:2880:10:1f07:face:b00c:0:0/96"; + MacAddress platformMac("82:02:00:ab:cd:ef"); +// EXPECT_CALL(platform, getLocalMac()).WillRepeatedly(Return(platformMac)); + + auto stateV2 = publishAndApplyConfig(stateV1, &config, &platform); + auto vlanV2 = stateV2->getVlans()->getVlan(VlanID(1234)); + EXPECT_EQ(nodeID, vlanV2->getNodeID()); + EXPECT_EQ(2, vlanV2->getGeneration()); + EXPECT_FALSE(vlanV2->isPublished()); + EXPECT_EQ(VlanID(1234), vlanV2->getID()); + EXPECT_EQ(kVlan1234, vlanV2->getName()); + EXPECT_EQ(expectedPorts, vlanV2->getPorts()); + // Check the ArpResponseTable + auto arpRespTable = vlanV2->getArpResponseTable(); + ArpResponseTable expectedArpResp; + expectedArpResp.setEntry(IPAddressV4("10.1.1.1"), platformMac, + InterfaceID(1)); + EXPECT_EQ(expectedArpResp.getTable(), arpRespTable->getTable()); + // Check the NdpResponseTable + auto ndpRespTable = vlanV2->getNdpResponseTable(); + NdpResponseTable expectedNdpResp; + expectedNdpResp.setEntry(IPAddressV6("2a03:2880:10:1f07:face:b00c:0:0"), + platformMac, InterfaceID(1)); + // The link-local IPv6 address should also have been automatically added + // to the NDP response table. + expectedNdpResp.setEntry(IPAddressV6("fe80::8002:00ff:feab:cdef"), + platformMac, InterfaceID(1)); + EXPECT_EQ(expectedNdpResp.getTable(), ndpRespTable->getTable()); + + // Add another interface + config.interfaces.resize(2); + config.interfaces[1].intfID = 2; + config.interfaces[1].routerID = 0; + config.interfaces[1].vlanID = 1234; + config.interfaces[1].ipAddresses.resize(2); + config.interfaces[1].ipAddresses[0] = "10.1.10.1/24"; + config.interfaces[1].ipAddresses[1] = "192.168.0.1/31"; + MacAddress intf2Mac("02:01:02:ab:cd:80"); + config.interfaces[1].mac = intf2Mac.toString(); + config.interfaces[1].__isset.mac = true; + auto stateV3 = publishAndApplyConfig(stateV2, &config, &platform); + auto vlanV3 = stateV3->getVlans()->getVlan(VlanID(1234)); + EXPECT_EQ(nodeID, vlanV3->getNodeID()); + EXPECT_EQ(3, vlanV3->getGeneration()); + EXPECT_FALSE(vlanV3->isPublished()); + EXPECT_EQ(VlanID(1234), vlanV3->getID()); + EXPECT_EQ(kVlan1234, vlanV2->getName()); + EXPECT_EQ(expectedPorts, vlanV3->getPorts()); + // Check the ArpResponseTable + arpRespTable = vlanV3->getArpResponseTable(); + ArpResponseTable expectedTable2; + expectedTable2.setEntry(IPAddressV4("10.1.1.1"), platformMac, + InterfaceID(1)); + expectedTable2.setEntry(IPAddressV4("10.1.10.1"), intf2Mac, + InterfaceID(2)); + expectedTable2.setEntry(IPAddressV4("192.168.0.1"), intf2Mac, + InterfaceID(2)); + EXPECT_EQ(expectedTable2.getTable(), arpRespTable->getTable()); + // The new interface has no IPv6 address, but the NDP table should still + // be updated with the link-local address. + expectedNdpResp.setEntry(IPAddressV6("fe80::1:02ff:feab:cd78"), + intf2Mac, InterfaceID(2)); + EXPECT_EQ(expectedNdpResp.getTable(), + vlanV3->getNdpResponseTable()->getTable()); + + // Configuration should fail if there the VLAN is used by multiple + // different routers + config.interfaces[1].routerID = 1; + EXPECT_THROW(publishAndApplyConfig(stateV3, &config, &platform), FbossError); + config.interfaces[1].routerID = 0; + + // Add a new VLAN with an ArpResponseTable that needs to be set up + // when the VLAN is first created + config.vlans.resize(2); + config.vlans[1].id = 99; + config.vlans[1].name = kVlan99; + config.interfaces.resize(3); + config.interfaces[2].intfID = 3; + config.interfaces[2].routerID = 1; + config.interfaces[2].vlanID = 99; + config.interfaces[2].ipAddresses.resize(2); + config.interfaces[2].ipAddresses[0] = "1.2.3.4/24"; + config.interfaces[2].ipAddresses[1] = "10.0.0.1/9"; + auto stateV4 = publishAndApplyConfig(stateV3, &config, &platform); + ASSERT_NE(nullptr, stateV4); + // VLAN 1234 should be unchanged + EXPECT_EQ(vlanV3, stateV4->getVlans()->getVlan(VlanID(1234))); + auto vlan99 = stateV4->getVlans()->getVlan(VlanID(99)); + auto vlan99_byName = stateV4->getVlans()->getVlanSlow(kVlan99); + ASSERT_NE(nullptr, vlan99); + EXPECT_EQ(vlan99, vlan99_byName); + EXPECT_EQ(0, vlan99->getGeneration()); + ArpResponseTable expectedTable99; + expectedTable99.setEntry(IPAddressV4("1.2.3.4"), platformMac, + InterfaceID(3)); + expectedTable99.setEntry(IPAddressV4("10.0.0.1"), platformMac, + InterfaceID(3)); + EXPECT_EQ(expectedTable99.getTable(), + vlan99->getArpResponseTable()->getTable()); + + // Change vlan99's MTU + auto stateV5 = publishAndApplyConfig(stateV4, &config, &platform); + ASSERT_NE(nullptr, stateV5); + // VLAN 1234 should be unchanged + EXPECT_EQ(vlanV3, stateV5->getVlans()->getVlan(VlanID(1234))); + auto vlan99v1 = stateV5->getVlans()->getVlan(VlanID(99)); + EXPECT_NE(vlan99, vlan99v1); + EXPECT_EQ(1, vlan99v1->getGeneration()); + EXPECT_EQ(vlan99->getDhcpV4Relay(), vlan99v1->getDhcpV4Relay()); + EXPECT_EQ(vlan99->getArpResponseTable(), vlan99v1->getArpResponseTable()); +} + +/* + * Test that forEachChanged(StateDelta::getVlansDelta(), ...) invokes the + * callback for the specified list of changed VLANs. + */ +void checkChangedVlans(const shared_ptr &oldVlans, + const shared_ptr &newVlans, + const std::set changedIDs, + const std::set addedIDs, + const std::set removedIDs) { + auto oldState = make_shared(); + oldState->resetVlans(oldVlans); + auto newState = make_shared(); + newState->resetVlans(newVlans); + + std::set foundChanged; + std::set foundAdded; + std::set foundRemoved; + StateDelta delta(oldState, newState); + DeltaFunctions::forEachChanged(delta.getVlansDelta(), + [&] (const shared_ptr &oldVlan, const shared_ptr &newVlan) { + EXPECT_EQ(oldVlan->getID(), newVlan->getID()); + EXPECT_NE(oldVlan, newVlan); + + auto ret = foundChanged.insert(oldVlan->getID()); + EXPECT_TRUE(ret.second); + }, + [&] (const shared_ptr &vlan) { + auto ret = foundAdded.insert(vlan->getID()); + EXPECT_TRUE(ret.second); + }, + [&] (const shared_ptr &vlan) { + auto ret = foundRemoved.insert(vlan->getID()); + EXPECT_TRUE(ret.second); + }); + + EXPECT_EQ(changedIDs, foundChanged); + EXPECT_EQ(addedIDs, foundAdded); + EXPECT_EQ(removedIDs, foundRemoved); +} + +TEST(VlanMap, applyConfig) { + SaiPlatform platform; + auto stateV0 = make_shared(); + stateV0->registerPort(PortID(1), "port1"); + stateV0->registerPort(PortID(2), "port2"); + stateV0->registerPort(PortID(3), "port3"); + stateV0->registerPort(PortID(4), "port4"); + stateV0->registerPort(PortID(9), "port9"); + stateV0->registerPort(PortID(19), "port19"); + stateV0->registerPort(PortID(20), "port29"); + + auto vlansV0 = stateV0->getVlans(); + + // Apply new config settings + cfg::SwitchConfig config; + config.vlans.resize(2); + config.vlans[0].id = 1234; + config.vlans[0].name = kVlan1234; + config.vlans[1].id = 99; + config.vlans[1].name = kVlan99; + config.vlanPorts.resize(7); + config.vlanPorts[0].vlanID = 1234; + config.vlanPorts[0].logicalPort = 1; + config.vlanPorts[1].vlanID = 1234; + config.vlanPorts[1].logicalPort = 2; + config.vlanPorts[2].vlanID = 1234; + config.vlanPorts[2].logicalPort = 3; + config.vlanPorts[3].vlanID = 1234; + config.vlanPorts[3].logicalPort = 4; + config.vlanPorts[4].vlanID = 99; + config.vlanPorts[4].logicalPort = 9; + config.vlanPorts[5].vlanID = 99; + config.vlanPorts[5].logicalPort = 19; + config.vlanPorts[6].vlanID = 99; + config.vlanPorts[6].logicalPort = 29; + + auto stateV1 = publishAndApplyConfig(stateV0, &config, &platform); + auto vlansV1 = stateV1->getVlans(); + ASSERT_NE(nullptr, vlansV1); + EXPECT_EQ(1, vlansV1->getGeneration()); + EXPECT_EQ(2, vlansV1->size()); + + // Check the new settings for VLAN 1234 + auto vlan1234v0 = vlansV1->getVlan(VlanID(1234)); + auto vlan1234v0_byName = vlansV1->getVlanSlow(kVlan1234); + ASSERT_NE(nullptr, vlan1234v0); + EXPECT_EQ(vlan1234v0, vlan1234v0_byName); + NodeID id1234 = vlan1234v0->getNodeID(); + EXPECT_EQ(VlanID(1234), vlan1234v0->getID()); + EXPECT_EQ(kVlan1234, vlan1234v0->getName()); + EXPECT_EQ(0, vlan1234v0->getGeneration()); + Vlan::MemberPorts ports1234v0; + ports1234v0.insert(make_pair(PortID(1), Vlan::PortInfo(false))); + ports1234v0.insert(make_pair(PortID(2), Vlan::PortInfo(false))); + ports1234v0.insert(make_pair(PortID(3), Vlan::PortInfo(false))); + ports1234v0.insert(make_pair(PortID(4), Vlan::PortInfo(false))); + EXPECT_EQ(ports1234v0, vlan1234v0->getPorts()); + + // Check the new settings for VLAN 99 + auto vlan99v0 = vlansV1->getVlan(VlanID(99)); + ASSERT_NE(nullptr, vlan99v0); + auto vlan99v0_byName = vlansV1->getVlanSlow(kVlan99); + EXPECT_EQ(vlan99v0, vlan99v0_byName); + NodeID id99 = vlan99v0->getNodeID(); + EXPECT_NE(id1234, id99); + EXPECT_EQ(VlanID(99), vlan99v0->getID()); + EXPECT_EQ(kVlan99, vlan99v0->getName()); + EXPECT_EQ(0, vlan99v0->getGeneration()); + Vlan::MemberPorts ports99v1; + ports99v1.insert(make_pair(PortID(9), Vlan::PortInfo(false))); + ports99v1.insert(make_pair(PortID(19), Vlan::PortInfo(false))); + ports99v1.insert(make_pair(PortID(29), Vlan::PortInfo(false))); + EXPECT_EQ(ports99v1, vlan99v0->getPorts()); + + // getVlan() should throw on a non-existent VLAN + EXPECT_THROW(vlansV1->getVlan(VlanID(1)), FbossError); + // getVlanIf() should return null on a non-existent VLAN + EXPECT_EQ(nullptr, vlansV1->getVlanIf(VlanID(1233))); + + checkChangedVlans(vlansV0, vlansV1, {}, {99, 1234}, {}); + + // Applying the same config again should result in no change + EXPECT_EQ(nullptr, publishAndApplyConfig(stateV1, &config, &platform)); + + // Drop one port from VLAN 1234 + config.vlanPorts[0] = config.vlanPorts[6]; + config.vlanPorts.resize(6); + auto stateV2 = publishAndApplyConfig(stateV1, &config, &platform); + auto vlansV2 = stateV2->getVlans(); + ASSERT_NE(nullptr, vlansV2); + EXPECT_EQ(2, vlansV2->getGeneration()); + EXPECT_EQ(2, vlansV2->size()); + + // VLAN 1234 should have changed + auto vlan1234v1 = vlansV2->getVlan(VlanID(1234)); + EXPECT_NE(vlan1234v0, vlan1234v1); + EXPECT_EQ(1, vlan1234v1->getGeneration()); + Vlan::MemberPorts ports1234v1; + ports1234v1.insert(make_pair(PortID(2), Vlan::PortInfo(false))); + ports1234v1.insert(make_pair(PortID(3), Vlan::PortInfo(false))); + ports1234v1.insert(make_pair(PortID(4), Vlan::PortInfo(false))); + EXPECT_EQ(ports1234v1, vlan1234v1->getPorts()); + + // VLAN 99 should not have changed + EXPECT_EQ(vlan99v0, vlansV2->getVlan(VlanID(99))); + + checkChangedVlans(vlansV1, vlansV2, {1234}, {}, {}); + EXPECT_EQ(id1234, vlansV2->getVlan(VlanID(1234))->getNodeID()); + EXPECT_EQ(id99, vlansV2->getVlan(VlanID(99))->getNodeID()); + + // Remove VLAN 99 + config.vlans.resize(1); + config.vlans[0].id = 1234; + config.vlanPorts.resize(3); + config.vlanPorts[0].vlanID = 1234; + config.vlanPorts[0].logicalPort = 2; + config.vlanPorts[1].vlanID = 1234; + config.vlanPorts[1].logicalPort = 3; + config.vlanPorts[2].vlanID = 1234; + config.vlanPorts[2].logicalPort = 4; + + auto stateV3 = publishAndApplyConfig(stateV2, &config, &platform); + auto vlansV3 = stateV3->getVlans(); + ASSERT_NE(nullptr, vlansV3); + EXPECT_EQ(3, vlansV3->getGeneration()); + EXPECT_EQ(1, vlansV3->size()); + + // VLAN 1234 should not have changed + EXPECT_EQ(vlan1234v1, vlansV3->getVlan(VlanID(1234))); + // VLAN 99 should no longer exist + EXPECT_EQ(nullptr, vlansV3->getVlanIf(VlanID(99))); + + checkChangedVlans(vlansV2, vlansV3, {}, {}, {99}); +} diff --git a/fboss/agent/hw/sai/SaiVrf.cpp b/fboss/agent/hw/sai/SaiVrf.cpp new file mode 100644 index 0000000000000..6e1e0f3aa2d0c --- /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__; + + pSaiVirtualRouterApi_ = hw->GetSaiVrfApi(); +} + +SaiVrf::~SaiVrf() { + VLOG(4) << "Entering " << __FUNCTION__; + + if (saiVrfId_ != SAI_NULL_OBJECT_ID) { + pSaiVirtualRouterApi_->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 = pSaiVirtualRouterApi_->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..b97dd1961ce1d --- /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 *pSaiVirtualRouterApi_ {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..87c7aebc3b746 --- /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..7f2303ee83e85 --- /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..1143434394a30 --- /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..9e4747c4b04eb --- /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 From dc9610d2472f2d943e2b3d37bb93455f446e04ee Mon Sep 17 00:00:00 2001 From: Zubin Shah Date: Tue, 1 Mar 2016 21:59:39 -0800 Subject: [PATCH 04/24] update build.md text --- BUILD.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/BUILD.md b/BUILD.md index 17aa996caac90..a94c7e79d1e5b 100644 --- a/BUILD.md +++ b/BUILD.md @@ -48,5 +48,5 @@ cmake .. -DWITH_SAI:BOOL=ON make ``` -The produced executables are `sim_agent` and `wedge_agent` or 'sai_agent' (when +The produced executables are `sim_agent` and `wedge_agent` or `sai_agent` (when building with SAI) in the build directory. From 5d2014ecff157154e1921df0444886e980018555 Mon Sep 17 00:00:00 2001 From: Zubin Shah Date: Tue, 1 Mar 2016 22:43:56 -0800 Subject: [PATCH 05/24] added platform files --- CMakeLists.txt | 8 +- fboss/agent/platforms/sai/SaiPlatform.cpp | 100 ++++++++++++++++++ fboss/agent/platforms/sai/SaiPlatform.h | 62 +++++++++++ fboss/agent/platforms/sai/SaiPort.cpp | 46 ++++++++ fboss/agent/platforms/sai/SaiPort.h | 51 +++++++++ fboss/agent/platforms/sai/Sai_ctrl.cpp | 29 +++++ fboss/agent/platforms/sai/Sai_test_ctrl.cpp | 16 +++ fboss/agent/platforms/sai/oss/SaiPlatform.cpp | 28 +++++ fboss/agent/platforms/sai/oss/SaiPort.cpp | 18 ++++ 9 files changed, 355 insertions(+), 3 deletions(-) create mode 100644 fboss/agent/platforms/sai/SaiPlatform.cpp create mode 100644 fboss/agent/platforms/sai/SaiPlatform.h create mode 100644 fboss/agent/platforms/sai/SaiPort.cpp create mode 100644 fboss/agent/platforms/sai/SaiPort.h create mode 100644 fboss/agent/platforms/sai/Sai_ctrl.cpp create mode 100644 fboss/agent/platforms/sai/Sai_test_ctrl.cpp create mode 100644 fboss/agent/platforms/sai/oss/SaiPlatform.cpp create mode 100644 fboss/agent/platforms/sai/oss/SaiPort.cpp diff --git a/CMakeLists.txt b/CMakeLists.txt index f777cf93b724c..8f3317f0a31f1 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -21,6 +21,10 @@ 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}") +if(WITH_SAI) +set(SAIADAPTER libSaiAdapter) +endif() + include_directories(${CMAKE_SOURCE_DIR}) include_directories(${CMAKE_BINARY_DIR}/gen) include_directories(${CMAKE_BINARY_DIR}/gen/fboss/agent/if/gen-cpp2) @@ -89,9 +93,7 @@ add_executable(sai_agent_test fboss/agent/platforms/sai/oss/SaiPort.cpp ) -target_link_libraries(sai_agent_test - fboss_sai_agent -) +target_link_libraries(sai_agent_test fboss_sai_agent) add_library(fboss_sai_agent STATIC fboss/agent/hw/sai/SaiError.cpp diff --git a/fboss/agent/platforms/sai/SaiPlatform.cpp b/fboss/agent/platforms/sai/SaiPlatform.cpp new file mode 100644 index 0000000000000..7545f859f5490 --- /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..01f4dddef72d2 --- /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..eb95d6bfc48f9 --- /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..656b122dfff85 --- /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/Sai_test_ctrl.cpp b/fboss/agent/platforms/sai/Sai_test_ctrl.cpp new file mode 100644 index 0000000000000..de284ab35bcda --- /dev/null +++ b/fboss/agent/platforms/sai/Sai_test_ctrl.cpp @@ -0,0 +1,16 @@ +/* + * 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 "gtest/gtest.h" + +int main(int argc, char **argv) { + ::testing::InitGoogleTest(&argc, argv); + return RUN_ALL_TESTS(); +} 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 From 074b1e475e5ee77fb4f5d509ecc5154969cd716b Mon Sep 17 00:00:00 2001 From: Zubin Shah Date: Tue, 1 Mar 2016 22:46:38 -0800 Subject: [PATCH 06/24] fix cmakelist.txt for SaiPlatform.cpp --- CMakeLists.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 8f3317f0a31f1..a167e448c21f1 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -77,7 +77,7 @@ include_directories(${CMAKE_SOURCE_DIR}/external/wangle) if(WITH_SAI) add_executable(sai_agent - fboss/agent/platforms/sai/SaiPlatforms.cpp + fboss/agent/platforms/sai/SaiPlatform.cpp fboss/agent/platforms/sai/SaiPort.cpp fboss/agent/platforms/sai/Sai_ctrl.cpp fboss/agent/platforms/sai/oss/SaiPlatform.cpp From 606219561c20c987d40e73425dbc442a824d6266 Mon Sep 17 00:00:00 2001 From: Zubin Shah Date: Wed, 2 Mar 2016 05:30:37 -0800 Subject: [PATCH 07/24] update CMakeLists.txt for SAI in fboss/github/* dir --- fboss/github/CMakeLists_SAI.txt | 318 ++++++++++++++++++++++++++++++++ 1 file changed, 318 insertions(+) create mode 100644 fboss/github/CMakeLists_SAI.txt diff --git a/fboss/github/CMakeLists_SAI.txt b/fboss/github/CMakeLists_SAI.txt new file mode 100644 index 0000000000000..395da6f0f7c59 --- /dev/null +++ b/fboss/github/CMakeLists_SAI.txt @@ -0,0 +1,318 @@ +cmake_minimum_required(VERSION 2.8) +project(FBOSS) +include(CMakeParseArguments) + +SET(WITH_SAI OFF CACHE BOOL "Optionally build with SAI adapter") + +# 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++11") +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/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/WedgePlatform.cpp + fboss/agent/platforms/wedge/oss/Wedge100Platform.cpp + fboss/agent/platforms/wedge/oss/WedgePort.cpp + fboss/agent/platforms/wedge/oss/WedgeProductInfo.cpp + fboss/agent/platforms/wedge/wedge_ctrl.cpp + fboss/agent/platforms/wedge/WedgeI2CBusLock.cpp + fboss/agent/platforms/wedge/WedgePlatform.cpp + fboss/agent/platforms/wedge/Wedge100Platform.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) From 3e53fae4667115cff92765475cb69215ec909df8 Mon Sep 17 00:00:00 2001 From: Zubin Shah Date: Wed, 2 Mar 2016 18:42:53 -0800 Subject: [PATCH 08/24] reverting Cmake to only support default wedge-agent. tbd: will work with FB to confirm the update required for SAI_agent --- CMakeLists.txt | 69 -------------------------------------------------- 1 file changed, 69 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index a167e448c21f1..c4fc3f89666b8 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -2,17 +2,12 @@ cmake_minimum_required(VERSION 2.8) project(FBOSS) include(CMakeParseArguments) -SET(WITH_SAI OFF CACHE BOOL "Build with or without sai_agent (default is without)") - # 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_PROPERTY(GLOBAL PROPERTY RULE_LAUNCH_COMPILE ccache) -SET_PROPERTY(GLOBAL PROPERTY RULE_LAUNCH_LINK ccache) - set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=gnu++11") 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") @@ -21,10 +16,6 @@ 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}") -if(WITH_SAI) -set(SAIADAPTER libSaiAdapter) -endif() - include_directories(${CMAKE_SOURCE_DIR}) include_directories(${CMAKE_BINARY_DIR}/gen) include_directories(${CMAKE_BINARY_DIR}/gen/fboss/agent/if/gen-cpp2) @@ -57,71 +48,15 @@ find_library(WANGLE wangle PATHS ${CMAKE_SOURCE_DIR}/external/wangle/wangle/buil 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) -if(WITH_SAI) -find_library(SAIADAPTER SaiAdapter PATHS ${CMAKE_SOURCE_DIR}/external/SAI/lib/) -else() find_library(OPENNSL opennsl PATHS ${CMAKE_SOURCE_DIR}/external/OpenNSL/bin/wedge-trident) -endif() find_library(IPROUTE2 netlink PATHS ${CMAKE_SOURCE_DIR}/external/iproute2/lib) -if(WITH_SAI) -include_directories(${CMAKE_SOURCE_DIR/external/SAI/inc}) -else() include_directories(${CMAKE_SOURCE_DIR}/external/OpenNSL/include) -endif() 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) - -if(WITH_SAI) -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/SaiPlatform.cpp - fboss/agent/platforms/sai/oss/SaiPort.cpp -) -target_link_libraries(sai_agent fboss_sai_agent) - -add_executable(sai_agent_test - fboss/agent/platforms/sai/SaiPlatform.cpp - fboss/agent/platforms/sai/SaiPort.cpp - fboss/agent/platforms/sai/Sai_ctrl.cpp - fboss/agent/platforms/sai/oss/SaiPlatform.cpp - fboss/agent/platforms/sai/oss/SaiPort.cpp -) - -target_link_libraries(sai_agent_test 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 -) - -target_link_libraries(fboss_sai_agent - fboss_agent - ${SAIADAPTER} -) -else() #WEDGE_AGENT - add_executable(wedge_agent fboss/agent/platforms/wedge/WedgePlatform.cpp fboss/agent/platforms/wedge/WedgeProductInfo.cpp @@ -131,11 +66,7 @@ add_executable(wedge_agent fboss/agent/platforms/wedge/oss/WedgePlatform.cpp ) target_link_libraries(wedge_agent fboss_agent) -endif() - - -## TODO: THIS NEEDS TO SEPRATE INTO WEDGE VS SAI add_library(fboss_agent STATIC common/stats/ServiceData.cpp From b523449bc3c40bcca31721f36778af2b51746906 Mon Sep 17 00:00:00 2001 From: Zubin Shah Date: Wed, 2 Mar 2016 19:17:15 -0800 Subject: [PATCH 09/24] revert BUILD instructions to make SAI compile , tbd: will work with FB to make the changes more suitable for FB's needs --- BUILD.md | 16 ++-------------- 1 file changed, 2 insertions(+), 14 deletions(-) diff --git a/BUILD.md b/BUILD.md index a94c7e79d1e5b..9d1fb973a428e 100644 --- a/BUILD.md +++ b/BUILD.md @@ -15,7 +15,6 @@ 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 [SAI-0.9.3](https://github.com/opencomputeproject/SAI/tree/v0.9.3.0) Once the prerequisites are available, take the following steps. @@ -27,9 +26,7 @@ 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 build with -SAI copy your platform specific libSaiAdapter.so to external/SAI/lib directory -or modify path to the library file. +If you use `getdeps.sh`, then this should work out of the box. Build as follows: @@ -40,13 +37,4 @@ cmake .. make ``` -Build as follows (for SAI Agent) -``` -mkdir fboss/build -cd fboss/build -cmake .. -DWITH_SAI:BOOL=ON -make -``` - -The produced executables are `sim_agent` and `wedge_agent` or `sai_agent` (when -building with SAI) in the build directory. +The produced executables are `sim_agent` and `wedge_agent` in the build directory. From 5122ae66f88476983e83b0c8b9b95a8f9f9440de Mon Sep 17 00:00:00 2001 From: Zubin Shah Date: Wed, 2 Mar 2016 20:14:25 -0800 Subject: [PATCH 10/24] remove Sai*Tests.* files --- fboss/agent/hw/sai/SaiInterfaceTests.cpp | 394 -------- fboss/agent/hw/sai/SaiPortTests.cpp | 359 ------- fboss/agent/hw/sai/SaiRouteTests.cpp | 1006 ------------------- fboss/agent/hw/sai/SaiTestUtils.cpp | 266 ----- fboss/agent/hw/sai/SaiTestUtils.h | 197 ---- fboss/agent/hw/sai/SaiVlanTests.cpp | 398 -------- fboss/agent/platforms/sai/Sai_test_ctrl.cpp | 16 - 7 files changed, 2636 deletions(-) delete mode 100644 fboss/agent/hw/sai/SaiInterfaceTests.cpp delete mode 100644 fboss/agent/hw/sai/SaiPortTests.cpp delete mode 100644 fboss/agent/hw/sai/SaiRouteTests.cpp delete mode 100644 fboss/agent/hw/sai/SaiTestUtils.cpp delete mode 100644 fboss/agent/hw/sai/SaiTestUtils.h delete mode 100644 fboss/agent/hw/sai/SaiVlanTests.cpp delete mode 100644 fboss/agent/platforms/sai/Sai_test_ctrl.cpp diff --git a/fboss/agent/hw/sai/SaiInterfaceTests.cpp b/fboss/agent/hw/sai/SaiInterfaceTests.cpp deleted file mode 100644 index 49750781106d0..0000000000000 --- a/fboss/agent/hw/sai/SaiInterfaceTests.cpp +++ /dev/null @@ -1,394 +0,0 @@ -/* - * 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/FbossError.h" -#include "fboss/agent/ApplyThriftConfig.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/StateDelta.h" -#include "fboss/agent/state/SwitchState.h" -#include "fboss/agent/gen-cpp/switch_config_types.h" -#include "fboss/agent/hw/sai/SaiTestUtils.h" -#include "fboss/agent/platforms/sai/SaiPlatform.h" - -#include - -using namespace facebook::fboss; -using folly::IPAddress; -using folly::MacAddress; -using std::make_shared; -using std::shared_ptr; -using ::testing::Return; - -TEST(Interface, addrToReach) { - SaiPlatform platform; - cfg::SwitchConfig config; - config.vlans.resize(2); - config.vlans[0].id = 1; - config.vlans[1].id = 2; - config.interfaces.resize(2); - auto *intfConfig = &config.interfaces[0]; - intfConfig->intfID = 1; - intfConfig->vlanID = 1; - intfConfig->routerID = 1; - intfConfig->mac = "00:02:00:11:22:33"; - intfConfig->__isset.mac = true; - intfConfig->ipAddresses.resize(4); - intfConfig->ipAddresses[0] = "10.1.1.1/24"; - intfConfig->ipAddresses[1] = "20.1.1.2/24"; - intfConfig->ipAddresses[2] = "::22:33:44/120"; - intfConfig->ipAddresses[3] = "::11:11:11/120"; - - intfConfig = &config.interfaces[1]; - intfConfig->intfID = 2; - intfConfig->vlanID = 2; - intfConfig->routerID = 2; - intfConfig->mac = "00:02:00:11:22:33"; - intfConfig->__isset.mac = true; - intfConfig->ipAddresses.resize(4); - intfConfig->ipAddresses[0] = "10.1.1.1/24"; - intfConfig->ipAddresses[1] = "20.1.1.2/24"; - intfConfig->ipAddresses[2] = "::22:33:44/120"; - intfConfig->ipAddresses[3] = "::11:11:11/120"; - - InterfaceID id(1); - shared_ptr oldState = make_shared(); - auto state = publishAndApplyConfig(oldState, &config, &platform); - ASSERT_NE(nullptr, state); - const auto &intfs = state->getInterfaces(); - const auto &intf1 = intfs->getInterface(InterfaceID(1)); - const auto &intf2 = intfs->getInterface(InterfaceID(2)); - - EXPECT_TRUE(intf1->hasAddress(IPAddress("10.1.1.1"))); - EXPECT_FALSE(intf1->hasAddress(IPAddress("10.1.2.1"))); - EXPECT_TRUE(intf2->hasAddress(IPAddress("::11:11:11"))); - EXPECT_FALSE(intf2->hasAddress(IPAddress("::11:11:12"))); - - auto ret = intfs->getIntfAddrToReach(RouterID(1), IPAddress("20.1.1.100")); - EXPECT_EQ(intf1.get(), ret.intf); - EXPECT_EQ(IPAddress("20.1.1.2"), *ret.addr); - EXPECT_EQ(24, ret.mask); - - ret = intfs->getIntfAddrToReach(RouterID(2), IPAddress("::22:33:4f")); - EXPECT_EQ(intf2.get(), ret.intf); - EXPECT_EQ(IPAddress("::22:33:44"), *ret.addr); - EXPECT_EQ(120, ret.mask); - - ret = intfs->getIntfAddrToReach(RouterID(2), IPAddress("::22:34:5f")); - EXPECT_EQ(nullptr, ret.intf); - EXPECT_EQ(nullptr, ret.addr); - EXPECT_EQ(0, ret.mask); -} - -TEST(Interface, applyConfig) { - SaiPlatform platform; - cfg::SwitchConfig config; - config.vlans.resize(2); - config.vlans[0].id = 1; - config.vlans[1].id = 2; - config.interfaces.resize(1); - auto *intfConfig = &config.interfaces[0]; - intfConfig->intfID = 1; - intfConfig->vlanID = 1; - intfConfig->routerID = 0; - intfConfig->mac = "00:02:00:11:22:33"; - intfConfig->__isset.mac = true; - - InterfaceID id(1); - shared_ptr oldState; - shared_ptr state; - shared_ptr oldInterface; - shared_ptr interface; - auto updateState = [&]() { - oldState = state; - oldInterface = interface; - state = publishAndApplyConfig(oldState, &config, &platform); - EXPECT_NE(oldState, state); - ASSERT_NE(nullptr, state); - interface = state->getInterfaces()->getInterface(id); - EXPECT_NE(oldInterface, interface); - ASSERT_NE(nullptr, interface); - }; - - state = make_shared(); - updateState(); - NodeID nodeID = interface->getNodeID(); - EXPECT_EQ(0, interface->getGeneration()); - EXPECT_EQ(VlanID(1), interface->getVlanID()); - EXPECT_EQ(RouterID(0), interface->getRouterID()); - EXPECT_EQ("Interface 1", interface->getName()); - EXPECT_EQ(MacAddress("00:02:00:11:22:33"), interface->getMac()); - EXPECT_EQ(Interface::Addresses {}, interface->getAddresses()); - EXPECT_EQ(0, interface->getNdpConfig().routerAdvertisementSeconds); - - // same configuration cause nothing changed - EXPECT_EQ(nullptr, publishAndApplyConfig(state, &config, &platform)); - - // vlanID change - intfConfig->vlanID = 2; - updateState(); - EXPECT_EQ(nodeID, interface->getNodeID()); - EXPECT_EQ(oldInterface->getGeneration() + 1, interface->getGeneration()); - EXPECT_EQ(VlanID(2), interface->getVlanID()); - EXPECT_EQ(RouterID(0), interface->getRouterID()); - EXPECT_EQ(oldInterface->getName(), interface->getName()); - EXPECT_EQ(oldInterface->getMac(), interface->getMac()); - EXPECT_EQ(oldInterface->getAddresses(), interface->getAddresses()); - - // routerID change - intfConfig->routerID = 1; - updateState(); - EXPECT_EQ(nodeID, interface->getNodeID()); - EXPECT_EQ(oldInterface->getGeneration() + 1, interface->getGeneration()); - EXPECT_EQ(VlanID(2), interface->getVlanID()); - EXPECT_EQ(RouterID(1), interface->getRouterID()); - EXPECT_EQ(oldInterface->getName(), interface->getName()); - EXPECT_EQ(oldInterface->getMac(), interface->getMac()); - EXPECT_EQ(oldInterface->getAddresses(), interface->getAddresses()); - - // MAC address change - intfConfig->mac = "00:02:00:12:34:56"; - updateState(); - EXPECT_EQ(oldInterface->getGeneration() + 1, interface->getGeneration()); - EXPECT_EQ(VlanID(2), interface->getVlanID()); - EXPECT_EQ(RouterID(1), interface->getRouterID()); - EXPECT_EQ(oldInterface->getName(), interface->getName()); - EXPECT_EQ(MacAddress("00:02:00:12:34:56"), interface->getMac()); - EXPECT_EQ(oldInterface->getAddresses(), interface->getAddresses()); - // Use the platform supplied MAC - intfConfig->mac = ""; - intfConfig->__isset.mac = false; - MacAddress platformMac("00:02:00:ab:cd:ef"); -// EXPECT_CALL(platform, getLocalMac()).WillRepeatedly(Return(platformMac)); - updateState(); - EXPECT_EQ(nodeID, interface->getNodeID()); - EXPECT_EQ(oldInterface->getGeneration() + 1, interface->getGeneration()); - EXPECT_EQ(oldInterface->getVlanID(), interface->getVlanID()); - EXPECT_EQ(oldInterface->getRouterID(), interface->getRouterID()); - EXPECT_EQ(oldInterface->getName(), interface->getName()); - EXPECT_EQ(platformMac, interface->getMac()); - EXPECT_EQ(oldInterface->getAddresses(), interface->getAddresses()); - - // IP addresses change - intfConfig->ipAddresses.resize(4); - intfConfig->ipAddresses[0] = "10.1.1.1/24"; - intfConfig->ipAddresses[1] = "20.1.1.2/24"; - intfConfig->ipAddresses[2] = "::22:33:44/120"; - intfConfig->ipAddresses[3] = "::11:11:11/120"; - updateState(); - EXPECT_EQ(nodeID, interface->getNodeID()); - EXPECT_EQ(oldInterface->getGeneration() + 1, interface->getGeneration()); - EXPECT_EQ(VlanID(2), interface->getVlanID()); - EXPECT_EQ(RouterID(1), interface->getRouterID()); - EXPECT_EQ(oldInterface->getName(), interface->getName()); - EXPECT_EQ(oldInterface->getMac(), interface->getMac()); - EXPECT_EQ(4, interface->getAddresses().size()); - - // change the order of IP address shall not change the interface - intfConfig->ipAddresses[0] = "10.1.1.1/24"; - intfConfig->ipAddresses[1] = "::22:33:44/120"; - intfConfig->ipAddresses[2] = "20.1.1.2/24"; - intfConfig->ipAddresses[3] = "::11:11:11/120"; - EXPECT_EQ(nullptr, publishAndApplyConfig(state, &config, &platform)); - - // duplicate IP addresses causes throw - intfConfig->ipAddresses[1] = intfConfig->ipAddresses[0]; - EXPECT_THROW(publishAndApplyConfig(state, &config, &platform), FbossError); - // Should still throw even if the mask is different - intfConfig->ipAddresses[1] = "10.1.1.1/16"; - EXPECT_THROW(publishAndApplyConfig(state, &config, &platform), FbossError); - intfConfig->ipAddresses[1] = "::22:33:44/120"; - - // Name change - intfConfig->name = "myintf"; - intfConfig->__isset.name = true; - updateState(); - EXPECT_EQ(nodeID, interface->getNodeID()); - EXPECT_EQ(oldInterface->getGeneration() + 1, interface->getGeneration()); - EXPECT_EQ("myintf", interface->getName()); - EXPECT_EQ(oldInterface->getVlanID(), interface->getVlanID()); - EXPECT_EQ(oldInterface->getRouterID(), interface->getRouterID()); - EXPECT_EQ(oldInterface->getMac(), interface->getMac()); - EXPECT_EQ(oldInterface->getAddresses(), interface->getAddresses()); - // Reset the name back to it's default value - intfConfig->__isset.name = false; - updateState(); - EXPECT_EQ(nodeID, interface->getNodeID()); - EXPECT_EQ(oldInterface->getGeneration() + 1, interface->getGeneration()); - EXPECT_EQ("Interface 1", interface->getName()); - EXPECT_EQ(oldInterface->getVlanID(), interface->getVlanID()); - EXPECT_EQ(oldInterface->getRouterID(), interface->getRouterID()); - EXPECT_EQ(oldInterface->getMac(), interface->getMac()); - EXPECT_EQ(oldInterface->getAddresses(), interface->getAddresses()); - EXPECT_EQ(oldInterface->getNdpConfig(), interface->getNdpConfig()); - - // Change the NDP configuration - intfConfig->__isset.ndp = true; - intfConfig->ndp.routerAdvertisementSeconds = 4; - updateState(); - EXPECT_EQ(nodeID, interface->getNodeID()); - EXPECT_EQ(oldInterface->getGeneration() + 1, interface->getGeneration()); - EXPECT_EQ(oldInterface->getName(), interface->getName()); - EXPECT_EQ(oldInterface->getVlanID(), interface->getVlanID()); - EXPECT_EQ(oldInterface->getRouterID(), interface->getRouterID()); - EXPECT_EQ(oldInterface->getMac(), interface->getMac()); - EXPECT_EQ(oldInterface->getAddresses(), interface->getAddresses()); - EXPECT_NE(oldInterface->getNdpConfig(), interface->getNdpConfig()); - EXPECT_EQ(4, interface->getNdpConfig().routerAdvertisementSeconds); - // Update the RA interval to 30 seconds - intfConfig->ndp.routerAdvertisementSeconds = 30; - updateState(); - EXPECT_EQ(nodeID, interface->getNodeID()); - EXPECT_EQ(oldInterface->getGeneration() + 1, interface->getGeneration()); - EXPECT_NE(oldInterface->getNdpConfig(), interface->getNdpConfig()); - EXPECT_EQ(30, interface->getNdpConfig().routerAdvertisementSeconds); - // Drop the NDP configuration - intfConfig->__isset.ndp = false; - updateState(); - EXPECT_EQ(nodeID, interface->getNodeID()); - EXPECT_EQ(oldInterface->getGeneration() + 1, interface->getGeneration()); - EXPECT_NE(oldInterface->getNdpConfig(), interface->getNdpConfig()); - EXPECT_EQ(0, interface->getNdpConfig().routerAdvertisementSeconds); - - // Changing the ID creates a new interface - intfConfig->intfID = 2; - id = InterfaceID(2); - updateState(); - // The generation number for the new interface will be 0 - EXPECT_NE(nodeID, interface->getNodeID()); - EXPECT_EQ(0, interface->getGeneration()); - EXPECT_EQ(oldInterface->getVlanID(), interface->getVlanID()); - EXPECT_EQ(oldInterface->getRouterID(), interface->getRouterID()); - EXPECT_EQ("Interface 2", interface->getName()); - EXPECT_EQ(oldInterface->getMac(), interface->getMac()); - EXPECT_EQ(oldInterface->getAddresses(), interface->getAddresses()); -} - -/* - * Test that forEachChanged(StateDelta::getIntfsDelta(), ...) invokes the - * callback for the specified list of changed interfaces. - */ -void checkChangedIntfs(const shared_ptr &oldIntfs, - const shared_ptr &newIntfs, - const std::set changedIDs, - const std::set addedIDs, - const std::set removedIDs) { - auto oldState = make_shared(); - oldState->resetIntfs(oldIntfs); - auto newState = make_shared(); - newState->resetIntfs(newIntfs); - - std::set foundChanged; - std::set foundAdded; - std::set foundRemoved; - StateDelta delta(oldState, newState); - DeltaFunctions::forEachChanged(delta.getIntfsDelta(), - [&] (const shared_ptr &oldIntf, - const shared_ptr &newIntf) { - EXPECT_EQ(oldIntf->getID(), newIntf->getID()); - EXPECT_NE(oldIntf, newIntf); - - auto ret = foundChanged.insert(oldIntf->getID()); - EXPECT_TRUE(ret.second); - }, - [&] (const shared_ptr &intf) { - auto ret = foundAdded.insert(intf->getID()); - EXPECT_TRUE(ret.second); - }, - [&] (const shared_ptr &intf) { - auto ret = foundRemoved.insert(intf->getID()); - EXPECT_TRUE(ret.second); - }); - - EXPECT_EQ(changedIDs, foundChanged); - EXPECT_EQ(addedIDs, foundAdded); - EXPECT_EQ(removedIDs, foundRemoved); -} - -TEST(InterfaceMap, applyConfig) { - SaiPlatform platform; - auto stateV0 = make_shared(); - auto intfsV0 = stateV0->getInterfaces(); - - cfg::SwitchConfig config; - config.vlans.resize(3); - config.vlans[0].id = 1; - config.vlans[1].id = 2; - config.vlans[2].id = 3; - config.interfaces.resize(2); - config.interfaces[0].intfID = 1; - config.interfaces[0].vlanID = 1; - config.interfaces[0].__isset.mac = true; - config.interfaces[0].mac = "00:00:00:00:00:11"; - config.interfaces[1].intfID = 2; - config.interfaces[1].vlanID = 2; - config.interfaces[1].__isset.mac = true; - config.interfaces[1].mac = "00:00:00:00:00:22"; - - auto stateV1 = publishAndApplyConfig(stateV0, &config, &platform); - ASSERT_NE(nullptr, stateV1); - auto intfsV1 = stateV1->getInterfaces(); - EXPECT_NE(intfsV1, intfsV0); - EXPECT_EQ(1, intfsV1->getGeneration()); - EXPECT_EQ(2, intfsV1->size()); - - // verify interface intfID==1 - auto intf1 = intfsV1->getInterface(InterfaceID(1)); - ASSERT_NE(nullptr, intf1); - EXPECT_EQ(VlanID(1), intf1->getVlanID()); - EXPECT_EQ("00:00:00:00:00:11", intf1->getMac().toString()); - EXPECT_EQ(0, intf1->getGeneration()); - - checkChangedIntfs(intfsV0, intfsV1, {}, {1, 2}, {}); - - // getInterface() should throw on a non-existent interface - EXPECT_THROW(intfsV1->getInterface(InterfaceID(99)), FbossError); - // getInterfaceIf() should return nullptr on a non-existent interface - EXPECT_EQ(nullptr, intfsV1->getInterfaceIf(InterfaceID(99))); - - // applying the same configure results in no change - EXPECT_EQ(nullptr, publishAndApplyConfig(stateV1, &config, &platform)); - - // adding some IP addresses - config.interfaces[1].ipAddresses.resize(2); - config.interfaces[1].ipAddresses[0] = "192.168.1.1/16"; - config.interfaces[1].ipAddresses[1] = "::1/48"; - auto stateV2 = publishAndApplyConfig(stateV1, &config, &platform); - ASSERT_NE(nullptr, stateV2); - auto intfsV2 = stateV2->getInterfaces(); - EXPECT_NE(intfsV1, intfsV2); - EXPECT_EQ(2, intfsV2->getGeneration()); - EXPECT_EQ(2, intfsV2->size()); - auto intf2 = intfsV2->getInterface(InterfaceID(2)); - EXPECT_EQ(2, intf2->getAddresses().size()); - - checkChangedIntfs(intfsV1, intfsV2, {2}, {}, {}); - - // add a new one together with deleting an existing one - config.interfaces[0].intfID = 3; - config.interfaces[0].vlanID = 3; - config.interfaces[0].__isset.mac = true; - config.interfaces[0].mac = "00:00:00:00:00:33"; - auto stateV3 = publishAndApplyConfig(stateV2, &config, &platform); - ASSERT_NE(nullptr, stateV3); - auto intfsV3 = stateV3->getInterfaces(); - EXPECT_NE(intfsV2, intfsV3); - EXPECT_EQ(3, intfsV3->getGeneration()); - EXPECT_EQ(2, intfsV3->size()); - auto intf3 = intfsV3->getInterface(InterfaceID(3)); - EXPECT_EQ(0, intf3->getAddresses().size()); - EXPECT_EQ(config.interfaces[0].mac, intf3->getMac().toString()); - // intf 1 should not be there anymroe - EXPECT_EQ(nullptr, intfsV3->getInterfaceIf(InterfaceID(1))); - - checkChangedIntfs(intfsV2, intfsV3, {}, {3}, {1}); -} diff --git a/fboss/agent/hw/sai/SaiPortTests.cpp b/fboss/agent/hw/sai/SaiPortTests.cpp deleted file mode 100644 index 8b2f71a417bff..0000000000000 --- a/fboss/agent/hw/sai/SaiPortTests.cpp +++ /dev/null @@ -1,359 +0,0 @@ -/* - * 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/ApplyThriftConfig.h" -#include "fboss/agent/FbossError.h" -#include "fboss/agent/state/DeltaFunctions.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/hw/sai/SaiTestUtils.h" -#include "fboss/agent/platforms/sai/SaiPlatform.h" - -#include - -using namespace facebook::fboss; -using std::make_pair; -using std::make_shared; -using std::shared_ptr; - -TEST(Port, applyConfig) { - SaiPlatform platform; - auto stateV0 = make_shared(); - stateV0->registerPort(PortID(1), "port1"); - auto portV0 = stateV0->getPort(PortID(1)); - EXPECT_EQ(0, portV0->getGeneration()); - EXPECT_FALSE(portV0->isPublished()); - EXPECT_EQ(PortID(1), portV0->getID()); - EXPECT_EQ("port1", portV0->getName()); - EXPECT_EQ(cfg::PortState::DOWN, portV0->getState()); - Port::VlanMembership emptyVlans; - EXPECT_EQ(emptyVlans, portV0->getVlans()); - - portV0->publish(); - EXPECT_TRUE(portV0->isPublished()); - - cfg::SwitchConfig config; - config.ports.resize(1); - config.ports[0].logicalID = 1; - config.ports[0].state = cfg::PortState::UP; - config.vlans.resize(2); - config.vlans[0].id = 2; - config.vlans[1].id = 5; - config.vlanPorts.resize(2); - config.vlanPorts[0].logicalPort = 1; - config.vlanPorts[0].vlanID = 2; - config.vlanPorts[0].emitTags = false; - config.vlanPorts[1].logicalPort = 1; - config.vlanPorts[1].vlanID = 5; - config.vlanPorts[1].emitTags = true; - - auto stateV1 = publishAndApplyConfig(stateV0, &config, &platform); - auto portV1 = stateV1->getPort(PortID(1)); - ASSERT_NE(nullptr, portV1); - EXPECT_NE(portV0, portV1); - - EXPECT_EQ(PortID(1), portV1->getID()); - EXPECT_EQ("port1", portV1->getName()); - EXPECT_EQ(1, portV1->getGeneration()); - EXPECT_EQ(cfg::PortState::UP, portV1->getState()); - EXPECT_FALSE(portV1->isPublished()); - Port::VlanMembership expectedVlans; - expectedVlans.insert(make_pair(VlanID(2), Port::VlanInfo(false))); - expectedVlans.insert(make_pair(VlanID(5), Port::VlanInfo(true))); - EXPECT_EQ(expectedVlans, portV1->getVlans()); - - // Applying the same config again should result in no changes - EXPECT_EQ(nullptr, publishAndApplyConfig(stateV1, &config, &platform)); - - // Applying the same config with a new VLAN list should result in changes - config.vlanPorts.resize(1); - config.vlanPorts[0].logicalPort = 1; - config.vlanPorts[0].vlanID = 2021; - config.vlanPorts[0].emitTags = false; - - Port::VlanMembership expectedVlansV2; - expectedVlansV2.insert(make_pair(VlanID(2021), Port::VlanInfo(false))); - auto stateV2 = publishAndApplyConfig(stateV1, &config, &platform); - auto portV2 = stateV2->getPort(PortID(1)); - ASSERT_NE(nullptr, portV2); - EXPECT_NE(portV1, portV2); - - EXPECT_EQ(PortID(1), portV2->getID()); - EXPECT_EQ("port1", portV2->getName()); - EXPECT_EQ(2, portV2->getGeneration()); - EXPECT_EQ(cfg::PortState::UP, portV2->getState()); - EXPECT_FALSE(portV2->isPublished()); - EXPECT_EQ(expectedVlansV2, portV2->getVlans()); - - // Applying the same config with a different speed should result in changes - config.ports[0].speed = cfg::PortSpeed::GIGE; - - auto stateV3 = publishAndApplyConfig(stateV2, &config, &platform); - auto portV3 = stateV3->getPort(PortID(1)); - ASSERT_NE(nullptr, portV3); - EXPECT_NE(portV2, portV3); - EXPECT_EQ(cfg::PortSpeed::GIGE, portV3->getSpeed()); - - // Attempting to apply a config with a non-existent PortID should fail. - config.ports[0].logicalID = 2; - EXPECT_THROW(publishAndApplyConfig(stateV3, &config, &platform), FbossError); -} - -TEST(Port, initDefaultConfig) { - SaiPlatform platform; - PortID portID(1); - auto state = make_shared(); - state->registerPort(portID, "port1"); - - // Applying an empty config should result in no changes. - cfg::SwitchConfig config; - EXPECT_EQ(nullptr, publishAndApplyConfig(state, &config, &platform)); - - // Adding a port entry in the config and initializing it with - // initDefaultConfig() should also result in no changes. - config.ports.resize(1); - state->getPort(portID)->initDefaultConfig(&config.ports[0]); - EXPECT_EQ(nullptr, publishAndApplyConfig(state, &config, &platform)); -} - -TEST(PortMap, registerPorts) { - auto ports = make_shared(); - EXPECT_EQ(0, ports->getGeneration()); - EXPECT_FALSE(ports->isPublished()); - EXPECT_EQ(0, ports->numPorts()); - - ports->registerPort(PortID(1), "port1"); - ports->registerPort(PortID(2), "port2"); - ports->registerPort(PortID(3), "port3"); - ports->registerPort(PortID(4), "port4"); - EXPECT_EQ(4, ports->numPorts()); - - auto port1 = ports->getPort(PortID(1)); - auto port2 = ports->getPort(PortID(2)); - auto port3 = ports->getPort(PortID(3)); - auto port4 = ports->getPort(PortID(4)); - EXPECT_EQ(PortID(1), port1->getID()); - EXPECT_EQ("port1", port1->getName()); - EXPECT_EQ(PortID(4), port4->getID()); - EXPECT_EQ("port4", port4->getName()); - - // Attempting to register a duplicate port ID should fail - EXPECT_THROW(ports->registerPort(PortID(2), "anotherPort2"), - FbossError); - - // Registering non-sequential IDs should work - ports->registerPort(PortID(10), "port10"); - EXPECT_EQ(5, ports->numPorts()); - auto port10 = ports->getPort(PortID(10)); - EXPECT_EQ(PortID(10), port10->getID()); - EXPECT_EQ("port10", port10->getName()); - - // Getting non-existent ports should fail - EXPECT_THROW(ports->getPort(PortID(0)), FbossError); - EXPECT_THROW(ports->getPort(PortID(7)), FbossError); - EXPECT_THROW(ports->getPort(PortID(300)), FbossError); - - // Publishing the PortMap should also mark all ports as published - ports->publish(); - EXPECT_TRUE(ports->isPublished()); - EXPECT_TRUE(port1->isPublished()); - EXPECT_TRUE(port2->isPublished()); - EXPECT_TRUE(port3->isPublished()); - EXPECT_TRUE(port4->isPublished()); - EXPECT_TRUE(port10->isPublished()); - - // Attempting to register new ports after the PortMap has been published - // should crash. - ASSERT_DEATH(ports->registerPort(PortID(5), "port5"), - "Check failed: !isPublished()"); -} - -/* - * Test that forEachChanged(StateDelta::getPortsDelta(), ...) invokes the - * callback for the specified list of changed ports. - */ -void checkChangedPorts(const shared_ptr &oldPorts, - const shared_ptr &newPorts, - const std::set changedIDs) { - auto oldState = make_shared(); - oldState->resetPorts(oldPorts); - auto newState = make_shared(); - newState->resetPorts(newPorts); - - std::set invokedPorts; - StateDelta delta(oldState, newState); - DeltaFunctions::forEachChanged(delta.getPortsDelta(), - [&] (const shared_ptr &oldPort, - const shared_ptr &newPort) { - EXPECT_EQ(oldPort->getID(), newPort->getID()); - EXPECT_NE(oldPort, newPort); - - auto ret = invokedPorts.insert(oldPort->getID()); - EXPECT_TRUE(ret.second); - }); - - EXPECT_EQ(changedIDs, invokedPorts); -} - -TEST(PortMap, applyConfig) { - SaiPlatform platform; - auto stateV0 = make_shared(); - auto portsV0 = stateV0->getPorts(); - portsV0->registerPort(PortID(1), "port1"); - portsV0->registerPort(PortID(2), "port2"); - portsV0->registerPort(PortID(3), "port3"); - portsV0->registerPort(PortID(4), "port4"); - portsV0->publish(); - EXPECT_EQ(0, portsV0->getGeneration()); - auto port1 = portsV0->getPort(PortID(1)); - auto port2 = portsV0->getPort(PortID(2)); - auto port3 = portsV0->getPort(PortID(3)); - auto port4 = portsV0->getPort(PortID(4)); - - // Applying an empty config shouldn't change a newly-constructed PortMap - cfg::SwitchConfig config; - EXPECT_EQ(nullptr, publishAndApplyConfig(stateV0, &config, &platform)); - - // Enable port 2 - config.ports.resize(1); - config.ports[0].logicalID = 2; - config.ports[0].state = cfg::PortState::UP; - auto stateV1 = publishAndApplyConfig(stateV0, &config, &platform); - auto portsV1 = stateV1->getPorts(); - ASSERT_NE(nullptr, portsV1); - EXPECT_EQ(1, portsV1->getGeneration()); - EXPECT_EQ(4, portsV1->numPorts()); - - // Only port 2 should have changed - EXPECT_EQ(port1, portsV1->getPort(PortID(1))); - EXPECT_NE(port2, portsV1->getPort(PortID(2))); - EXPECT_EQ(port3, portsV1->getPort(PortID(3))); - EXPECT_EQ(port4, portsV1->getPort(PortID(4))); - checkChangedPorts(portsV0, portsV1, {2}); - - auto newPort2 = portsV1->getPort(PortID(2)); - EXPECT_EQ(cfg::PortState::UP, newPort2->getState()); - EXPECT_EQ(cfg::PortState::DOWN, port1->getState()); - EXPECT_EQ(cfg::PortState::DOWN, port3->getState()); - EXPECT_EQ(cfg::PortState::DOWN, port4->getState()); - - // The new PortMap and port 2 should still be unpublished. - // The remaining other ports are the same and were previously published - EXPECT_FALSE(portsV1->isPublished()); - EXPECT_FALSE(newPort2->isPublished()); - EXPECT_TRUE(port1->isPublished()); - // Publish portsV1 now. - portsV1->publish(); - EXPECT_TRUE(portsV1->isPublished()); - EXPECT_TRUE(newPort2->isPublished()); - EXPECT_TRUE(port1->isPublished()); - - // Applying the same config again should do nothing. - EXPECT_EQ(nullptr, publishAndApplyConfig(stateV1, &config, &platform)); - - // Now mark all ports up - config.ports.resize(4); - config.ports[0].logicalID = 1; - config.ports[0].state = cfg::PortState::UP; - config.ports[1].logicalID = 2; - config.ports[1].state = cfg::PortState::UP; - config.ports[2].logicalID = 3; - config.ports[2].state = cfg::PortState::UP; - config.ports[3].logicalID = 4; - config.ports[3].state = cfg::PortState::UP; - - auto stateV2 = publishAndApplyConfig(stateV1, &config, &platform); - auto portsV2 = stateV2->getPorts(); - ASSERT_NE(nullptr, portsV2); - EXPECT_EQ(2, portsV2->getGeneration()); - - EXPECT_NE(port1, portsV2->getPort(PortID(1))); - EXPECT_EQ(newPort2, portsV2->getPort(PortID(2))); - EXPECT_NE(port3, portsV2->getPort(PortID(3))); - EXPECT_NE(port4, portsV2->getPort(PortID(4))); - - EXPECT_EQ(cfg::PortState::UP, portsV2->getPort(PortID(1))->getState()); - EXPECT_EQ(cfg::PortState::UP, portsV2->getPort(PortID(2))->getState()); - EXPECT_EQ(cfg::PortState::UP, portsV2->getPort(PortID(3))->getState()); - EXPECT_EQ(cfg::PortState::UP, portsV2->getPort(PortID(4))->getState()); - checkChangedPorts(portsV1, portsV2, {1, 3, 4}); - - EXPECT_FALSE(portsV2->getPort(PortID(1))->isPublished()); - EXPECT_TRUE(portsV2->getPort(PortID(2))->isPublished()); - EXPECT_FALSE(portsV2->getPort(PortID(3))->isPublished()); - EXPECT_FALSE(portsV2->getPort(PortID(4))->isPublished()); - portsV2->publish(); - EXPECT_TRUE(portsV2->getPort(PortID(1))->isPublished()); - EXPECT_TRUE(portsV2->getPort(PortID(2))->isPublished()); - EXPECT_TRUE(portsV2->getPort(PortID(3))->isPublished()); - EXPECT_TRUE(portsV2->getPort(PortID(4))->isPublished()); - - // Applying a config with ports that don't already exist should fail - config.ports[0].logicalID = 10; - config.ports[0].state = cfg::PortState::UP; - EXPECT_THROW(publishAndApplyConfig(stateV2, &config, &platform), FbossError); - - // If we remove port3 from the config, it should be marked down - config.ports.resize(3); - config.ports[0].logicalID = 1; - config.ports[2].logicalID = 4; - auto stateV3 = publishAndApplyConfig(stateV2, &config, &platform); - auto portsV3 = stateV3->getPorts(); - ASSERT_NE(nullptr, portsV3); - EXPECT_EQ(3, portsV3->getGeneration()); - - EXPECT_EQ(4, portsV3->numPorts()); - EXPECT_EQ(cfg::PortState::UP, portsV3->getPort(PortID(1))->getState()); - EXPECT_EQ(cfg::PortState::UP, portsV3->getPort(PortID(2))->getState()); - EXPECT_EQ(cfg::PortState::DOWN, portsV3->getPort(PortID(3))->getState()); - EXPECT_EQ(cfg::PortState::UP, portsV3->getPort(PortID(4))->getState()); - checkChangedPorts(portsV2, portsV3, {3}); -} - -TEST(PortMap, iterateOrder) { - // The NodeMapDelta::Iterator code assumes that the PortMap iterator walks - // through the ports in sorted order (sorted by PortID). - // - // Add a test to ensure that this always remains true. (If we ever change - // the underlying map data structure used for PortMap, we will need to update - // the StateDelta code.) - auto ports = make_shared(); - ports->registerPort(PortID(99), "a"); - ports->registerPort(PortID(37), "b"); - ports->registerPort(PortID(88), "c"); - ports->registerPort(PortID(4), "d"); - ports->publish(); - - auto it = ports->begin(); - ASSERT_NE(ports->end(), it); - EXPECT_EQ(PortID(4), (*it)->getID()); - EXPECT_EQ("d", (*it)->getName()); - - ++it; - ASSERT_NE(ports->end(), it); - EXPECT_EQ(PortID(37), (*it)->getID()); - EXPECT_EQ("b", (*it)->getName()); - - ++it; - ASSERT_NE(ports->end(), it); - EXPECT_EQ(PortID(88), (*it)->getID()); - EXPECT_EQ("c", (*it)->getName()); - - ++it; - ASSERT_NE(ports->end(), it); - EXPECT_EQ(PortID(99), (*it)->getID()); - EXPECT_EQ("a", (*it)->getName()); - - ++it; - EXPECT_EQ(ports->end(), it); -} diff --git a/fboss/agent/hw/sai/SaiRouteTests.cpp b/fboss/agent/hw/sai/SaiRouteTests.cpp deleted file mode 100644 index d66d09e84907e..0000000000000 --- a/fboss/agent/hw/sai/SaiRouteTests.cpp +++ /dev/null @@ -1,1006 +0,0 @@ -/* - * 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/FbossError.h" -#include "fboss/agent/ApplyThriftConfig.h" -#include "fboss/agent/state/DeltaFunctions.h" -#include "fboss/agent/state/Interface.h" -#include "fboss/agent/state/InterfaceMap.h" -#include "fboss/agent/state/Route.h" -#include "fboss/agent/state/RouteTableRib.h" -#include "fboss/agent/state/RouteTable.h" -#include "fboss/agent/state/RouteUpdater.h" -#include "fboss/agent/state/RouteTableMap.h" -#include "fboss/agent/state/RouteDelta.h" -#include "fboss/agent/state/NodeMapDelta.h" -#include "fboss/agent/state/StateDelta.h" -#include "fboss/agent/state/SwitchState.h" -#include "fboss/agent/hw/sai/SaiTestUtils.h" -#include "fboss/agent/platforms/sai/SaiPlatform.h" -#include "fboss/agent/gen-cpp/switch_config_types.h" - -#include - -using namespace facebook::fboss; -using folly::IPAddress; -using folly::IPAddressV4; -using folly::IPAddressV6; -using std::make_shared; -using std::shared_ptr; -using ::testing::Return; - -TEST(RouteUpdater, dedup) { - SaiPlatform platform; - auto stateV0 = make_shared(); - auto tablesV0 = stateV0->getRouteTables(); - - cfg::SwitchConfig config; - config.vlans.resize(1); - config.vlans[0].id = 1; - - config.interfaces.resize(2); - config.interfaces[0].intfID = 1; - config.interfaces[0].vlanID = 1; - config.interfaces[0].routerID = 0; - config.interfaces[0].__isset.mac = true; - config.interfaces[0].mac = "00:00:00:00:00:11"; - config.interfaces[0].ipAddresses.resize(2); - config.interfaces[0].ipAddresses[0] = "1.1.1.1/24"; - config.interfaces[0].ipAddresses[1] = "1::1/48"; - config.interfaces[1].intfID = 2; - config.interfaces[1].vlanID = 1; - config.interfaces[1].routerID = 0; - config.interfaces[1].__isset.mac = true; - config.interfaces[1].mac = "00:00:00:00:00:22"; - config.interfaces[1].ipAddresses.resize(2); - config.interfaces[1].ipAddresses[0] = "2.2.2.2/24"; - config.interfaces[1].ipAddresses[1] = "2::1/48"; - - auto stateV1 = publishAndApplyConfig(stateV0, &config, &platform); - ASSERT_NE(nullptr, stateV1); - stateV1->publish(); - auto rid = RouterID(0); - // 2 different nexthops - RouteNextHops nhop1; - nhop1.emplace(IPAddress("1.1.1.10")); // resolved by intf 1 - RouteNextHops nhop2; - nhop2.emplace(IPAddress("2.2.2.10")); // resolved by intf 2 - // 4 prefixes - RouteV4::Prefix r1 {IPAddressV4("10.1.1.0"), 24}; - RouteV4::Prefix r2 {IPAddressV4("20.1.1.0"), 24}; - RouteV6::Prefix r3 {IPAddressV6("1001::0"), 48}; - RouteV6::Prefix r4 {IPAddressV6("2001::0"), 48}; - - const auto &tables1 = stateV1->getRouteTables(); - RouteUpdater u2(tables1); - u2.addRoute(rid, IPAddress(r1.network), r1.mask, nhop1); - u2.addRoute(rid, IPAddress(r2.network), r2.mask, nhop2); - u2.addRoute(rid, IPAddress(r3.network), r3.mask, nhop1); - u2.addRoute(rid, IPAddress(r4.network), r4.mask, nhop2); - auto tables2 = u2.updateDone(); - ASSERT_NE(nullptr, tables2); - tables2->publish(); - - // start from empty table now, and then re-add the same routes - RouteUpdater u3(tables2, true); - u3.addInterfaceAndLinkLocalRoutes(stateV1->getInterfaces()); - u3.addRoute(rid, IPAddress(r1.network), r1.mask, nhop1); - u3.addRoute(rid, IPAddress(r2.network), r2.mask, nhop2); - u3.addRoute(rid, IPAddress(r3.network), r3.mask, nhop1); - u3.addRoute(rid, IPAddress(r4.network), r4.mask, nhop2); - auto tables3 = u3.updateDone(); - EXPECT_EQ(nullptr, tables3); - - // start from empty table now, and then re-add the same routes, - // except for one difference - RouteUpdater u4(tables2, true); - u4.addInterfaceAndLinkLocalRoutes(stateV1->getInterfaces()); - u4.addRoute(rid, IPAddress(r1.network), r1.mask, nhop1); - u4.addRoute(rid, IPAddress(r2.network), r2.mask, nhop1); // different nexthop - u4.addRoute(rid, IPAddress(r3.network), r3.mask, nhop1); - u4.addRoute(rid, IPAddress(r4.network), r4.mask, nhop2); - auto tables4 = u4.updateDone(); - ASSERT_NE(nullptr, tables4); - tables4->publish(); - - // get all 4 routes from table2 - auto t2 = tables2->getRouteTableIf(rid); - ASSERT_NE(nullptr, t2); - auto rib24 = t2->getRibV4(); - ASSERT_NE(nullptr, rib24); - auto t2r1 = rib24->exactMatch(r1); - auto t2r2 = rib24->exactMatch(r2); - auto rib26 = t2->getRibV6(); - ASSERT_NE(nullptr, rib26); - auto t2r3 = rib26->exactMatch(r3); - auto t2r4 = rib26->exactMatch(r4); - ASSERT_NE(nullptr, t2r1); - ASSERT_NE(nullptr, t2r2); - ASSERT_NE(nullptr, t2r3); - ASSERT_NE(nullptr, t2r4); - - // get all 4 routes from table4 - auto t4 = tables4->getRouteTableIf(rid); - ASSERT_NE(nullptr, t4); - auto rib44 = t4->getRibV4(); - ASSERT_NE(nullptr, rib44); - auto t4r1 = rib44->exactMatch(r1); - auto t4r2 = rib44->exactMatch(r2); - auto rib46 = t4->getRibV6(); - ASSERT_NE(nullptr, rib46); - auto t4r3 = rib46->exactMatch(r3); - auto t4r4 = rib46->exactMatch(r4); - ASSERT_NE(nullptr, t4r1); - ASSERT_NE(nullptr, t4r2); - ASSERT_NE(nullptr, t4r3); - ASSERT_NE(nullptr, t4r4); - - EXPECT_EQ(t2r1, t4r1); - EXPECT_NE(t2r2, t4r2); // different routes - EXPECT_EQ(t2r2->getGeneration() + 1, t4r2->getGeneration()); - EXPECT_EQ(t2r3, t4r3); - EXPECT_EQ(t2r4, t4r4); -} - -TEST(Route, resolve) { - SaiPlatform platform; - auto stateV0 = make_shared(); - auto tablesV0 = stateV0->getRouteTables(); - - cfg::SwitchConfig config; - config.vlans.resize(1); - config.vlans[0].id = 1; - - config.interfaces.resize(2); - config.interfaces[0].intfID = 1; - config.interfaces[0].vlanID = 1; - config.interfaces[0].routerID = 0; - config.interfaces[0].__isset.mac = true; - config.interfaces[0].mac = "00:00:00:00:00:11"; - config.interfaces[0].ipAddresses.resize(2); - config.interfaces[0].ipAddresses[0] = "1.1.1.1/24"; - config.interfaces[0].ipAddresses[1] = "1::1/48"; - config.interfaces[1].intfID = 2; - config.interfaces[1].vlanID = 1; - config.interfaces[1].routerID = 0; - config.interfaces[1].__isset.mac = true; - config.interfaces[1].mac = "00:00:00:00:00:22"; - config.interfaces[1].ipAddresses.resize(2); - config.interfaces[1].ipAddresses[0] = "2.2.2.2/24"; - config.interfaces[1].ipAddresses[1] = "2::1/48"; - - auto stateV1 = publishAndApplyConfig(stateV0, &config, &platform); - stateV1->publish(); - ASSERT_NE(nullptr, stateV1); - - auto rid = RouterID(0); - // recursive lookup - { - RouteUpdater u1(stateV1->getRouteTables()); - RouteNextHops nexthops1; - nexthops1.emplace(IPAddress("1.1.1.10")); // resolved by intf 1 - u1.addRoute(rid, IPAddress("1.1.3.0"), 24, nexthops1); - RouteNextHops nexthops2; - nexthops2.emplace(IPAddress("1.1.3.10")); // resolved by '1.1.3/24' - u1.addRoute(rid, IPAddress("8.8.8.0"), 24, nexthops2); - auto tables2 = u1.updateDone(); - ASSERT_NE(nullptr, tables2); - tables2->publish(); - auto t2 = tables2->getRouteTableIf(rid); - ASSERT_NE(nullptr, t2); - auto rib2 = t2->getRibV4(); - RouteV4::Prefix p2 {IPAddressV4("1.1.3.0"), 24}; - auto r21 = rib2->exactMatch(p2); - ASSERT_NE(nullptr, r21); - EXPECT_TRUE(r21->isResolved()); - EXPECT_FALSE(r21->isUnresolvable()); - EXPECT_FALSE(r21->isConnected()); - EXPECT_TRUE(r21->isWithNexthops()); - EXPECT_FALSE(r21->needResolve()); - p2 = RouteV4::Prefix {IPAddressV4("8.8.8.0"), 24}; - auto r22 = rib2->exactMatch(p2); - ASSERT_NE(nullptr, r22); - EXPECT_TRUE(r22->isResolved()); - EXPECT_FALSE(r22->isUnresolvable()); - EXPECT_FALSE(r22->isConnected()); - EXPECT_FALSE(r22->needResolve()); - // r21 and r22 are different routes - EXPECT_NE(r21, r22); - EXPECT_NE(r21->prefix(), r22->prefix()); - // check the forwarding info - RouteForwardNexthops expFwd2; - expFwd2.emplace(InterfaceID(1), IPAddress("1.1.1.10")); - EXPECT_EQ(expFwd2, r21->getForwardInfo().getNexthops()); - EXPECT_EQ(expFwd2, r22->getForwardInfo().getNexthops()); - } - // recursive lookup loop - { - // create a route table w/ the following 3 routes - // 1. 30/8 -> 20.1.1.1 - // 2. 20/8 -> 10.1.1.1 - // 3. 10/8 -> 30.1.1.1 - // The above 3 routes causes lookup loop, which should result in - // all unresolvable. - RouteUpdater u1(stateV1->getRouteTables()); - RouteNextHops nexthops1; - nexthops1.emplace(IPAddress("20.1.1.1")); - u1.addRoute(rid, IPAddress("30.0.0.0"), 8, nexthops1); - RouteNextHops nexthops2; - nexthops2.emplace(IPAddress("10.1.1.1")); - u1.addRoute(rid, IPAddress("20.0.0.0"), 8, nexthops2); - RouteNextHops nexthops3; - nexthops3.emplace(IPAddress("30.1.1.1")); - u1.addRoute(rid, IPAddress("10.0.0.0"), 8, nexthops3); - auto tables2 = u1.updateDone(); - ASSERT_NE(nullptr, tables2); - tables2->publish(); - auto t2 = tables2->getRouteTableIf(rid); - ASSERT_NE(nullptr, t2); - auto rib2 = t2->getRibV4(); - auto verifyPrefix = [&](std::string network) { - RouteV4::Prefix prefix {IPAddressV4(network), 8}; - auto route = rib2->exactMatch(prefix); - ASSERT_NE(nullptr, route); - EXPECT_FALSE(route->isResolved()); - EXPECT_TRUE(route->isUnresolvable()); - EXPECT_FALSE(route->isConnected()); - EXPECT_TRUE(route->isWithNexthops()); - EXPECT_FALSE(route->needResolve()); - EXPECT_FALSE(route->isProcessing()); - }; - verifyPrefix("10.0.0.0"); - verifyPrefix("20.0.0.0"); - verifyPrefix("30.0.0.0"); - } - // recursive lookup across 2 updates - { - RouteUpdater u1(stateV1->getRouteTables()); - RouteNextHops nexthops1; - nexthops1.emplace(IPAddress("50.0.0.1")); - u1.addRoute(rid, IPAddress("40.0.0.0"), 8, nexthops1); - auto tables2 = u1.updateDone(); - ASSERT_NE(nullptr, tables2); - tables2->publish(); - auto t2 = tables2->getRouteTableIf(rid); - ASSERT_NE(nullptr, t2); - auto rib2 = t2->getRibV4(); - RouteV4::Prefix p1 {IPAddressV4("40.0.0.0"), 8}; - auto r21 = rib2->exactMatch(p1); - ASSERT_NE(nullptr, r21); - // 40.0.0.0/8 should be unresolved - EXPECT_FALSE(r21->isResolved()); - EXPECT_TRUE(r21->isUnresolvable()); - EXPECT_FALSE(r21->isConnected()); - EXPECT_FALSE(r21->needResolve()); - // Resolve 50.0.0.1 this should also resolve 40.0.0.0/8 - RouteNextHops nexthops2; - nexthops2.emplace(IPAddress("1.1.1.1")); // intf 1 - RouteUpdater u2(tables2); - u2.addRoute(rid, IPAddress("50.0.0.0"), 8, nexthops2); - auto tables3 = u2.updateDone(); - ASSERT_NE(nullptr, tables3); - tables3->publish(); - auto t3 = tables3->getRouteTableIf(rid); - ASSERT_NE(nullptr, t3); - auto rib3 = t3->getRibV4(); - auto r31 = rib3->exactMatch(p1); - ASSERT_NE(nullptr, r31); - // 40.0.0.0/8 should be resolved - EXPECT_TRUE(r31->isResolved()); - EXPECT_FALSE(r31->isUnresolvable()); - EXPECT_FALSE(r31->isConnected()); - EXPECT_FALSE(r31->needResolve()); - auto r31NextHops = r31->nexthops(); - EXPECT_EQ(1, r31NextHops.size()); - auto r32 = rib3->longestMatch(r31NextHops.begin()->asV4()); - // 50.0.0.1/32 should be resolved - ASSERT_NE(nullptr, r32); - EXPECT_TRUE(r32->isResolved()); - EXPECT_FALSE(r32->isUnresolvable()); - EXPECT_FALSE(r32->isConnected()); - EXPECT_FALSE(r32->needResolve()); - RouteV4::Prefix p2 {IPAddressV4("50.0.0.0"), 8}; - auto r33 = rib3->exactMatch(p2); - ASSERT_NE(nullptr, r33); - // 50.0.0.0/8 should be resolved - EXPECT_TRUE(r33->isResolved()); - EXPECT_FALSE(r33->isUnresolvable()); - EXPECT_FALSE(r33->isConnected()); - EXPECT_FALSE(r33->needResolve()); - - } -} - -// Testing add and delete ECMP routes -TEST(Route, addDel) { - SaiPlatform platform; - auto stateV0 = make_shared(); - auto tablesV0 = stateV0->getRouteTables(); - - cfg::SwitchConfig config; - config.vlans.resize(1); - config.vlans[0].id = 1; - - config.interfaces.resize(2); - config.interfaces[0].intfID = 1; - config.interfaces[0].vlanID = 1; - config.interfaces[0].routerID = 0; - config.interfaces[0].__isset.mac = true; - config.interfaces[0].mac = "00:00:00:00:00:11"; - config.interfaces[0].ipAddresses.resize(2); - config.interfaces[0].ipAddresses[0] = "1.1.1.1/24"; - config.interfaces[0].ipAddresses[1] = "1::1/48"; - config.interfaces[1].intfID = 2; - config.interfaces[1].vlanID = 1; - config.interfaces[1].routerID = 0; - config.interfaces[1].__isset.mac = true; - config.interfaces[1].mac = "00:00:00:00:00:22"; - config.interfaces[1].ipAddresses.resize(2); - config.interfaces[1].ipAddresses[0] = "2.2.2.2/24"; - config.interfaces[1].ipAddresses[1] = "2::1/48"; - - auto stateV1 = publishAndApplyConfig(stateV0, &config, &platform); - ASSERT_NE(nullptr, stateV1); - stateV1->publish(); - - auto rid = RouterID(0); - RouteNextHops nexthops; - nexthops.emplace(IPAddress("1.1.1.10")); // resolved by intf 1 - nexthops.emplace(IPAddress("2::2")); // resolved by intf 2 - nexthops.emplace(IPAddress("1.1.2.10")); // un-resolvable - RouteNextHops nexthops2; - nexthops2.emplace(IPAddress("1.1.3.10")); // un-resolvable - nexthops2.emplace(IPAddress("11:11::1")); // un-resolvable - - RouteUpdater u1(stateV1->getRouteTables()); - u1.addRoute(rid, IPAddress("10.1.1.1"), 24, nexthops); - u1.addRoute(rid, IPAddress("2001::1"), 48, nexthops); - auto tables2 = u1.updateDone(); - ASSERT_NE(nullptr, tables2); - tables2->publish(); - auto t2 = tables2->getRouteTableIf(rid); - ASSERT_NE(nullptr, t2); - // v4 route - auto rib2v4 = t2->getRibV4(); - RouteV4::Prefix p2 {IPAddressV4("10.1.1.0"), 24}; - auto r2 = rib2v4->exactMatch(p2); - ASSERT_NE(nullptr, r2); - EXPECT_TRUE(r2->isResolved()); - EXPECT_FALSE(r2->isDrop()); - EXPECT_FALSE(r2->isToCPU()); - EXPECT_FALSE(r2->isUnresolvable()); - EXPECT_FALSE(r2->isConnected()); - EXPECT_FALSE(r2->needResolve()); - // v6 route - auto rib2v6 = t2->getRibV6(); - RouteV6::Prefix p2v6 {IPAddressV6("2001::0"), 48}; - auto r2v6 = rib2v6->exactMatch(p2v6); - ASSERT_NE(nullptr, r2v6); - EXPECT_TRUE(r2v6->isResolved()); - EXPECT_FALSE(r2v6->isDrop()); - EXPECT_FALSE(r2v6->isToCPU()); - EXPECT_FALSE(r2v6->isUnresolvable()); - EXPECT_FALSE(r2v6->isConnected()); - EXPECT_FALSE(r2v6->needResolve()); - // forwarding info - EXPECT_EQ(RouteForwardAction::NEXTHOPS, r2->getForwardInfo().getAction()); - EXPECT_EQ(RouteForwardAction::NEXTHOPS, r2v6->getForwardInfo().getAction()); - const auto &fwd2 = r2->getForwardInfo().getNexthops(); - const auto &fwd2v6 = r2v6->getForwardInfo().getNexthops(); - EXPECT_EQ(2, fwd2.size()); - EXPECT_EQ(2, fwd2v6.size()); - RouteForwardNexthops expFwd2; - expFwd2.emplace(InterfaceID(1), IPAddress("1.1.1.10")); - expFwd2.emplace(InterfaceID(2), IPAddress("2::2")); - EXPECT_EQ(expFwd2, fwd2); - EXPECT_EQ(expFwd2, fwd2v6); - - // change the nexthops of the V4 route - RouteUpdater u2(tables2); - u2.addRoute(rid, IPAddress("10.1.1.1"), 24, nexthops2); - auto tables3 = u2.updateDone(); - ASSERT_NE(nullptr, tables3); - tables3->publish(); - auto t3 = tables3->getRouteTableIf(rid); - ASSERT_NE(nullptr, t3); - auto rib3v4 = t3->getRibV4(); - RouteV4::Prefix p3 {IPAddressV4("10.1.1.0"), 24}; - auto r3 = rib3v4->exactMatch(p3); - ASSERT_NE(nullptr, r3); - EXPECT_FALSE(r3->isResolved()); - EXPECT_TRUE(r3->isUnresolvable()); - EXPECT_FALSE(r3->isConnected()); - EXPECT_FALSE(r3->needResolve()); - - // re-add the same route does not cause change - RouteUpdater u3(tables3); - u3.addRoute(rid, IPAddress("10.1.1.1"), 24, nexthops2); - auto tables4 = u3.updateDone(); - EXPECT_EQ(nullptr, tables4); - - // now delete the V4 route - RouteUpdater u4(tables3); - u4.delRoute(rid, IPAddress("10.1.1.1"), 24); - auto tables5 = u4.updateDone(); - ASSERT_NE(nullptr, tables5); - tables5->publish(); - auto t5 = tables5->getRouteTableIf(rid); - ASSERT_NE(nullptr, t5); - auto rib5 = t5->getRibV4(); - RouteV4::Prefix p5 {IPAddressV4("10.1.1.0"), 24}; - auto r5 = rib5->exactMatch(p5); - EXPECT_EQ(nullptr, r5); - - // change an old route to punt to CPU, add a new route to DROP - RouteUpdater u5(tables3); - u5.addRoute(rid, IPAddress("10.1.1.0"), 24, RouteForwardAction::TO_CPU); - u5.addRoute(rid, IPAddress("10.1.2.0"), 24, RouteForwardAction::DROP); - auto tables6 = u5.updateDone(); - EXPECT_NE(nullptr, tables6); - auto t6 = tables6->getRouteTableIf(rid); - EXPECT_NE(nullptr, t6); - auto rib6 = t6->getRibV4(); - RouteV4::Prefix p6_1 {IPAddressV4("10.1.1.0"), 24}; - auto r6_1 = rib6->exactMatch(p6_1); - EXPECT_NE(nullptr, r6_1); - EXPECT_TRUE(r6_1->isResolved()); - EXPECT_FALSE(r6_1->isConnected()); - EXPECT_FALSE(r6_1->isWithNexthops()); - EXPECT_TRUE(r6_1->isToCPU()); - EXPECT_FALSE(r6_1->isDrop()); - EXPECT_EQ(RouteForwardAction::TO_CPU, r6_1->getForwardInfo().getAction()); - RouteV4::Prefix p6_2 {IPAddressV4("10.1.2.0"), 24}; - auto r6_2 = rib6->exactMatch(p6_2); - EXPECT_NE(nullptr, r6_2); - EXPECT_TRUE(r6_2->isResolved()); - EXPECT_FALSE(r6_2->isConnected()); - EXPECT_FALSE(r6_2->isWithNexthops()); - EXPECT_FALSE(r6_2->isToCPU()); - EXPECT_TRUE(r6_2->isDrop()); - EXPECT_EQ(RouteForwardAction::DROP, r6_2->getForwardInfo().getAction()); -} - -// Test interface routes -TEST(Route, Interface) { - SaiPlatform platform; - auto stateV0 = make_shared(); - auto tablesV0 = stateV0->getRouteTables(); - - cfg::SwitchConfig config; - config.vlans.resize(1); - config.vlans[0].id = 1; - - config.interfaces.resize(2); - config.interfaces[0].intfID = 1; - config.interfaces[0].vlanID = 1; - config.interfaces[0].routerID = 0; - config.interfaces[0].__isset.mac = true; - config.interfaces[0].mac = "00:00:00:00:00:11"; - config.interfaces[0].ipAddresses.resize(2); - config.interfaces[0].ipAddresses[0] = "1.1.1.1/24"; - config.interfaces[0].ipAddresses[1] = "1::1/48"; - config.interfaces[1].intfID = 2; - config.interfaces[1].vlanID = 1; - config.interfaces[1].routerID = 0; - config.interfaces[1].__isset.mac = true; - config.interfaces[1].mac = "00:00:00:00:00:22"; - config.interfaces[1].ipAddresses.resize(2); - config.interfaces[1].ipAddresses[0] = "2.2.2.2/24"; - config.interfaces[1].ipAddresses[1] = "2::1/48"; - - auto stateV1 = publishAndApplyConfig(stateV0, &config, &platform); - ASSERT_NE(nullptr, stateV1); - stateV1->publish(); - auto tablesV1 = stateV1->getRouteTables(); - EXPECT_NE(tablesV0, tablesV1); - EXPECT_EQ(1, tablesV1->getGeneration()); - EXPECT_EQ(1, tablesV1->size()); - - auto table1 = tablesV1->getRouteTableIf(RouterID(0)); - ASSERT_NE(nullptr, table1); - auto rib4 = table1->getRibV4(); - auto rib6 = table1->getRibV6(); - ASSERT_NE(nullptr, rib4); - ASSERT_NE(nullptr, rib6); - EXPECT_FALSE(rib4->empty()); - EXPECT_FALSE(rib6->empty()); - EXPECT_FALSE(table1->empty()); - EXPECT_EQ(2, rib4->size()); - EXPECT_EQ(3, rib6->size()); - // verify the ipv4 route - { - auto rt = rib4->exactMatch(RoutePrefixV4 {IPAddressV4("1.1.1.0"), 24}); - ASSERT_NE(nullptr, rt); - EXPECT_EQ(0, rt->getGeneration()); - EXPECT_TRUE(rt->isResolved()); - EXPECT_TRUE(rt->isConnected()); - EXPECT_FALSE(rt->isWithNexthops()); - EXPECT_FALSE(rt->isToCPU()); - EXPECT_FALSE(rt->isDrop()); - EXPECT_EQ(RouteForwardAction::NEXTHOPS, rt->getForwardInfo().getAction()); - const auto &fwds = rt->getForwardInfo().getNexthops(); - EXPECT_EQ(1, fwds.size()); - const auto &fwd = *fwds.begin(); - EXPECT_EQ(InterfaceID(1), fwd.intf); - EXPECT_EQ(IPAddress("1.1.1.1"), fwd.nexthop); - } - // verify the ipv6 route - { - auto rt = rib6->exactMatch(RoutePrefixV6 {IPAddressV6("2::0"), 48}); - ASSERT_NE(nullptr, rt); - EXPECT_EQ(0, rt->getGeneration()); - EXPECT_TRUE(rt->isResolved()); - EXPECT_TRUE(rt->isConnected()); - EXPECT_FALSE(rt->isWithNexthops()); - EXPECT_FALSE(rt->isToCPU()); - EXPECT_FALSE(rt->isDrop()); - EXPECT_EQ(RouteForwardAction::NEXTHOPS, rt->getForwardInfo().getAction()); - const auto &fwds = rt->getForwardInfo().getNexthops(); - EXPECT_EQ(1, fwds.size()); - const auto &fwd = *fwds.begin(); - EXPECT_EQ(InterfaceID(2), fwd.intf); - EXPECT_EQ(IPAddress("2::1"), fwd.nexthop); - } - - { - // verify the link local route - auto rt = rib6->exactMatch(RoutePrefixV6 {IPAddressV6("fe80::"), 64}); - ASSERT_NE(nullptr, rt); - EXPECT_EQ(0, rt->getGeneration()); - EXPECT_TRUE(rt->isResolved()); - EXPECT_FALSE(rt->isConnected()); - EXPECT_FALSE(rt->isWithNexthops()); - EXPECT_TRUE(rt->isToCPU()); - EXPECT_EQ(RouteForwardAction::TO_CPU, rt->getForwardInfo().getAction()); - const auto &fwds = rt->getForwardInfo().getNexthops(); - EXPECT_EQ(0, fwds.size()); - } - - // swap the interface addresses which causes route change - config.interfaces[1].ipAddresses[0] = "1.1.1.1/24"; - config.interfaces[1].ipAddresses[1] = "1::1/48"; - config.interfaces[0].ipAddresses[0] = "2.2.2.2/24"; - config.interfaces[0].ipAddresses[1] = "2::1/48"; - - auto stateV2 = publishAndApplyConfig(stateV1, &config, &platform); - ASSERT_NE(nullptr, stateV2); - stateV2->publish(); - auto tablesV2 = stateV2->getRouteTables(); - EXPECT_NE(tablesV1, tablesV2); - EXPECT_EQ(2, tablesV2->getGeneration()); - EXPECT_EQ(1, tablesV2->size()); - - auto table2 = tablesV2->getRouteTableIf(RouterID(0)); - ASSERT_NE(nullptr, table1); - auto rib4V2 = table2->getRibV4(); - auto rib6V2 = table2->getRibV6(); - ASSERT_NE(nullptr, rib4); - ASSERT_NE(nullptr, rib6); - EXPECT_NE(rib4, rib4V2); - EXPECT_NE(rib6, rib6V2); - EXPECT_FALSE(rib4V2->empty()); - EXPECT_FALSE(rib6V2->empty()); - EXPECT_FALSE(table1->empty()); - EXPECT_EQ(2, rib4V2->size()); - EXPECT_EQ(3, rib6V2->size()); - // verify the ipv4 route - { - auto rt = rib4V2->exactMatch(RoutePrefixV4 {IPAddressV4("1.1.1.0"), 24}); - ASSERT_NE(nullptr, rt); - EXPECT_EQ(1, rt->getGeneration()); - const auto &fwds = rt->getForwardInfo().getNexthops(); - EXPECT_EQ(1, fwds.size()); - const auto &fwd = *fwds.begin(); - EXPECT_EQ(InterfaceID(2), fwd.intf); - EXPECT_EQ(IPAddress("1.1.1.1"), fwd.nexthop); - } - // verify the ipv6 route - { - auto rt = rib6V2->exactMatch(RoutePrefixV6 {IPAddressV6("2::0"), 48}); - ASSERT_NE(nullptr, rt); - EXPECT_EQ(1, rt->getGeneration()); - const auto &fwds = rt->getForwardInfo().getNexthops(); - EXPECT_EQ(1, fwds.size()); - const auto &fwd = *fwds.begin(); - EXPECT_EQ(InterfaceID(1), fwd.intf); - EXPECT_EQ(IPAddress("2::1"), fwd.nexthop); - } -} - -namespace TEMP { -struct Route { - uint32_t vrf; - IPAddress prefix; - uint8_t len; - Route(uint32_t vrf, IPAddress prefix, uint8_t len) - : vrf(vrf), prefix(prefix), len(len) {} - bool operator<(const Route &rt) const { - if (vrf < rt.vrf) { - return true; - } else if (vrf > rt.vrf) { - return false; - } - - if (len < rt.len) { - return true; - } else if (len > rt.len) { - return false; - } - - return prefix < rt.prefix; - } - bool operator==(const Route &rt) const { - return vrf == rt.vrf && len == rt.len && prefix == rt.prefix; - } -}; -} - -void checkChangedRoute(const shared_ptr &oldTables, - const shared_ptr &newTables, - const std::set changedIDs, - const std::set addedIDs, - const std::set removedIDs) { - auto oldState = make_shared(); - oldState->resetRouteTables(oldTables); - auto newState = make_shared(); - newState->resetRouteTables(newTables); - - std::set foundChanged; - std::set foundAdded; - std::set foundRemoved; - StateDelta delta(oldState, newState); - - for (auto const& rtDelta : delta.getRouteTablesDelta()) { - RouterID id; - - if (!rtDelta.getOld()) { - id = rtDelta.getNew()->getID(); - } else { - id = rtDelta.getOld()->getID(); - } - - DeltaFunctions::forEachChanged( - rtDelta.getRoutesV4Delta(), - [&] (const shared_ptr &oldRt, - const shared_ptr &newRt) { - EXPECT_EQ(oldRt->prefix(), newRt->prefix()); - EXPECT_NE(oldRt, newRt); - const auto prefix = newRt->prefix(); - auto ret = foundChanged.insert( - TEMP::Route(id, IPAddress(prefix.network), prefix.mask)); - EXPECT_TRUE(ret.second); - }, - [&] (const shared_ptr &rt) { - const auto prefix = rt->prefix(); - auto ret = foundAdded.insert( - TEMP::Route(id, IPAddress(prefix.network), prefix.mask)); - EXPECT_TRUE(ret.second); - }, - [&] (const shared_ptr &rt) { - const auto prefix = rt->prefix(); - auto ret = foundRemoved.insert( - TEMP::Route(id, IPAddress(prefix.network), prefix.mask)); - EXPECT_TRUE(ret.second); - }); - DeltaFunctions::forEachChanged( - rtDelta.getRoutesV6Delta(), - [&] (const shared_ptr &oldRt, - const shared_ptr &newRt) { - EXPECT_EQ(oldRt->prefix(), newRt->prefix()); - EXPECT_NE(oldRt, newRt); - const auto prefix = newRt->prefix(); - auto ret = foundChanged.insert( - TEMP::Route(id, IPAddress(prefix.network), prefix.mask)); - EXPECT_TRUE(ret.second); - }, - [&] (const shared_ptr &rt) { - const auto prefix = rt->prefix(); - auto ret = foundAdded.insert( - TEMP::Route(id, IPAddress(prefix.network), prefix.mask)); - EXPECT_TRUE(ret.second); - }, - [&] (const shared_ptr &rt) { - const auto prefix = rt->prefix(); - auto ret = foundRemoved.insert( - TEMP::Route(id, IPAddress(prefix.network), prefix.mask)); - EXPECT_TRUE(ret.second); - }); - } - - EXPECT_EQ(changedIDs, foundChanged); - EXPECT_EQ(addedIDs, foundAdded); - EXPECT_EQ(removedIDs, foundRemoved); -} - -void checkChangedRouteTable(const shared_ptr &oldTables, - const shared_ptr &newTables, - const std::set changedIDs, - const std::set addedIDs, - const std::set removedIDs) { - auto oldState = make_shared(); - oldState->resetRouteTables(oldTables); - auto newState = make_shared(); - newState->resetRouteTables(newTables); - - std::set foundChanged; - std::set foundAdded; - std::set foundRemoved; - StateDelta delta(oldState, newState); - DeltaFunctions::forEachChanged( - delta.getRouteTablesDelta(), - [&] (const shared_ptr &oldTable, - const shared_ptr &newTable) { - EXPECT_EQ(oldTable->getID(), newTable->getID()); - EXPECT_NE(oldTable, newTable); - auto ret = foundChanged.insert(oldTable->getID()); - EXPECT_TRUE(ret.second); - }, - [&] (const shared_ptr &table) { - auto ret = foundAdded.insert(table->getID()); - EXPECT_TRUE(ret.second); - }, - [&] (const shared_ptr &table) { - auto ret = foundRemoved.insert(table->getID()); - EXPECT_TRUE(ret.second); - }); - - EXPECT_EQ(changedIDs, foundChanged); - EXPECT_EQ(addedIDs, foundAdded); - EXPECT_EQ(removedIDs, foundRemoved); -} - -TEST(RouteTableMap, applyConfig) { - SaiPlatform platform; - auto stateV0 = make_shared(); - auto tablesV0 = stateV0->getRouteTables(); - - cfg::SwitchConfig config; - config.vlans.resize(3); - config.vlans[0].id = 1; - config.vlans[1].id = 2; - config.vlans[2].id = 3; - config.interfaces.resize(2); - config.interfaces[0].intfID = 1; - config.interfaces[0].vlanID = 1; - config.interfaces[0].routerID = 0; - config.interfaces[0].__isset.mac = true; - config.interfaces[0].mac = "00:00:00:00:00:11"; - config.interfaces[1].intfID = 2; - config.interfaces[1].vlanID = 2; - config.interfaces[1].routerID = 1; - config.interfaces[1].__isset.mac = true; - config.interfaces[1].mac = "00:00:00:00:00:22"; - - auto stateV1 = publishAndApplyConfig(stateV0, &config, &platform); - ASSERT_NE(nullptr, stateV1); - stateV1->publish(); - auto tablesV1 = stateV1->getRouteTables(); - EXPECT_EQ(tablesV0, tablesV1); - EXPECT_EQ(0, tablesV1->getGeneration()); - EXPECT_EQ(0, tablesV1->size()); - - config.interfaces[0].ipAddresses.resize(4); - config.interfaces[0].ipAddresses[0] = "1.1.1.1/24"; - config.interfaces[0].ipAddresses[1] = "1.1.1.2/24"; - config.interfaces[0].ipAddresses[2] = "1.1.1.10/24"; - config.interfaces[0].ipAddresses[3] = "::1/48"; - config.interfaces[1].ipAddresses.resize(2); - config.interfaces[1].ipAddresses[0] = "1.1.1.1/24"; - config.interfaces[1].ipAddresses[1] = "::1/48"; - - auto stateV2 = publishAndApplyConfig(stateV1, &config, &platform); - ASSERT_NE(nullptr, stateV2); - stateV2->publish(); - auto tablesV2 = stateV2->getRouteTables(); - EXPECT_NE(tablesV1, tablesV2); - EXPECT_EQ(1, tablesV2->getGeneration()); - EXPECT_EQ(2, tablesV2->size()); - EXPECT_NE(nullptr, tablesV2->getRouteTable(RouterID(0))); - EXPECT_NE(nullptr, tablesV2->getRouteTable(RouterID(1))); - - checkChangedRouteTable(tablesV1, tablesV2, {}, {0,1}, {}); - checkChangedRoute(tablesV1, tablesV2, - {}, { - TEMP::Route{0, IPAddress("1.1.1.0"), 24}, - TEMP::Route{0, IPAddress("::0"), 48}, - TEMP::Route{1, IPAddress("1.1.1.0"), 24}, - TEMP::Route{1, IPAddress("::0"), 48}, - }, - {}); - - // change an interface address - config.interfaces[0].ipAddresses[3] = "11::11/48"; - - auto stateV3 = publishAndApplyConfig(stateV2, &config, &platform); - ASSERT_NE(nullptr, stateV3); - stateV3->publish(); - auto tablesV3 = stateV3->getRouteTables(); - EXPECT_NE(tablesV2, tablesV3); - EXPECT_EQ(2, tablesV3->getGeneration()); - EXPECT_EQ(2, tablesV3->size()); - EXPECT_NE(nullptr, tablesV2->getRouteTable(RouterID(0))); - EXPECT_NE(nullptr, tablesV2->getRouteTable(RouterID(1))); - - checkChangedRouteTable(tablesV2, tablesV3, {0}, {}, {}); - checkChangedRoute(tablesV2, tablesV3, - {}, - {TEMP::Route{0, IPAddress("11::0"), 48}}, - {TEMP::Route{0, IPAddress("::0"), 48}}); - - // move one interface to cause same route prefix conflict - config.interfaces[1].routerID = 0; - EXPECT_THROW(publishAndApplyConfig(stateV3, &config, &platform), FbossError); - - // add a new interface in a new VRF - config.interfaces.resize(3); - config.interfaces[2].intfID = 3; - config.interfaces[2].vlanID = 3; - config.interfaces[2].routerID = 2; - config.interfaces[2].__isset.mac = true; - config.interfaces[2].mac = "00:00:00:00:00:33"; - config.interfaces[2].ipAddresses.resize(2); - config.interfaces[2].ipAddresses[0] = "1.1.1.1/24"; - config.interfaces[2].ipAddresses[1] = "::1/48"; - // and move one interface to another vrf and fix the address conflict - config.interfaces[1].routerID = 0; - config.interfaces[1].ipAddresses.resize(2); - config.interfaces[1].ipAddresses[0] = "2.2.2.1/24"; - config.interfaces[1].ipAddresses[1] = "1::2/48"; - - auto stateV4 = publishAndApplyConfig(stateV3, &config, &platform); - ASSERT_NE(nullptr, stateV4); - stateV4->publish(); - auto tablesV4 = stateV4->getRouteTables(); - EXPECT_NE(tablesV3, tablesV4); - EXPECT_EQ(3, tablesV4->getGeneration()); - EXPECT_EQ(2, tablesV4->size()); - EXPECT_NE(nullptr, tablesV4->getRouteTable(RouterID(0))); - EXPECT_EQ(nullptr, tablesV4->getRouteTableIf(RouterID(1))); - EXPECT_NE(nullptr, tablesV4->getRouteTable(RouterID(2))); - - checkChangedRouteTable(tablesV3, tablesV4, {0}, {2}, {1}); - checkChangedRoute(tablesV3, tablesV4, - {}, { - TEMP::Route{0, IPAddress("2.2.2.0"), 24}, - TEMP::Route{0, IPAddress("1::0"), 48}, - TEMP::Route{2, IPAddress("1.1.1.0"), 24}, - TEMP::Route{2, IPAddress("::0"), 48}, - TEMP::Route{2, IPAddress("fe80::"), 64}, - }, { - TEMP::Route{1, IPAddress("1.1.1.0"), 24}, - TEMP::Route{1, IPAddress("::0"), 48} - }); - - // re-apply the same configure generates no change - EXPECT_EQ(nullptr, publishAndApplyConfig(stateV4, &config, &platform)); -} - -TEST(Route, changedRoutesPostUpdate) { - SaiPlatform platform; - auto stateV0 = make_shared(); - auto tablesV0 = stateV0->getRouteTables(); - - cfg::SwitchConfig config; - config.vlans.resize(1); - config.vlans[0].id = 1; - - config.interfaces.resize(1); - config.interfaces[0].intfID = 1; - config.interfaces[0].vlanID = 1; - config.interfaces[0].routerID = 0; - config.interfaces[0].__isset.mac = true; - config.interfaces[0].mac = "00:00:00:00:00:11"; - config.interfaces[0].ipAddresses.resize(2); - config.interfaces[0].ipAddresses[0] = "1.1.1.1/24"; - config.interfaces[0].ipAddresses[1] = "1::1/48"; - - auto stateV1 = publishAndApplyConfig(stateV0, &config, &platform); - ASSERT_NE(nullptr, stateV1); - stateV1->publish(); - auto rid = RouterID(0); - RouteNextHops nexthops; - nexthops.emplace(IPAddress("1.1.1.10")); // resolved by intf 1 - nexthops.emplace(IPAddress("2::2")); // resolved by intf 2 - - auto numChangedRoutes = [=] (const RTMapDelta& delta) { - auto cnt = 0; - - for (auto itr = delta.begin(); itr != delta.end(); ++itr) { - const auto &v4Delta = itr->getRoutesV4Delta(); - const auto &v6Delta = itr->getRoutesV6Delta(); - - for (auto v4Itr = v4Delta.begin(); v4Itr != v4Delta.end(); - ++v4Itr, ++cnt); - - for (auto v6Itr = v6Delta.begin(); v6Itr != v6Delta.end(); - ++v6Itr, ++cnt); - } - - return cnt; - }; - // Add a couple of routes - auto tables1 = stateV1->getRouteTables(); - stateV1->publish(); - RouteUpdater u1(tables1); - u1.addRoute(rid, IPAddress("10.1.1.0"), 24, nexthops); - u1.addRoute(rid, IPAddress("2001::0"), 48, nexthops); - auto tables2 = u1.updateDone(); - ASSERT_NE(nullptr, tables2); - auto t2 = tables2->getRouteTableIf(rid); - ASSERT_NE(nullptr, t2); - // v4 route - auto rib2v4 = t2->getRibV4(); - RouteV4::Prefix p2 {IPAddressV4("10.1.1.0"), 24}; - auto r2 = rib2v4->exactMatch(p2); - ASSERT_NE(nullptr, r2); - EXPECT_TRUE(r2->isResolved()); - EXPECT_FALSE(r2->isUnresolvable()); - EXPECT_FALSE(r2->isConnected()); - EXPECT_FALSE(r2->needResolve()); - // v6 route - auto rib2v6 = t2->getRibV6(); - RouteV6::Prefix p2v6 {IPAddressV6("2001::0"), 48}; - auto r2v6 = rib2v6->exactMatch(p2v6); - ASSERT_NE(nullptr, r2v6); - EXPECT_TRUE(r2v6->isResolved()); - EXPECT_FALSE(r2v6->isUnresolvable()); - EXPECT_FALSE(r2v6->isConnected()); - EXPECT_FALSE(r2v6->needResolve()); - auto stateV2 = stateV1->clone(); - stateV2->resetRouteTables(tables2); - StateDelta delta12(stateV1, stateV2); - EXPECT_EQ(2, numChangedRoutes(delta12.getRouteTablesDelta())); - checkChangedRouteTable(tables1, tables2, {0}, {}, {}); - checkChangedRoute(tables1, tables2, - {}, { - TEMP::Route{0, IPAddress("10.1.1.0"), 24}, - TEMP::Route{0, IPAddress("2001::0"), 48}, - }, - {}); - stateV2->publish(); - // Add 2 more routes - RouteUpdater u2(stateV2->getRouteTables()); - u2.addRoute(rid, IPAddress("10.10.1.0"), 24, nexthops); - u2.addRoute(rid, IPAddress("2001:10::0"), 48, nexthops); - auto tables3 = u2.updateDone(); - ASSERT_NE(nullptr, tables3); - auto t3 = tables3->getRouteTableIf(rid); - ASSERT_NE(nullptr, t3); - // v4 route - auto rib3v4 = t3->getRibV4(); - RouteV4::Prefix p3 {IPAddressV4("10.10.1.0"), 24}; - auto r3 = rib3v4->exactMatch(p3); - ASSERT_NE(nullptr, r3); - EXPECT_TRUE(r3->isResolved()); - EXPECT_FALSE(r3->isUnresolvable()); - EXPECT_FALSE(r3->isConnected()); - EXPECT_FALSE(r3->needResolve()); - // v6 route - auto rib3v6 = t3->getRibV6(); - RouteV6::Prefix p3v6 {IPAddressV6("2001:10::0"), 48}; - auto r3v6 = rib3v6->exactMatch(p3v6); - ASSERT_NE(nullptr, r3v6); - EXPECT_TRUE(r3v6->isResolved()); - EXPECT_FALSE(r3v6->isUnresolvable()); - EXPECT_FALSE(r3v6->isConnected()); - EXPECT_FALSE(r3v6->needResolve()); - auto stateV3 = stateV2->clone(); - stateV3->resetRouteTables(tables3); - StateDelta delta23(stateV2, stateV3); - EXPECT_EQ(2, numChangedRoutes(delta23.getRouteTablesDelta())); - checkChangedRouteTable(tables2, tables3, {0}, {}, {}); - checkChangedRoute(tables2, tables3, - {}, { - TEMP::Route{0, IPAddress("10.10.1.0"), 24}, - TEMP::Route{0, IPAddress("2001:10::0"), 48}, - }, - {}); - stateV3->publish(); -} diff --git a/fboss/agent/hw/sai/SaiTestUtils.cpp b/fboss/agent/hw/sai/SaiTestUtils.cpp deleted file mode 100644 index c7735895f0719..0000000000000 --- a/fboss/agent/hw/sai/SaiTestUtils.cpp +++ /dev/null @@ -1,266 +0,0 @@ -/* - * 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/hw/sai/SaiTestUtils.h" -#include "fboss/agent/hw/sai/SaiSwitch.h" - -#include "fboss/agent/ApplyThriftConfig.h" -#include "fboss/agent/SwSwitch.h" -#include "fboss/agent/HwSwitch.h" -#include "fboss/agent/state/SwitchState.h" -#include "fboss/agent/state/Vlan.h" -#include "fboss/agent/state/VlanMap.h" -#include "fboss/agent/state/Interface.h" -#include "fboss/agent/state/Port.h" -#include "fboss/agent/state/RouteUpdater.h" -#include "fboss/agent/platforms/sai/SaiPlatform.h" - -#include "fboss/agent/gen-cpp/switch_config_types.h" - -#include -#include - -using folly::MacAddress; -using folly::IPAddress; -using folly::ByteRange; -using folly::IOBuf; -using folly::io::Cursor; -using folly::make_unique; -using folly::StringPiece; -using std::make_shared; -using std::shared_ptr; -using std::string; -using std::unique_ptr; -using ::testing::_; -using ::testing::Return; - -namespace facebook { namespace fboss { - -shared_ptr publishAndApplyConfig( - shared_ptr &state, - const cfg::SwitchConfig *config, - const Platform *platform) { - state->publish(); - return applyThriftConfig(state, config, platform); -} - -shared_ptr publishAndApplyConfigFile( - shared_ptr &state, - StringPiece path, - const Platform *platform) { - state->publish(); - return applyThriftConfigFile(state, path, platform); -} - -unique_ptr createSaiSw(const shared_ptr &state) { - auto sw = make_unique(make_unique()); - auto stateAndBootType = std::make_pair(state, BootType::COLD_BOOT); -// EXPECT_HW_CALL(sw, init(_)).WillOnce(Return(stateAndBootType)); - sw->init(); - return sw; -} - -unique_ptr createSaiSw(const shared_ptr &state, - const MacAddress &mac) { - auto platform = make_unique(); -// EXPECT_CALL(*platform.get(), getLocalMac()).WillRepeatedly(Return(mac)); - auto sw = make_unique(std::move(platform)); - auto stateAndBootType = std::make_pair(state, BootType::COLD_BOOT); -// EXPECT_HW_CALL(sw, init(_)).WillOnce(Return(stateAndBootType)); - sw->init(); - return sw; -} - -unique_ptr createSaiSw(cfg::SwitchConfig *config, - MacAddress mac, - uint32_t maxPort) { - // Create the initial state, which only has ports - auto initialState = make_shared(); - - if (maxPort == 0) { - for (const auto& port : config->ports) { - maxPort = std::max(static_cast(maxPort), port.logicalID); - } - } - - for (uint32_t idx = 1; idx <= maxPort; ++idx) { - initialState->registerPort(PortID(idx), folly::to("port", idx)); - } - - // Create the SwSwitch - auto platform = make_unique(); -// EXPECT_CALL(*platform.get(), getLocalMac()).WillRepeatedly(Return(mac)); - auto sw = make_unique(std::move(platform)); - auto stateAndBootType = std::make_pair(initialState, BootType::COLD_BOOT); -// EXPECT_HW_CALL(sw, init(_)).WillOnce(Return(stateAndBootType)); - sw->init(); - - // Apply the thrift config - auto updateFn = [&](const shared_ptr &state) { - return applyThriftConfig(state, config, sw->getPlatform()); - }; - sw->updateStateBlocking("test_setup", updateFn); - return sw; -} - -unique_ptr createSaiSw(cfg::SwitchConfig *config, - uint32_t maxPort) { - return createSaiSw(config, MacAddress("02:00:00:00:00:01"), maxPort); -} - -SaiSwitch *getSaiHw(SwSwitch *sw) { - return boost::polymorphic_downcast(sw->getHw()); -} - -SaiPlatform *getSaiPlatform(SwSwitch *sw) { - return boost::polymorphic_downcast(sw->getPlatform()); -} - -void waitForStateUpdates(SwSwitch *sw) { - // All StateUpdates scheduled from this thread will be applied in order, - // so we can simply perform a blocking no-op update. When it is done - // we can be sure that all previously scheduled updates have also been - // applied. - auto noopUpdate = [](const shared_ptr &state) { - return shared_ptr(); - }; - sw->updateStateBlocking("waitForStateUpdates", noopUpdate); -} - - -shared_ptr testStateA() { - // Setup a default state object - auto state = make_shared(); - - // Add VLAN 1, and ports 1-9 which belong to it. - auto vlan1 = make_shared(VlanID(1), "Vlan1"); - state->addVlan(vlan1); - - for (int idx = 1; idx < 10; ++idx) { - state->registerPort(PortID(idx), folly::to("port", idx)); - vlan1->addPort(PortID(idx), false); - } - - // Add VLAN 55, and ports 10-19 which belong to it. - auto vlan55 = make_shared(VlanID(55), "Vlan55"); - state->addVlan(vlan55); - - for (int idx = 10; idx < 20; ++idx) { - state->registerPort(PortID(idx), folly::to("port", idx)); - vlan55->addPort(PortID(idx), false); - } - - // Add Interface 1 to VLAN 1 -/* auto intf1 = make_shared - (InterfaceID(1), RouterID(0), VlanID(1), - "interface1", MacAddress("00:02:00:00:00:01"), InterfaceID(5), PortID(1)); - Interface::Addresses addrs1; - addrs1.emplace(IPAddress("10.0.0.1"), 24); - addrs1.emplace(IPAddress("192.168.0.1"), 24); - addrs1.emplace(IPAddress("2401:db00:2110:3001::0001"), 64); - intf1->setAddresses(addrs1); - state->addIntf(intf1); - // Add Interface 55 to VLAN 55 - auto intf55 = make_shared - (RouterID(0), VlanID(55), - "interface55", MacAddress("00:02:00:00:00:55"), InterfaceID(5), PortID(55)); - Interface::Addresses addrs55; - addrs55.emplace(IPAddress("10.0.55.1"), 24); - addrs55.emplace(IPAddress("192.168.55.1"), 24); - addrs55.emplace(IPAddress("2401:db00:2110:3055::0001"), 64); - intf55->setAddresses(addrs55); - state->addIntf(intf55); -*/ - RouteUpdater updater(state->getRouteTables()); - updater.addInterfaceAndLinkLocalRoutes(state->getInterfaces()); - - RouteNextHops nexthops; - nexthops.emplace(IPAddress("10.0.0.22")); // resolved by intf 1 - nexthops.emplace(IPAddress("10.0.0.23")); // resolved by intf 1 - nexthops.emplace(IPAddress("1.1.2.10")); // un-resolvable - - updater.addRoute(RouterID(0), IPAddress("10.1.1.0"), 24, nexthops); - - auto newRt = updater.updateDone(); - state->resetRouteTables(newRt); - - return state; -} - -std::string fbossHexDump(const IOBuf *buf) { - Cursor cursor(buf); - size_t length = cursor.totalLength(); - - if (length == 0) { - return ""; - } - - string result; - result.reserve(length * 3); - folly::format(&result, "{:02x}", cursor.read()); - - for (size_t n = 1; n < length; ++n) { - folly::format(&result, " {:02x}", cursor.read()); - } - - return result; -} - -std::string fbossHexDump(const IOBuf &buf) { - return fbossHexDump(&buf); -} - -std::string fbossHexDump(folly::ByteRange buf) { - IOBuf iobuf(IOBuf::WRAP_BUFFER, buf); - return fbossHexDump(&iobuf); -} - -std::string fbossHexDump(folly::StringPiece buf) { - IOBuf iobuf(IOBuf::WRAP_BUFFER, ByteRange(buf)); - return fbossHexDump(&iobuf); -} - -std::string fbossHexDump(const string &buf) { - IOBuf iobuf(IOBuf::WRAP_BUFFER, ByteRange(StringPiece(buf))); - return fbossHexDump(&iobuf); -} - -TxPacketMatcher::TxPacketMatcher(StringPiece name, TxMatchFn fn) - : name_(name.str()), - fn_(std::move(fn)) { -} - -::testing::Matcher> TxPacketMatcher::createMatcher( - folly::StringPiece name, -TxMatchFn &&fn) { - return ::testing::MakeMatcher(new TxPacketMatcher(name, fn)); -} - -bool TxPacketMatcher::MatchAndExplain( - shared_ptr pkt, - ::testing::MatchResultListener *l) const { - try { - fn_(pkt.get()); - return true; - } catch (const std::exception &ex) { - *l << ex.what(); - return false; - } -} - -void TxPacketMatcher::DescribeTo(std::ostream *os) const { - *os << name_; -} - -void TxPacketMatcher::DescribeNegationTo(std::ostream *os) const { - *os << "not " << name_; -} - -}} // facebook::fboss diff --git a/fboss/agent/hw/sai/SaiTestUtils.h b/fboss/agent/hw/sai/SaiTestUtils.h deleted file mode 100644 index 79efeeea4040c..0000000000000 --- a/fboss/agent/hw/sai/SaiTestUtils.h +++ /dev/null @@ -1,197 +0,0 @@ -/* - * 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 -#include "fboss/agent/FbossError.h" - -namespace facebook { namespace fboss { - -class SaiSwitch; -class SaiPlatform; -class SwitchState; -class SwSwitch; -class TxPacket; -class Platform; - -namespace cfg { -class SwitchConfig; -} - -/* - * In the non unit test code state passed to apply*Config is the state - * returned from SwSwitch init, which is always published. However this - * is not the case for unit test code where we are constructing config - * by hand and applying it to a (often empty) initial state. Now in a rare - * case if such a config had only route updates in it, since the previous - * state was not published, we would apply these route updates inline - * and now when we compare the old and new route tables to determine - * if anything changed we would come up empty. The end result in such - * a case would be to return a null new state falsely signifying no change. - * To avoid this, provide convenience wrappers for unit test code to call. - */ -std::shared_ptr publishAndApplyConfig( - std::shared_ptr &state, - const cfg::SwitchConfig *config, - const Platform *platform); - -std::shared_ptr publishAndApplyConfigFile( - std::shared_ptr &state, - folly::StringPiece path, - const Platform *platform); - -/* - * Create a SwSwitch for testing purposes, with the specified initial state. - * - * This will use a SaiHwSwitch for the HwSwitch implementation, with the - * specified initial state. The returned SwSwitch will have already been - * initialized. - */ -std::unique_ptr createSaiSw(const std::shared_ptr &); -std::unique_ptr createSaiSw(const std::shared_ptr &, - const folly::MacAddress &); - -std::unique_ptr createSaiSw(cfg::SwitchConfig *cfg, - folly::MacAddress mac, - uint32_t maxPorts = 0); -std::unique_ptr createSaiSw(cfg::SwitchConfig *cfg, - uint32_t maxPorts = 0); - -/* - * Get the SaiSwitch from a SwSwitch. - */ -SaiSwitch *getSaiHw(SwSwitch *sw); - -/* - * Get the SaiPlatform from a SwSwitch. - */ -SaiPlatform *getSaiPlatform(SwSwitch *sw); - -/* - * Wait until all pending StateUpdates have been applied. - */ -void waitForStateUpdates(SwSwitch *sw); - -/** - * check the field value - */ -template -void checkField(const ExpectedType &expected, const ActualType &actual, - folly::StringPiece fieldName) { - if (expected != actual) { - throw FbossError("expected ", fieldName, " to be ", expected, - ", but found ", actual); - return; - } -} - -/* - * Create a SwitchState for testing. - * - * Profile A: - * - 20 ports, 2 VLANS, 2 interfaces - * - Ports 1-10 are in VLAN 1 - * - Ports 11-20 are in VLAN 55 - * - Interface 1: - * - VLAN 1 - * - MAC 00:02:00:00:00:01 - * - IPs: - * 10.0.0.1/24 - * 192.168.0.1/24 - * - Interface 55: - * - VLAN 55 - * - MAC 00:02:00:00:00:55 - * - IPs: - * 10.0.55.1/24 - * 192.168.55.1/24 - */ -std::shared_ptr testStateA(); - - -/* - * Convenience macro that wraps EXPECT_CALL() on the underlying Saiwitch - */ -#define EXPECT_HW_CALL(sw, method) \ - EXPECT_CALL(*getSaiHw((sw).get()), method) - -/* - * Convenience macro that wraps EXPECT_CALL() on the underlying SaiPlatform - */ -#define EXPECT_PLATFORM_CALL(sw, method) \ - EXPECT_CALL(*getSaiPlatform((sw).get()), method) - -/* - * Helper functions for comparing buffer-like objects - */ -#define EXPECT_BUF_EQ(a, b) EXPECT_EQ(fbossHexDump(a), fbossHexDump(b)) -#define ASSERT_BUF_EQ(a, b) ASSERT_EQ(fbossHexDump(a), fbossHexDump(b)) - -std::string fbossHexDump(const folly::IOBuf *buf); -std::string fbossHexDump(const folly::IOBuf &buf); -std::string fbossHexDump(folly::ByteRange buf); -std::string fbossHexDump(folly::StringPiece buf); -std::string fbossHexDump(const std::string &buf); - -/* - * Convenience macro for expecting a packet to be transmitted by the switch - * - * The name should be a brief description of the match being performed. - * - * The matchFn should be a function that accepts a single "const TxPacket*" - * argument. If the packet matches it should return void, and if it doesn't - * match it should throw an exception describing why the packet did not match. - * - * Note that you should not use gtest EXPECT_* macros when checking the - * packet. Just because one packet is not a match does not mean that another - * packet will not arrive and match the filter. For instance, consider the - * following code: - * - * EXPECT_PKT(sw, "ARP reply", arpReplyChecker).Times(1); - * EXPECT_PKT(sw, "ICMP packet", icmpChecker).Times(1); - * - * The code expects to see one ARP reply and one ICMP packet, but it may have - * to call both filters on a packet to see which one it is. - */ -#define EXPECT_PKT(sw, name, matchFn) \ - EXPECT_HW_CALL(sw, sendPacketSwitched_( \ - TxPacketMatcher::createMatcher(name, matchFn))); - -typedef std::function TxMatchFn; - -/* - * A gmock MatcherInterface for matching TxPacket objects. - */ -class TxPacketMatcher : - public ::testing::MatcherInterface> { -public: - TxPacketMatcher(folly::StringPiece name, TxMatchFn fn); - - static ::testing::Matcher> createMatcher( - folly::StringPiece name, - TxMatchFn &&fn); - - bool MatchAndExplain(std::shared_ptr pkt, - ::testing::MatchResultListener *l) const override; - - void DescribeTo(std::ostream *os) const override; - void DescribeNegationTo(std::ostream *os) const override; - -private: - std::string name_; - TxMatchFn fn_; -}; - -}} // facebook::fboss diff --git a/fboss/agent/hw/sai/SaiVlanTests.cpp b/fboss/agent/hw/sai/SaiVlanTests.cpp deleted file mode 100644 index 028f7ca78354e..0000000000000 --- a/fboss/agent/hw/sai/SaiVlanTests.cpp +++ /dev/null @@ -1,398 +0,0 @@ -/* - * 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/ApplyThriftConfig.h" -#include "fboss/agent/FbossError.h" -#include "fboss/agent/state/ArpResponseTable.h" -#include "fboss/agent/state/DeltaFunctions.h" -#include "fboss/agent/state/NdpResponseTable.h" -#include "fboss/agent/state/NodeMapDelta.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/hw/sai/SaiTestUtils.h" -#include "fboss/agent/platforms/sai/SaiPlatform.h" -#include "fboss/agent/gen-cpp/switch_config_types.h" - -#include -#include - -using namespace facebook::fboss; -using folly::IPAddressV4; -using folly::IPAddressV6; -using folly::MacAddress; -using std::make_pair; -using std::make_shared; -using std::shared_ptr; -using std::string; -using testing::Return; - -static const string kVlan1234("Vlan1234"); -static const string kVlan99("Vlan99"); - -TEST(Vlan, applyConfig) { - SaiPlatform platform; - auto stateV0 = make_shared(); - auto vlanV0 = make_shared(VlanID(1234), kVlan1234); - stateV0->addVlan(vlanV0); - stateV0->registerPort(PortID(1), "port1"); - stateV0->registerPort(PortID(99), "port99"); - - NodeID nodeID = vlanV0->getNodeID(); - EXPECT_EQ(0, vlanV0->getGeneration()); - EXPECT_FALSE(vlanV0->isPublished()); - EXPECT_EQ(VlanID(1234), vlanV0->getID()); - Vlan::MemberPorts emptyPorts; - EXPECT_EQ(emptyPorts, vlanV0->getPorts()); - - vlanV0->publish(); - EXPECT_TRUE(vlanV0->isPublished()); - - cfg::SwitchConfig config; - config.ports.resize(2); - config.ports[0].logicalID = 1; - config.ports[0].state = cfg::PortState::UP; - config.ports[1].logicalID = 99; - config.ports[1].state = cfg::PortState::UP; - config.vlans.resize(1); - config.vlans[0].id = 1234; - config.vlans[0].name = kVlan1234; - config.vlans[0].dhcpRelayOverridesV4["02:00:00:00:00:02"] = "1.2.3.4"; - config.vlans[0].dhcpRelayOverridesV6["02:00:00:00:00:02"] = - "2a03:2880:10:1f07:face:b00c:0:0"; - config.vlanPorts.resize(2); - config.vlanPorts[0].logicalPort = 1; - config.vlanPorts[0].vlanID = 1234; - config.vlanPorts[0].emitTags = false; - config.vlanPorts[1].logicalPort = 99; - config.vlanPorts[1].vlanID = 1234; - config.vlanPorts[1].emitTags = true; - - Vlan::MemberPorts expectedPorts; - expectedPorts.insert(make_pair(PortID(1), Vlan::PortInfo(false))); - expectedPorts.insert(make_pair(PortID(99), Vlan::PortInfo(true))); - - auto stateV1 = publishAndApplyConfig(stateV0, &config, &platform); - auto vlanV1 = stateV1->getVlans()->getVlan(VlanID(1234)); - ASSERT_NE(nullptr, vlanV1); - auto vlanV1_byName = stateV1->getVlans()->getVlanSlow(kVlan1234); - EXPECT_EQ(vlanV1, vlanV1_byName); - EXPECT_EQ(nodeID, vlanV1->getNodeID()); - EXPECT_EQ(1, vlanV1->getGeneration()); - EXPECT_FALSE(vlanV1->isPublished()); - EXPECT_EQ(VlanID(1234), vlanV1->getID()); - EXPECT_EQ(kVlan1234, vlanV1->getName()); - EXPECT_EQ(expectedPorts, vlanV1->getPorts()); - EXPECT_EQ(0, vlanV1->getArpResponseTable()->getTable().size()); - - auto map4 = vlanV1->getDhcpV4RelayOverrides(); - EXPECT_EQ(IPAddressV4("1.2.3.4"), - IPAddressV4(map4[MacAddress("02:00:00:00:00:02")])); - auto map6 = vlanV1->getDhcpV6RelayOverrides(); - EXPECT_EQ(IPAddressV6("2a03:2880:10:1f07:face:b00c:0:0"), - IPAddressV6(map6[MacAddress("02:00:00:00:00:02")])); - - // Applying the same config again should return null - EXPECT_EQ(nullptr, publishAndApplyConfig(stateV1, &config, &platform)); - - // Add an interface - config.interfaces.resize(1); - config.interfaces[0].intfID = 1; - config.interfaces[0].routerID = 0; - config.interfaces[0].vlanID = 1234; - config.interfaces[0].ipAddresses.resize(2); - config.interfaces[0].ipAddresses[0] = "10.1.1.1/24"; - config.interfaces[0].ipAddresses[1] = "2a03:2880:10:1f07:face:b00c:0:0/96"; - MacAddress platformMac("82:02:00:ab:cd:ef"); -// EXPECT_CALL(platform, getLocalMac()).WillRepeatedly(Return(platformMac)); - - auto stateV2 = publishAndApplyConfig(stateV1, &config, &platform); - auto vlanV2 = stateV2->getVlans()->getVlan(VlanID(1234)); - EXPECT_EQ(nodeID, vlanV2->getNodeID()); - EXPECT_EQ(2, vlanV2->getGeneration()); - EXPECT_FALSE(vlanV2->isPublished()); - EXPECT_EQ(VlanID(1234), vlanV2->getID()); - EXPECT_EQ(kVlan1234, vlanV2->getName()); - EXPECT_EQ(expectedPorts, vlanV2->getPorts()); - // Check the ArpResponseTable - auto arpRespTable = vlanV2->getArpResponseTable(); - ArpResponseTable expectedArpResp; - expectedArpResp.setEntry(IPAddressV4("10.1.1.1"), platformMac, - InterfaceID(1)); - EXPECT_EQ(expectedArpResp.getTable(), arpRespTable->getTable()); - // Check the NdpResponseTable - auto ndpRespTable = vlanV2->getNdpResponseTable(); - NdpResponseTable expectedNdpResp; - expectedNdpResp.setEntry(IPAddressV6("2a03:2880:10:1f07:face:b00c:0:0"), - platformMac, InterfaceID(1)); - // The link-local IPv6 address should also have been automatically added - // to the NDP response table. - expectedNdpResp.setEntry(IPAddressV6("fe80::8002:00ff:feab:cdef"), - platformMac, InterfaceID(1)); - EXPECT_EQ(expectedNdpResp.getTable(), ndpRespTable->getTable()); - - // Add another interface - config.interfaces.resize(2); - config.interfaces[1].intfID = 2; - config.interfaces[1].routerID = 0; - config.interfaces[1].vlanID = 1234; - config.interfaces[1].ipAddresses.resize(2); - config.interfaces[1].ipAddresses[0] = "10.1.10.1/24"; - config.interfaces[1].ipAddresses[1] = "192.168.0.1/31"; - MacAddress intf2Mac("02:01:02:ab:cd:80"); - config.interfaces[1].mac = intf2Mac.toString(); - config.interfaces[1].__isset.mac = true; - auto stateV3 = publishAndApplyConfig(stateV2, &config, &platform); - auto vlanV3 = stateV3->getVlans()->getVlan(VlanID(1234)); - EXPECT_EQ(nodeID, vlanV3->getNodeID()); - EXPECT_EQ(3, vlanV3->getGeneration()); - EXPECT_FALSE(vlanV3->isPublished()); - EXPECT_EQ(VlanID(1234), vlanV3->getID()); - EXPECT_EQ(kVlan1234, vlanV2->getName()); - EXPECT_EQ(expectedPorts, vlanV3->getPorts()); - // Check the ArpResponseTable - arpRespTable = vlanV3->getArpResponseTable(); - ArpResponseTable expectedTable2; - expectedTable2.setEntry(IPAddressV4("10.1.1.1"), platformMac, - InterfaceID(1)); - expectedTable2.setEntry(IPAddressV4("10.1.10.1"), intf2Mac, - InterfaceID(2)); - expectedTable2.setEntry(IPAddressV4("192.168.0.1"), intf2Mac, - InterfaceID(2)); - EXPECT_EQ(expectedTable2.getTable(), arpRespTable->getTable()); - // The new interface has no IPv6 address, but the NDP table should still - // be updated with the link-local address. - expectedNdpResp.setEntry(IPAddressV6("fe80::1:02ff:feab:cd78"), - intf2Mac, InterfaceID(2)); - EXPECT_EQ(expectedNdpResp.getTable(), - vlanV3->getNdpResponseTable()->getTable()); - - // Configuration should fail if there the VLAN is used by multiple - // different routers - config.interfaces[1].routerID = 1; - EXPECT_THROW(publishAndApplyConfig(stateV3, &config, &platform), FbossError); - config.interfaces[1].routerID = 0; - - // Add a new VLAN with an ArpResponseTable that needs to be set up - // when the VLAN is first created - config.vlans.resize(2); - config.vlans[1].id = 99; - config.vlans[1].name = kVlan99; - config.interfaces.resize(3); - config.interfaces[2].intfID = 3; - config.interfaces[2].routerID = 1; - config.interfaces[2].vlanID = 99; - config.interfaces[2].ipAddresses.resize(2); - config.interfaces[2].ipAddresses[0] = "1.2.3.4/24"; - config.interfaces[2].ipAddresses[1] = "10.0.0.1/9"; - auto stateV4 = publishAndApplyConfig(stateV3, &config, &platform); - ASSERT_NE(nullptr, stateV4); - // VLAN 1234 should be unchanged - EXPECT_EQ(vlanV3, stateV4->getVlans()->getVlan(VlanID(1234))); - auto vlan99 = stateV4->getVlans()->getVlan(VlanID(99)); - auto vlan99_byName = stateV4->getVlans()->getVlanSlow(kVlan99); - ASSERT_NE(nullptr, vlan99); - EXPECT_EQ(vlan99, vlan99_byName); - EXPECT_EQ(0, vlan99->getGeneration()); - ArpResponseTable expectedTable99; - expectedTable99.setEntry(IPAddressV4("1.2.3.4"), platformMac, - InterfaceID(3)); - expectedTable99.setEntry(IPAddressV4("10.0.0.1"), platformMac, - InterfaceID(3)); - EXPECT_EQ(expectedTable99.getTable(), - vlan99->getArpResponseTable()->getTable()); - - // Change vlan99's MTU - auto stateV5 = publishAndApplyConfig(stateV4, &config, &platform); - ASSERT_NE(nullptr, stateV5); - // VLAN 1234 should be unchanged - EXPECT_EQ(vlanV3, stateV5->getVlans()->getVlan(VlanID(1234))); - auto vlan99v1 = stateV5->getVlans()->getVlan(VlanID(99)); - EXPECT_NE(vlan99, vlan99v1); - EXPECT_EQ(1, vlan99v1->getGeneration()); - EXPECT_EQ(vlan99->getDhcpV4Relay(), vlan99v1->getDhcpV4Relay()); - EXPECT_EQ(vlan99->getArpResponseTable(), vlan99v1->getArpResponseTable()); -} - -/* - * Test that forEachChanged(StateDelta::getVlansDelta(), ...) invokes the - * callback for the specified list of changed VLANs. - */ -void checkChangedVlans(const shared_ptr &oldVlans, - const shared_ptr &newVlans, - const std::set changedIDs, - const std::set addedIDs, - const std::set removedIDs) { - auto oldState = make_shared(); - oldState->resetVlans(oldVlans); - auto newState = make_shared(); - newState->resetVlans(newVlans); - - std::set foundChanged; - std::set foundAdded; - std::set foundRemoved; - StateDelta delta(oldState, newState); - DeltaFunctions::forEachChanged(delta.getVlansDelta(), - [&] (const shared_ptr &oldVlan, const shared_ptr &newVlan) { - EXPECT_EQ(oldVlan->getID(), newVlan->getID()); - EXPECT_NE(oldVlan, newVlan); - - auto ret = foundChanged.insert(oldVlan->getID()); - EXPECT_TRUE(ret.second); - }, - [&] (const shared_ptr &vlan) { - auto ret = foundAdded.insert(vlan->getID()); - EXPECT_TRUE(ret.second); - }, - [&] (const shared_ptr &vlan) { - auto ret = foundRemoved.insert(vlan->getID()); - EXPECT_TRUE(ret.second); - }); - - EXPECT_EQ(changedIDs, foundChanged); - EXPECT_EQ(addedIDs, foundAdded); - EXPECT_EQ(removedIDs, foundRemoved); -} - -TEST(VlanMap, applyConfig) { - SaiPlatform platform; - auto stateV0 = make_shared(); - stateV0->registerPort(PortID(1), "port1"); - stateV0->registerPort(PortID(2), "port2"); - stateV0->registerPort(PortID(3), "port3"); - stateV0->registerPort(PortID(4), "port4"); - stateV0->registerPort(PortID(9), "port9"); - stateV0->registerPort(PortID(19), "port19"); - stateV0->registerPort(PortID(20), "port29"); - - auto vlansV0 = stateV0->getVlans(); - - // Apply new config settings - cfg::SwitchConfig config; - config.vlans.resize(2); - config.vlans[0].id = 1234; - config.vlans[0].name = kVlan1234; - config.vlans[1].id = 99; - config.vlans[1].name = kVlan99; - config.vlanPorts.resize(7); - config.vlanPorts[0].vlanID = 1234; - config.vlanPorts[0].logicalPort = 1; - config.vlanPorts[1].vlanID = 1234; - config.vlanPorts[1].logicalPort = 2; - config.vlanPorts[2].vlanID = 1234; - config.vlanPorts[2].logicalPort = 3; - config.vlanPorts[3].vlanID = 1234; - config.vlanPorts[3].logicalPort = 4; - config.vlanPorts[4].vlanID = 99; - config.vlanPorts[4].logicalPort = 9; - config.vlanPorts[5].vlanID = 99; - config.vlanPorts[5].logicalPort = 19; - config.vlanPorts[6].vlanID = 99; - config.vlanPorts[6].logicalPort = 29; - - auto stateV1 = publishAndApplyConfig(stateV0, &config, &platform); - auto vlansV1 = stateV1->getVlans(); - ASSERT_NE(nullptr, vlansV1); - EXPECT_EQ(1, vlansV1->getGeneration()); - EXPECT_EQ(2, vlansV1->size()); - - // Check the new settings for VLAN 1234 - auto vlan1234v0 = vlansV1->getVlan(VlanID(1234)); - auto vlan1234v0_byName = vlansV1->getVlanSlow(kVlan1234); - ASSERT_NE(nullptr, vlan1234v0); - EXPECT_EQ(vlan1234v0, vlan1234v0_byName); - NodeID id1234 = vlan1234v0->getNodeID(); - EXPECT_EQ(VlanID(1234), vlan1234v0->getID()); - EXPECT_EQ(kVlan1234, vlan1234v0->getName()); - EXPECT_EQ(0, vlan1234v0->getGeneration()); - Vlan::MemberPorts ports1234v0; - ports1234v0.insert(make_pair(PortID(1), Vlan::PortInfo(false))); - ports1234v0.insert(make_pair(PortID(2), Vlan::PortInfo(false))); - ports1234v0.insert(make_pair(PortID(3), Vlan::PortInfo(false))); - ports1234v0.insert(make_pair(PortID(4), Vlan::PortInfo(false))); - EXPECT_EQ(ports1234v0, vlan1234v0->getPorts()); - - // Check the new settings for VLAN 99 - auto vlan99v0 = vlansV1->getVlan(VlanID(99)); - ASSERT_NE(nullptr, vlan99v0); - auto vlan99v0_byName = vlansV1->getVlanSlow(kVlan99); - EXPECT_EQ(vlan99v0, vlan99v0_byName); - NodeID id99 = vlan99v0->getNodeID(); - EXPECT_NE(id1234, id99); - EXPECT_EQ(VlanID(99), vlan99v0->getID()); - EXPECT_EQ(kVlan99, vlan99v0->getName()); - EXPECT_EQ(0, vlan99v0->getGeneration()); - Vlan::MemberPorts ports99v1; - ports99v1.insert(make_pair(PortID(9), Vlan::PortInfo(false))); - ports99v1.insert(make_pair(PortID(19), Vlan::PortInfo(false))); - ports99v1.insert(make_pair(PortID(29), Vlan::PortInfo(false))); - EXPECT_EQ(ports99v1, vlan99v0->getPorts()); - - // getVlan() should throw on a non-existent VLAN - EXPECT_THROW(vlansV1->getVlan(VlanID(1)), FbossError); - // getVlanIf() should return null on a non-existent VLAN - EXPECT_EQ(nullptr, vlansV1->getVlanIf(VlanID(1233))); - - checkChangedVlans(vlansV0, vlansV1, {}, {99, 1234}, {}); - - // Applying the same config again should result in no change - EXPECT_EQ(nullptr, publishAndApplyConfig(stateV1, &config, &platform)); - - // Drop one port from VLAN 1234 - config.vlanPorts[0] = config.vlanPorts[6]; - config.vlanPorts.resize(6); - auto stateV2 = publishAndApplyConfig(stateV1, &config, &platform); - auto vlansV2 = stateV2->getVlans(); - ASSERT_NE(nullptr, vlansV2); - EXPECT_EQ(2, vlansV2->getGeneration()); - EXPECT_EQ(2, vlansV2->size()); - - // VLAN 1234 should have changed - auto vlan1234v1 = vlansV2->getVlan(VlanID(1234)); - EXPECT_NE(vlan1234v0, vlan1234v1); - EXPECT_EQ(1, vlan1234v1->getGeneration()); - Vlan::MemberPorts ports1234v1; - ports1234v1.insert(make_pair(PortID(2), Vlan::PortInfo(false))); - ports1234v1.insert(make_pair(PortID(3), Vlan::PortInfo(false))); - ports1234v1.insert(make_pair(PortID(4), Vlan::PortInfo(false))); - EXPECT_EQ(ports1234v1, vlan1234v1->getPorts()); - - // VLAN 99 should not have changed - EXPECT_EQ(vlan99v0, vlansV2->getVlan(VlanID(99))); - - checkChangedVlans(vlansV1, vlansV2, {1234}, {}, {}); - EXPECT_EQ(id1234, vlansV2->getVlan(VlanID(1234))->getNodeID()); - EXPECT_EQ(id99, vlansV2->getVlan(VlanID(99))->getNodeID()); - - // Remove VLAN 99 - config.vlans.resize(1); - config.vlans[0].id = 1234; - config.vlanPorts.resize(3); - config.vlanPorts[0].vlanID = 1234; - config.vlanPorts[0].logicalPort = 2; - config.vlanPorts[1].vlanID = 1234; - config.vlanPorts[1].logicalPort = 3; - config.vlanPorts[2].vlanID = 1234; - config.vlanPorts[2].logicalPort = 4; - - auto stateV3 = publishAndApplyConfig(stateV2, &config, &platform); - auto vlansV3 = stateV3->getVlans(); - ASSERT_NE(nullptr, vlansV3); - EXPECT_EQ(3, vlansV3->getGeneration()); - EXPECT_EQ(1, vlansV3->size()); - - // VLAN 1234 should not have changed - EXPECT_EQ(vlan1234v1, vlansV3->getVlan(VlanID(1234))); - // VLAN 99 should no longer exist - EXPECT_EQ(nullptr, vlansV3->getVlanIf(VlanID(99))); - - checkChangedVlans(vlansV2, vlansV3, {}, {}, {99}); -} diff --git a/fboss/agent/platforms/sai/Sai_test_ctrl.cpp b/fboss/agent/platforms/sai/Sai_test_ctrl.cpp deleted file mode 100644 index de284ab35bcda..0000000000000 --- a/fboss/agent/platforms/sai/Sai_test_ctrl.cpp +++ /dev/null @@ -1,16 +0,0 @@ -/* - * 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 "gtest/gtest.h" - -int main(int argc, char **argv) { - ::testing::InitGoogleTest(&argc, argv); - return RUN_ALL_TESTS(); -} From 057fe3e4505ab73aa7e5b6227c19f4ccf3f6ff3d Mon Sep 17 00:00:00 2001 From: Zubin Shah Date: Wed, 2 Mar 2016 20:49:00 -0800 Subject: [PATCH 11/24] Update BUILD.md with SAI instructions --- BUILD.md | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/BUILD.md b/BUILD.md index 9d1fb973a428e..36ca26e624c04 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,10 @@ 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 a specific SAI adapter, copy the SAI adapter shared library +(libSaiAdapter.so) to external/SAI/lib/ directory or modify path to the library +file in CMakeList.txt. Build as follows: @@ -36,5 +40,5 @@ cd fboss/build cmake .. make ``` - -The produced executables are `sim_agent` and `wedge_agent` in the build directory. +NOTE: use "cmake .. -DWITH_SAI:BOOL=ON" to build sai_agent. +The produced executables are `sim_agent` and `wedge_agent` (or `sai_agent`) in the build directory. From 0deed5311836e71541aa9609aea29f1db317c024 Mon Sep 17 00:00:00 2001 From: Zubin Shah Date: Wed, 2 Mar 2016 06:14:03 -0800 Subject: [PATCH 12/24] update build instructions and steps for SAI compilation --- BUILD.md | 8 ++++---- fboss/agent/hw/sai/SaiSwitch.cpp | 20 ++++++++++---------- fboss/github/CMakeLists_SAI.txt | 12 ++++++------ 3 files changed, 20 insertions(+), 20 deletions(-) diff --git a/BUILD.md b/BUILD.md index 36ca26e624c04..c8d42724fbfb5 100644 --- a/BUILD.md +++ b/BUILD.md @@ -28,9 +28,10 @@ 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 build with a specific SAI adapter, copy the SAI adapter shared library -(libSaiAdapter.so) to external/SAI/lib/ directory or modify path to the library -file in CMakeList.txt. + +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: @@ -40,5 +41,4 @@ cd fboss/build cmake .. make ``` -NOTE: use "cmake .. -DWITH_SAI:BOOL=ON" to build sai_agent. The produced executables are `sim_agent` and `wedge_agent` (or `sai_agent`) in the build directory. diff --git a/fboss/agent/hw/sai/SaiSwitch.cpp b/fboss/agent/hw/sai/SaiSwitch.cpp index 47c86b1dfd73f..61e9eb650cd26 100644 --- a/fboss/agent/hw/sai/SaiSwitch.cpp +++ b/fboss/agent/hw/sai/SaiSwitch.cpp @@ -68,12 +68,12 @@ 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); -} +//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 { @@ -98,10 +98,10 @@ SaiSwitch::SaiSwitch(SaiPlatformBase *platform) , lockFd(-1) { VLOG(4) << "Entering " << __FUNCTION__; - 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_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_); diff --git a/fboss/github/CMakeLists_SAI.txt b/fboss/github/CMakeLists_SAI.txt index 395da6f0f7c59..a8fa53a203e6c 100644 --- a/fboss/github/CMakeLists_SAI.txt +++ b/fboss/github/CMakeLists_SAI.txt @@ -10,7 +10,7 @@ SET(WITH_SAI OFF CACHE BOOL "Optionally build with SAI adapter") # 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++11") +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") @@ -101,6 +101,11 @@ add_library(fboss_sai_agent STATIC 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 @@ -135,14 +140,9 @@ add_library(fboss_sai_agent STATIC fboss/agent/packet/NDPRouterAdvertisement.cpp fboss/agent/packet/PktUtil.cpp fboss/agent/Platform.cpp - fboss/agent/platforms/wedge/oss/WedgePlatform.cpp - fboss/agent/platforms/wedge/oss/Wedge100Platform.cpp fboss/agent/platforms/wedge/oss/WedgePort.cpp fboss/agent/platforms/wedge/oss/WedgeProductInfo.cpp - fboss/agent/platforms/wedge/wedge_ctrl.cpp fboss/agent/platforms/wedge/WedgeI2CBusLock.cpp - fboss/agent/platforms/wedge/WedgePlatform.cpp - fboss/agent/platforms/wedge/Wedge100Platform.cpp fboss/agent/platforms/wedge/WedgePort.cpp fboss/agent/platforms/wedge/WedgeProductInfo.cpp fboss/agent/platforms/wedge/WedgeQsfp.cpp From 2ef1f477903653ea2706ed26522677660806ca94 Mon Sep 17 00:00:00 2001 From: Zubin Shah Date: Wed, 2 Mar 2016 06:16:41 -0800 Subject: [PATCH 13/24] update CMakeLists.txt for sai compilation --- fboss/github/CMakeLists_SAI.txt | 2 -- 1 file changed, 2 deletions(-) diff --git a/fboss/github/CMakeLists_SAI.txt b/fboss/github/CMakeLists_SAI.txt index a8fa53a203e6c..6771c601bff12 100644 --- a/fboss/github/CMakeLists_SAI.txt +++ b/fboss/github/CMakeLists_SAI.txt @@ -2,8 +2,6 @@ cmake_minimum_required(VERSION 2.8) project(FBOSS) include(CMakeParseArguments) -SET(WITH_SAI OFF CACHE BOOL "Optionally build with SAI adapter") - # 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 From 516638a7ee4b1f9a9e105f7c151862710de51516 Mon Sep 17 00:00:00 2001 From: Vitaliy Senchyshyn Date: Mon, 11 Apr 2016 15:51:41 +0300 Subject: [PATCH 14/24] Fixed log message in SaiPortTable.cpp --- fboss/agent/hw/sai/SaiPortTable.cpp | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/fboss/agent/hw/sai/SaiPortTable.cpp b/fboss/agent/hw/sai/SaiPortTable.cpp index fd5c2e5997d9e..23f6b0f505b2e 100644 --- a/fboss/agent/hw/sai/SaiPortTable.cpp +++ b/fboss/agent/hw/sai/SaiPortTable.cpp @@ -75,7 +75,9 @@ SaiPortBase *SaiPortTable::GetSaiPort(sai_object_id_t id) const { auto iter = saiPhysicalPorts_.find(id); if (iter == saiPhysicalPorts_.end()) { - throw SaiError("Cannot find the SAI port object for SAI port ", id); + 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(); From 26ae30b444a533aabf9bedde2df85d5600093d50 Mon Sep 17 00:00:00 2001 From: Vitaliy Senchyshyn Date: Mon, 11 Apr 2016 16:52:39 +0300 Subject: [PATCH 15/24] Add resolution of Hosts, Next Hops and Routes when ARP is resolved. --- fboss/agent/hw/sai/SaiHost.cpp | 73 +++++--- fboss/agent/hw/sai/SaiHost.h | 17 +- fboss/agent/hw/sai/SaiHostTable.cpp | 75 +++----- fboss/agent/hw/sai/SaiHostTable.h | 28 +-- fboss/agent/hw/sai/SaiIntf.cpp | 14 +- fboss/agent/hw/sai/SaiNextHop.cpp | 246 ++++++++++++++++++------- fboss/agent/hw/sai/SaiNextHop.h | 50 ++++- fboss/agent/hw/sai/SaiNextHopTable.cpp | 13 +- fboss/agent/hw/sai/SaiNextHopTable.h | 12 +- fboss/agent/hw/sai/SaiRoute.cpp | 55 +++++- fboss/agent/hw/sai/SaiRoute.h | 18 ++ fboss/agent/hw/sai/SaiRouteTable.cpp | 35 +++- fboss/agent/hw/sai/SaiRouteTable.h | 12 +- fboss/agent/hw/sai/SaiSwitch.cpp | 200 +++++--------------- fboss/agent/hw/sai/SaiSwitch.h | 3 - 15 files changed, 496 insertions(+), 355 deletions(-) diff --git a/fboss/agent/hw/sai/SaiHost.cpp b/fboss/agent/hw/sai/SaiHost.cpp index 0bb179ead4587..b36549d7440f2 100644 --- a/fboss/agent/hw/sai/SaiHost.cpp +++ b/fboss/agent/hw/sai/SaiHost.cpp @@ -41,10 +41,10 @@ SaiHost::~SaiHost() { } pSaiNeighborApi_->remove_neighbor_entry(&neighborEntry_); - VLOG(3) << "deleted L3 host object for " << ip_; + VLOG(3) << "Deleted L3 host object for " << ip_; } -void SaiHost::Program(sai_packet_action_t action) { +void SaiHost::Program(sai_packet_action_t action, const folly::MacAddress &mac) { VLOG(4) << "Entering " << __FUNCTION__; if (!added_) { @@ -71,52 +71,73 @@ void SaiHost::Program(sai_packet_action_t action) { } // fill neighbor attributes - sai_attribute_t attr_list[SAI_NEIGHBOR_ATTR_COUNT] = {0}; + std::vector attrList; + sai_attribute_t attr {0}; // MAC - attr_list[0].id = SAI_NEIGHBOR_ATTR_DST_MAC_ADDRESS; - memcpy(attr_list[0].value.mac, mac_.bytes(), sizeof(attr_list[0].value.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_list[1].id = SAI_NEIGHBOR_ATTR_PACKET_ACTION; - attr_list[1].value.u32 = action; + attr.id = SAI_NEIGHBOR_ATTR_PACKET_ACTION; + attr.value.u32 = action; + attrList.push_back(attr); // create neighbor sai_status_t saiRetVal = pSaiNeighborApi_->create_neighbor_entry(&neighborEntry_, - SAI_NEIGHBOR_ATTR_COUNT, - attr_list); + 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); + " MAC: ", mac_, " router interface ID: ", intf_, + " Error: ", saiRetVal); } added_ = true; action_ = action; return; - - } else if (action == action_) { - // nothing to do more - return; } - // fill neighbor packet action attribute - sai_attribute_t action_attr = {0}; - action_attr.id = SAI_NEIGHBOR_ATTR_PACKET_ACTION; - action_attr.value.u32 = action; + 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 = pSaiNeighborApi_->set_neighbor_attribute(&neighborEntry_, - &action_attr); - if (saiRetVal != SAI_STATUS_SUCCESS) { - throw SaiError("Could not set packet action attribute: ", action, + // set action attribute + sai_status_t saiRetVal = pSaiNeighborApi_->set_neighbor_attribute(&neighborEntry_, + &macAttr); + if (saiRetVal != SAI_STATUS_SUCCESS) { + throw SaiError("Could not set MAC: ", mac, "to SAI neighbor with IP addr: ", ip_, - " MAC: ", mac_, " router interface ID: ", intf_, + " router interface ID: ", intf_, " Error: ", saiRetVal); + } + + mac_ = mac; } - action_ = action; + 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 = pSaiNeighborApi_->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 index c5272b7ee3289..379b055f5c899 100644 --- a/fboss/agent/hw/sai/SaiHost.h +++ b/fboss/agent/hw/sai/SaiHost.h @@ -45,6 +45,15 @@ class SaiHost { */ 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. @@ -53,21 +62,17 @@ class SaiHost { * * @return none */ - void Program(sai_packet_action_t action); + 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; - - enum { - SAI_NEIGHBOR_ATTR_COUNT = 2 - }; const SaiSwitch *hw_; const InterfaceID intf_; const folly::IPAddress ip_; - const folly::MacAddress mac_; + folly::MacAddress mac_; sai_packet_action_t action_ {SAI_PACKET_ACTION_DROP}; bool added_ {false}; // if added to the HW host table or not diff --git a/fboss/agent/hw/sai/SaiHostTable.cpp b/fboss/agent/hw/sai/SaiHostTable.cpp index 122626b5f7f32..11681418703a8 100644 --- a/fboss/agent/hw/sai/SaiHostTable.cpp +++ b/fboss/agent/hw/sai/SaiHostTable.cpp @@ -22,19 +22,32 @@ SaiHostTable::~SaiHostTable() { VLOG(4) << "Entering " << __FUNCTION__; } -SaiHost* SaiHostTable::IncRefOrCreateSaiHost(InterfaceID intf, +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, mac); - auto ret = hosts_.emplace(key, std::make_pair(nullptr, 1)); + Key key(intf, ip); + auto ret = hosts_.emplace(key, nullptr); auto &iter = ret.first; if (!ret.second) { // there was an entry already there - iter->second.second++; // increase the reference counter - return iter->second.first.get(); + return iter->second.get(); } SCOPE_FAIL { @@ -44,55 +57,27 @@ SaiHost* SaiHostTable::IncRefOrCreateSaiHost(InterfaceID intf, auto newHost = folly::make_unique(hw_, intf, ip, mac); auto hostPtr = newHost.get(); - iter->second.first = std::move(newHost); + iter->second = std::move(newHost); return hostPtr; } -SaiHost *SaiHostTable::GetSaiHost(InterfaceID intf, - const folly::IPAddress &ip, - const folly::MacAddress &mac) const { +void SaiHostTable::removeSaiHost(InterfaceID intf, + const folly::IPAddress &ip) noexcept { VLOG(4) << "Entering " << __FUNCTION__; - Key key(intf, ip, mac); + Key key(intf, ip); auto iter = hosts_.find(key); - if (iter == hosts_.end()) { - return nullptr; - } - - return iter->second.first.get(); -} - -SaiHost *SaiHostTable::DerefSaiHost(InterfaceID intf, - const folly::IPAddress &ip, - const folly::MacAddress &mac) noexcept { - VLOG(4) << "Entering " << __FUNCTION__; - - Key key(intf, ip, mac); - auto iter = hosts_.find(key); - - if (iter == hosts_.end()) { - return nullptr; - } - - auto &entry = iter->second; - CHECK_GT(entry.second, 0); - - if (--entry.second == 0) { + if (iter != hosts_.end()) { hosts_.erase(iter); - return nullptr; } - - return entry.first.get(); } SaiHostTable::Key::Key(InterfaceID intf, - const folly::IPAddress &ip, - const folly::MacAddress &mac) + const folly::IPAddress &ip) : intf_(intf) - , ip_(ip) - , mac_(mac) { + , ip_(ip) { } bool SaiHostTable::Key::operator<(const Key &k) const{ @@ -104,15 +89,7 @@ bool SaiHostTable::Key::operator<(const Key &k) const{ return false; } - if (ip_ < k.ip_) { - return true; - } - - if (ip_ > k.ip_) { - return false; - } - - return (mac_ < k.mac_); + return (ip_ < k.ip_); } }} // facebook::fboss diff --git a/fboss/agent/hw/sai/SaiHostTable.h b/fboss/agent/hw/sai/SaiHostTable.h index a69f19dbd049f..07dbaad7f887a 100644 --- a/fboss/agent/hw/sai/SaiHostTable.h +++ b/fboss/agent/hw/sai/SaiHostTable.h @@ -27,49 +27,39 @@ class SaiHostTable { virtual ~SaiHostTable(); // return nullptr if not found - SaiHost *GetSaiHost(InterfaceID intf, - const folly::IPAddress &ip, - const folly::MacAddress &mac) const; - + SaiHost *getSaiHost(InterfaceID intf, const folly::IPAddress &ip) const; /** - * Allocates a new SaiHost if no such one exists. - * For the existing entry, incRefOrCreateSaiHost() will increase - * the reference counter by 1. + * Allocates a new SaiHost if no such one exists or + * updates an existing one. * * @return The SaiHost pointer just created or found. */ - SaiHost *IncRefOrCreateSaiHost(InterfaceID intf, + SaiHost *createOrUpdateSaiHost(InterfaceID intf, const folly::IPAddress &ip, const folly::MacAddress &mac); /** - * Decrease an existing SaiHost entry's reference counter by 1. - * Only until the reference counter is 0, the SaiHost entry - * is deleted. + * Remove an existing SaiHost entry * * @return nullptr, if the SaiHost entry is deleted * @retrun the SaiHost object that has reference counter * decreased by 1, but the object is still valid as it is * still referred in somewhere else */ - SaiHost *DerefSaiHost(InterfaceID intf, - const folly::IPAddress &ip, - const folly::MacAddress &mac) noexcept; + void removeSaiHost(InterfaceID intf, + const folly::IPAddress &ip) noexcept; private: struct Key { Key(InterfaceID intf, - const folly::IPAddress &ip, - const folly::MacAddress &mac); + const folly::IPAddress &ip); bool operator<(const Key &k) const; InterfaceID intf_; folly::IPAddress ip_; - folly::MacAddress mac_; }; - typedef std::pair, uint32_t> HostMapNode; - typedef boost::container::flat_map HostMap; + typedef boost::container::flat_map> HostMap; const SaiSwitch *hw_; HostMap hosts_; diff --git a/fboss/agent/hw/sai/SaiIntf.cpp b/fboss/agent/hw/sai/SaiIntf.cpp index db317e8b0be8f..83e2ca19c3f63 100644 --- a/fboss/agent/hw/sai/SaiIntf.cpp +++ b/fboss/agent/hw/sai/SaiIntf.cpp @@ -155,14 +155,14 @@ void SaiIntf::Program(const shared_ptr &intf) { } auto createHost = [&](const IPAddress& addr) { - auto host = hw_->WritableHostTable()->IncRefOrCreateSaiHost(intf->getID(), - addr, - intf->getMac()); + auto host = hw_->WritableHostTable()->createOrUpdateSaiHost(intf->getID(), + addr, + intf->getMac()); try { - host->Program(SAI_PACKET_ACTION_TRAP); + host->Program(SAI_PACKET_ACTION_TRAP, intf->getMac()); } catch (const FbossError &e) { - hw_->WritableHostTable()->DerefSaiHost(intf->getID(), addr, intf->getMac()); + hw_->WritableHostTable()->removeSaiHost(intf->getID(), addr); // let handle this in the caller. throw; } @@ -170,7 +170,7 @@ void SaiIntf::Program(const shared_ptr &intf) { auto ret = hosts_.insert(addr); CHECK(ret.second); SCOPE_FAIL { - hw_->WritableHostTable()->DerefSaiHost(intf->getID(), addr, intf->getMac()); + hw_->WritableHostTable()->removeSaiHost(intf->getID(), addr); hosts_.erase(ret.first); }; }; @@ -227,7 +227,7 @@ void SaiIntf::removeHost(const folly::IPAddress& addr) { return; } - hw_->WritableHostTable()->DerefSaiHost(intf_->getID(), addr, intf_->getMac()); + hw_->WritableHostTable()->removeSaiHost(intf_->getID(), addr); hosts_.erase(iter); } diff --git a/fboss/agent/hw/sai/SaiNextHop.cpp b/fboss/agent/hw/sai/SaiNextHop.cpp index 44538dc120c14..4612a6a2ccb6d 100644 --- a/fboss/agent/hw/sai/SaiNextHop.cpp +++ b/fboss/agent/hw/sai/SaiNextHop.cpp @@ -15,6 +15,7 @@ #include "SaiError.h" #include "SaiIntfTable.h" #include "SaiIntf.h" +#include "SaiHostTable.h" namespace facebook { namespace fboss { @@ -24,6 +25,17 @@ SaiNextHop::SaiNextHop(const SaiSwitch *hw, const RouteForwardInfo &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); + pSaiNextHopApi_ = hw->GetSaiNextHopApi(); pSaiNextHopGroupApi_ = hw->GetSaiNextHopGroupApi(); } @@ -34,20 +46,20 @@ SaiNextHop::~SaiNextHop() { sai_status_t saiRetVal = SAI_STATUS_FAILURE; // Remove all next hops and group(if it was created) from the HW. - for (auto iter = nextHops_.begin(); iter != nextHops_.end(); ++iter) { + for (auto iter : hwNextHops_) { if (nhGroupId_ != SAI_NULL_OBJECT_ID) { - saiRetVal = pSaiNextHopGroupApi_->remove_next_hop_from_group(nhGroupId_, 1, &iter->first); + saiRetVal = pSaiNextHopGroupApi_->remove_next_hop_from_group(nhGroupId_, 1, &iter.first); if (saiRetVal != SAI_STATUS_SUCCESS) { - LOG(ERROR) << "Could not remove next hop " << iter->second.str() << " from next hop group: " + LOG(ERROR) << "Could not remove next hop " << iter.second.str() << " from next hop group: " << nhGroupId_ << " of next hops: " << fwdInfo_.str() << "Error: " << saiRetVal; } } - saiRetVal = pSaiNextHopApi_->remove_next_hop(iter->first); + saiRetVal = pSaiNextHopApi_->remove_next_hop(iter.first); if (saiRetVal != SAI_STATUS_SUCCESS) { - LOG(ERROR) << "Could not remove next hop " << iter->second.str() + LOG(ERROR) << "Could not remove next hop " << iter.second.str() << " from HW. Error: " << saiRetVal; } } @@ -63,105 +75,207 @@ SaiNextHop::~SaiNextHop() { sai_object_id_t SaiNextHop::GetSaiNextHopId() const { - if (nhGroupId_ != SAI_NULL_OBJECT_ID) { + if (isGroup_) { return nhGroupId_; } - auto iter = nextHops_.begin(); - if (iter == nextHops_.end()) { - throw SaiError("Attempt to get SAI ID of the next hop: ", - fwdInfo_, "which is not programmed on hardware"); + 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 = pSaiNextHopGroupApi_->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; + pSaiNextHopApi_->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) { + pSaiNextHopApi_->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; - RouteForwardInfo::Nexthops nextHops = fwdInfo_.getNexthops(); + sai_status_t saiRetVal {SAI_STATUS_FAILURE}; - if (nextHops.empty()) { + if (fwdInfo_.getNexthops().empty()) { throw SaiError("Next hop list to be programmed on HW is empty"); } - if ((nhGroupId_ != SAI_NULL_OBJECT_ID) || - (nextHops_.size() != 0)) { - - throw SaiError("Attempt to configure for the second time the next hop ", - fwdInfo_.str().c_str(), "which was already programmed in HW"); + if ((nhGroupId_ != SAI_NULL_OBJECT_ID) || !hwNextHops_.empty()) { + // Already configured. Nothing to do more here. + return; } - uint8_t nh_count = nextHops.size(); - // an array of object IDs passed to create_next_hop_group() - std::unique_ptr nextHopIds(new sai_object_id_t[nh_count]); + std::vector nextHopIds; - // Create all next hops and fill the nextHops_ map and nextHopIds array - uint8_t i = 0; - for (auto iter = nextHops.begin(); iter != nextHops.end(); ++iter, ++i) { + // Create all next hops and fill the hwNextHops_ map and nextHopIds array + for (auto iter : resolvedNextHops_) { - sai_object_id_t nhId = SAI_NULL_OBJECT_ID; - sai_attribute_t attr_list[SAI_NH_ATTR_COUNT] = {0}; + sai_object_id_t nhId = programNh(iter.intf, iter.nexthop); - // Fill atributes - // NH type - attr_list[0].id = SAI_NEXT_HOP_ATTR_TYPE; - attr_list[0].value.u32 = SAI_NEXT_HOP_IP; + if (nhId != SAI_NULL_OBJECT_ID) { + hwNextHops_.emplace(nhId, iter); + nextHopIds.push_back(nhId); + } + } - // IP address - attr_list[1].id = SAI_NEXT_HOP_ATTR_IP; + if (!isGroup_ || nextHopIds.empty()) { + // Nothing more to do for a single next hop or a group + // with empty NH list. + return; + } - if (iter->nexthop.isV4()) { - attr_list[1].value.ipaddr.addr_family = SAI_IP_ADDR_FAMILY_IPV4; - memcpy(&attr_list[1].value.ipaddr.addr.ip4, iter->nexthop.bytes(), sizeof(attr_list[1].value.ip4)); - } else { - attr_list[1].value.ipaddr.addr_family = SAI_IP_ADDR_FAMILY_IPV6; - memcpy(attr_list[1].value.ipaddr.addr.ip6, iter->nexthop.bytes(), sizeof(attr_list[1].value.ip6)); - } + // 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 iter : hwNextHops_) { - // Router interface ID - attr_list[2].id = SAI_NEXT_HOP_ATTR_ROUTER_INTERFACE_ID; - attr_list[2].value.oid = hw_->GetIntfTable()->GetIntfIf(iter->intf)->GetIfId(); + saiRetVal = pSaiNextHopApi_->remove_next_hop(iter.first); + if (saiRetVal != SAI_STATUS_SUCCESS) { + LOG(ERROR) << "Could not remove next hop " << iter.second.str() + << " from HW. Error: " << saiRetVal; + } - // Create NH in SAI - saiRetVal = pSaiNextHopApi_->create_next_hop(&nhId, SAI_NH_ATTR_COUNT, attr_list); - if (saiRetVal != SAI_STATUS_SUCCESS) { - throw SaiError("Could not create next hop ", iter->str().c_str(), " on HW. Error: ", saiRetVal); + unresolvedNextHops_.emplace(iter.second); } - nextHops_.emplace(nhId, *iter); - nextHopIds[i] = nhId; + resolvedNextHops_.clear(); + hwNextHops_.clear(); } - if (nh_count == 1) { - // Nothing more to do for a single host. - return; - } + resolved_ = true; +} - // Now we will create NH group with the next hops previously created - sai_attribute_t attr_list[SAI_NH_GROUP_ATTR_COUNT] = {0}; - sai_object_list_t nhList = {nh_count, nextHopIds.get()}; - sai_object_id_t nhGroupId = SAI_NULL_OBJECT_ID; +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 group type - attr_list[0].id = SAI_NEXT_HOP_GROUP_ATTR_TYPE; - attr_list[0].value.u32 = SAI_NEXT_HOP_GROUP_ECMP; + // 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); - // NH list - attr_list[1].id = SAI_NEXT_HOP_ATTR_IP; - attr_list[1].value.objlist = nhList; + // 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 group in SAI - saiRetVal = pSaiNextHopGroupApi_->create_next_hop_group(&nhGroupId, SAI_NH_GROUP_ATTR_COUNT, attr_list); + // Create NH in SAI + saiRetVal = pSaiNextHopApi_->create_next_hop(&nhId, attrList.size(), attrList.data()); if (saiRetVal != SAI_STATUS_SUCCESS) { - throw SaiError("Could not create next hop group", fwdInfo_.str().c_str(), " on HW. Error: ", saiRetVal); + 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 = pSaiNextHopGroupApi_->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 = pSaiNextHopGroupApi_->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; + } } - nhGroupId_ = nhGroupId; + return nhGroupId_; } }} // facebook::fboss diff --git a/fboss/agent/hw/sai/SaiNextHop.h b/fboss/agent/hw/sai/SaiNextHop.h index d65bbcc0d8d67..5abd62283c791 100644 --- a/fboss/agent/hw/sai/SaiNextHop.h +++ b/fboss/agent/hw/sai/SaiNextHop.h @@ -11,6 +11,7 @@ #pragma once #include +#include #include "fboss/agent/types.h" @@ -45,18 +46,35 @@ class SaiNextHop { /** * Gets Sai Next Hop ID or Sai Next Hop Group 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 + * @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. - * If one of SAI HW calls fails it throws an exception; * * @return none */ @@ -67,15 +85,29 @@ class SaiNextHop { SaiNextHop(SaiNextHop const &) = delete; SaiNextHop &operator=(SaiNextHop const &) = delete; - enum { - SAI_NH_GROUP_ATTR_COUNT = 2, - SAI_NH_ATTR_COUNT = 3 - }; + /** + * 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_; - std::map nextHops_; + 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 *pSaiNextHopApi_ {nullptr}; diff --git a/fboss/agent/hw/sai/SaiNextHopTable.cpp b/fboss/agent/hw/sai/SaiNextHopTable.cpp index e5a3a837f729a..b9b987df72d7b 100644 --- a/fboss/agent/hw/sai/SaiNextHopTable.cpp +++ b/fboss/agent/hw/sai/SaiNextHopTable.cpp @@ -31,7 +31,7 @@ sai_object_id_t SaiNextHopTable::GetSaiNextHopId(const RouteForwardInfo &fwdInfo auto iter = nextHops_.find(fwdInfo.getNexthops()); if (iter == nextHops_.end()) { - throw SaiError("Cannot find SaiNextHop object for forwarding info: ", fwdInfo); + return SAI_NULL_OBJECT_ID; } return iter->second.first->GetSaiNextHopId(); @@ -81,4 +81,15 @@ SaiNextHop* SaiNextHopTable::DerefSaiNextHop(const RouteForwardInfo &fwdInfo) no return entry.first.get(); } +void SaiNextHopTable::onResolved(InterfaceID intf, const folly::IPAddress &ip) { + + for (auto iter = nextHops_.begin(); iter != nextHops_.end(); ++iter) { + try { + iter->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 index 2745c6ea2c948..f61f7a1e40de8 100644 --- a/fboss/agent/hw/sai/SaiNextHopTable.h +++ b/fboss/agent/hw/sai/SaiNextHopTable.h @@ -30,9 +30,9 @@ class SaiNextHopTable { /** * Given fwdInfo gets Sai Next Hop ID of sai_object_id_t type. - * Throws an exception if there is no such Next Hop found. * - * @return Sai Vrf 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; @@ -64,6 +64,14 @@ class SaiNextHopTable { */ 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; diff --git a/fboss/agent/hw/sai/SaiRoute.cpp b/fboss/agent/hw/sai/SaiRoute.cpp index 949c16f77a7f6..f18750fcd5ea6 100644 --- a/fboss/agent/hw/sai/SaiRoute.cpp +++ b/fboss/agent/hw/sai/SaiRoute.cpp @@ -101,6 +101,7 @@ void SaiRoute::Program(const RouteForwardInfo &fwd) { // 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 @@ -167,8 +168,8 @@ void SaiRoute::Program(const RouteForwardInfo &fwd) { sai_status_t saiRetVal = pSaiRouteApi_->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); + " netmask length: ", prefixLen_, " vrf: ", vrf_, + " Error: ", saiRetVal); } added_ = true; @@ -183,10 +184,12 @@ void SaiRoute::Program(const RouteForwardInfo &fwd) { 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; @@ -202,8 +205,8 @@ void SaiRoute::Program(const RouteForwardInfo &fwd) { sai_status_t saiRetVal = pSaiRouteApi_->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); + " netmask length: ", prefixLen_, " vrf: ", vrf_, + " Error: ", saiRetVal); } if (newAction == RouteForwardAction::NEXTHOPS) { @@ -211,16 +214,52 @@ void SaiRoute::Program(const RouteForwardInfo &fwd) { routeAttr.id = SAI_ROUTE_ATTR_NEXT_HOP_ID; routeAttr.value.oid = hw_->WritableNextHopTable()->IncRefOrCreateSaiNextHop(fwd)->GetSaiNextHopId(); - // Set next hop ID route attribute in SAI - saiRetVal = pSaiRouteApi_->set_route_attribute(&routeEntry_, &routeAttr); - if (saiRetVal != SAI_STATUS_SUCCESS) { - throw SaiError("Could not set next hop ID attribute for route with addr: ", ipAddr_, + if (routeAttr.value.oid != SAI_NULL_OBJECT_ID) { + // Set next hop ID route attribute in SAI + saiRetVal = pSaiRouteApi_->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 = pSaiRouteApi_->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 index 331fd05ec237f..146d5e08ec9ab 100644 --- a/fboss/agent/hw/sai/SaiRoute.h +++ b/fboss/agent/hw/sai/SaiRoute.h @@ -55,6 +55,23 @@ class SaiRoute { */ 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); @@ -64,6 +81,7 @@ class SaiRoute { folly::IPAddress ipAddr_; uint8_t prefixLen_; RouteForwardInfo fwd_; + bool resolved_ {false}; sai_unicast_route_entry_t routeEntry_; bool added_ {false}; diff --git a/fboss/agent/hw/sai/SaiRouteTable.cpp b/fboss/agent/hw/sai/SaiRouteTable.cpp index 82ec8effb53f9..ed5d99bf0b443 100644 --- a/fboss/agent/hw/sai/SaiRouteTable.cpp +++ b/fboss/agent/hw/sai/SaiRouteTable.cpp @@ -91,7 +91,12 @@ void SaiRouteTable::AddRoute(RouterID vrf, const RouteT *route) { prefix.mask)); } - ret.first->second->Program(route->getForwardInfo()); + auto pRoute = ret.first->second.get(); + pRoute->Program(route->getForwardInfo()); + + if (!pRoute->isResolved()) { + unresolvedRoutes_.insert(ret.first->second.get()); + } } template @@ -106,9 +111,37 @@ void SaiRouteTable::DeleteRoute(RouterID vrf, const RouteT *route) { 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 *); diff --git a/fboss/agent/hw/sai/SaiRouteTable.h b/fboss/agent/hw/sai/SaiRouteTable.h index b4c1f04ad6775..db3d274c5e576 100644 --- a/fboss/agent/hw/sai/SaiRouteTable.h +++ b/fboss/agent/hw/sai/SaiRouteTable.h @@ -40,6 +40,16 @@ class SaiRouteTable { 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; @@ -49,7 +59,7 @@ class SaiRouteTable { }; const SaiSwitch *hw_; boost::container::flat_map> fib_; - + boost::container::flat_set unresolvedRoutes_; }; }} // facebook::fboss diff --git a/fboss/agent/hw/sai/SaiSwitch.cpp b/fboss/agent/hw/sai/SaiSwitch.cpp index 61e9eb650cd26..b6172c3f9f773 100644 --- a/fboss/agent/hw/sai/SaiSwitch.cpp +++ b/fboss/agent/hw/sai/SaiSwitch.cpp @@ -370,8 +370,6 @@ void SaiSwitch::stateChanged(const StateDelta &delta) { // Add all new interfaces forEachAdded(delta.getIntfsDelta(), &SaiSwitch::ProcessAddedIntf, this); - AddHostsForDemo(); - // Any ARP changes ProcessArpChanges(delta); @@ -415,7 +413,7 @@ bool SaiSwitch::sendPacketSwitched(std::unique_ptr pkt) noexcept { attr.id = SAI_HOSTIF_PACKET_TX_TYPE; attr.value.s32 = SAI_HOSTIF_TX_TYPE_PIPELINE_LOOKUP; attrList.push_back(attr); -#if 0 + sai_status_t status = pSaiHostIntfApi_->send_packet(hostIfFdId_, pkt->buf()->writableData(), pkt->buf()->length(), @@ -425,7 +423,7 @@ bool SaiSwitch::sendPacketSwitched(std::unique_ptr pkt) noexcept { LOG(ERROR) << "Failed to send switched packet. Error: " << status; return false; } -#endif + return true; } @@ -451,7 +449,7 @@ bool SaiSwitch::sendPacketOutOfPort(std::unique_ptr pkt, PortID portID attr.id = SAI_HOSTIF_PACKET_EGRESS_PORT_OR_LAG; attr.value.oid = portId; attrList.push_back(attr); -#if 0 + sai_status_t status = pSaiHostIntfApi_->send_packet(hostIfFdId_, pkt->buf()->writableData(), pkt->buf()->length(), @@ -461,7 +459,7 @@ bool SaiSwitch::sendPacketOutOfPort(std::unique_ptr pkt, PortID portID LOG(ERROR) << "Failed to send packet out of port: " << portID.t << " Error: " << status; return false; } -#endif + return true; } @@ -605,50 +603,58 @@ void SaiSwitch::ProcessNeighborEntryDelta(const DELTA &delta) { const auto *oldEntry = delta.getOld().get(); const auto *newEntry = delta.getNew().get(); - auto refNewEntry = [&]() { + 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_->IncRefOrCreateSaiHost(newEntry->getIntfID(), + auto host = hostTable_->createOrUpdateSaiHost(newEntry->getIntfID(), IPAddress(newEntry->getIP()), newEntry->getMac()); try { - host->Program(action); + 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_->DerefSaiHost(newEntry->getIntfID(), - IPAddress(newEntry->getIP()), - newEntry->getMac()); + hostTable_->removeSaiHost(newEntry->getIntfID(), + IPAddress(newEntry->getIP())); LOG(ERROR) << e.what(); return; } + }; - auto unrefOldEntry = [&]() { - VLOG(3) << "Deleting neighbor entry"; - auto host = hostTable_->DerefSaiHost(oldEntry->getIntfID(), - IPAddress(oldEntry->getIP()), - oldEntry->getMac()); - if (host) { - try { - host->Program(SAI_PACKET_ACTION_TRAP); - } catch (const SaiError &e) { - LOG(ERROR) << e.what(); - return; - } - } + auto removeOldEntry = [&]() { + hostTable_->removeSaiHost(oldEntry->getIntfID(), + IPAddress(oldEntry->getIP())); }; if (!oldEntry) { - refNewEntry(); + updateCreateNewEntry(); + } else if (!newEntry) { - unrefOldEntry(); + + removeOldEntry(); + } else if ((oldEntry->getIntfID() != newEntry->getIntfID()) || - (oldEntry->getIP() != newEntry->getIP()) || - (oldEntry->getMac() != newEntry->getMac())) { - refNewEntry(); - unrefOldEntry(); + (oldEntry->getIP() != newEntry->getIP())) { + + updateCreateNewEntry(); + removeOldEntry(); + + } else if (oldEntry->getMac() != newEntry->getMac()) { + + updateCreateNewEntry(); } } @@ -927,12 +933,14 @@ void SaiSwitch::ProcessAddedVlan(const shared_ptr &vlan) { saiStatus = pSaiVlanApi_->create_vlan(vlanId); if (saiStatus != SAI_STATUS_SUCCESS) { - throw SaiError("Failed to create VLAN ", vlanId); + LOG(ERROR) << "Failed to create VLAN " << vlanId; + return; } saiStatus = pSaiVlanApi_->add_ports_to_vlan(vlanId, portList.size(), portList.data()); if (saiStatus != SAI_STATUS_SUCCESS) { - throw SaiError("Failed to add ports to VLAN ", vlanId); + LOG(ERROR) << "Failed to add ports to VLAN " << vlanId; + return; } warmBootCache_->AddVlanInfo(vlan->getID(), portList); @@ -1061,8 +1069,8 @@ void SaiSwitch::OnPacketReceived(const void *buf, } catch (const std::exception &ex) { - LOG(ERROR) << __FUNCTION__ << " Could not allocate SaiRxPacket. Reason: " << - folly::exceptionStr(ex); + LOG(ERROR) << __FUNCTION__ << " Could not allocate SaiRxPacket. Reason: " + << folly::exceptionStr(ex); return; } @@ -1079,126 +1087,4 @@ void SaiSwitch::ReleaseLock() close(lockFd); } - -void SaiSwitch::AddHostsForDemo() { - VLOG(4) << "Entering " << __FUNCTION__; - - static bool added {false}; - - if (added) { - return; - } - - sai_fdb_api_t *pSaiFdbApi {nullptr}; - sai_api_query(SAI_API_FDB, (void **) &pSaiFdbApi); - - // iterate trough all L3 interfaces and create - // static FDB entries for neighbor hosts and the - // neighbor hosts themselves which will be used in demo - auto intfPtr = GetIntfTable()->GetFirstIntfIf(); - while (intfPtr != nullptr) { - - auto mac = intfPtr->GetInterface()->getMac(); - // host mac will be intf mac + 1 - const_cast(mac.bytes())[5] += 1; - - // Pick the same port number as interface ID. - auto egressPortId = PortID(intfPtr->GetInterface()->getID().t); - sai_object_id_t saiEgressPortId {SAI_NULL_OBJECT_ID}; - - try { - saiEgressPortId = GetPortTable()->GetSaiPortId(egressPortId); - } catch (const SaiError &e) { - LOG(ERROR) << e.what(); - intfPtr = GetIntfTable()->GetNextIntfIf(intfPtr); - continue; - } - - // Add static FDB entry for a host - sai_fdb_entry_t fdb_entry; - - memcpy(fdb_entry.mac_address, mac.bytes(), sizeof(fdb_entry.mac_address)); - fdb_entry.vlan_id = intfPtr->GetInterface()->getVlanID().t; - - // FDB attributes - std::vector attr_list; - sai_attribute_t attr; - - // entry type - attr.id = SAI_FDB_ENTRY_ATTR_TYPE; - attr.value.u32 = SAI_FDB_ENTRY_STATIC; - attr_list.push_back(attr); - - // port id - attr.id = SAI_FDB_ENTRY_ATTR_PORT_ID; - attr.value.oid = saiEgressPortId; - attr_list.push_back(attr); - - // packet action - attr.id = SAI_FDB_ENTRY_ATTR_PACKET_ACTION; - attr.value.oid = SAI_PACKET_ACTION_FORWARD; - attr_list.push_back(attr); - - // create fdb entry - sai_status_t saiRetVal = pSaiFdbApi->create_fdb_entry(&fdb_entry, attr_list.size(), attr_list.data()); - if (saiRetVal != SAI_STATUS_SUCCESS) { - LOG(ERROR) << "Could not create static fdb entry with VLAN: " << fdb_entry.vlan_id - << ", MAC: " << mac.toString() << ", port: " << egressPortId.t - << ". Error: " << saiRetVal; - - intfPtr = GetIntfTable()->GetNextIntfIf(intfPtr); - continue; - } - - VLOG(2) << "Created static fdb entry with VLAN: " << fdb_entry.vlan_id << ", MAC: " - << mac << ", port: " << egressPortId.t; - - // create hosts - for (const auto& addrPair : intfPtr->GetInterface()->getAddresses()) { - // host IP will be intf IP + 1 - auto addr = addrPair.first; - const_cast(addr.bytes())[3] += 1; - - // Compose ARP reply - uint32_t pktLen = 64; - auto pkt = allocatePacket(pktLen); - folly::io::RWPrivateCursor cursor(pkt->buf()); - - pkt->writeEthHeader(&cursor, intfPtr->GetInterface()->getMac(), - mac, ArpHandler::ETHERTYPE_ARP); - cursor.writeBE(ARP_HTYPE_ETHERNET); - cursor.writeBE(ARP_PTYPE_IPV4); - cursor.writeBE(ARP_HLEN_ETHERNET); - cursor.writeBE(ARP_PLEN_IPV4); - cursor.writeBE(ARP_OPER_REPLY); - // sender MAC/IP - cursor.push(mac.bytes(), MacAddress::SIZE); - cursor.write(addr.asV4().toLong()); - // target MAC/IP - cursor.push(intfPtr->GetInterface()->getMac().bytes(), MacAddress::SIZE); - cursor.write(addrPair.first.asV4().toLong()); - // Fill the padding with 0s - memset(cursor.writableData(), 0, cursor.length()); - - // Pkt attributes - std::vector pkt_attr_list; - sai_attribute_t pkt_attr; - - // entry type - pkt_attr.id = SAI_HOSTIF_PACKET_INGRESS_PORT; - pkt_attr.value.oid = saiEgressPortId; - pkt_attr_list.push_back(pkt_attr); - - // Simulate ARP replay received in order to install neighbour hosts - PacketRxCallback(pkt->buf()->data(), pktLen, pkt_attr_list.size(), pkt_attr_list.data()); - - VLOG(2) << "Created host with Intf: " << intfPtr->GetInterface()->getID().t - << ", IP: " << addr << ", MAC: " << mac; - } - intfPtr = GetIntfTable()->GetNextIntfIf(intfPtr); - } // while (intfPtr != nullptr) - - added = true; -} - }} // facebook::fboss diff --git a/fboss/agent/hw/sai/SaiSwitch.h b/fboss/agent/hw/sai/SaiSwitch.h index a7c6cffe110d4..55dadfcb0aa85 100644 --- a/fboss/agent/hw/sai/SaiSwitch.h +++ b/fboss/agent/hw/sai/SaiSwitch.h @@ -252,9 +252,6 @@ class SaiSwitch : public HwSwitch { sai_size_t buf_size, uint32_t attr_count, const sai_attribute_t *attr_list) noexcept; - - void AddHostsForDemo(); - enum { SAI_SWITCH_DEFAULT_PROFILE_ID = 0 }; From c3491304dd824adb4f5d95d80d964600cca481cc Mon Sep 17 00:00:00 2001 From: Vitaliy Senchyshyn Date: Mon, 11 Apr 2016 17:46:18 +0300 Subject: [PATCH 16/24] Fixed Vlan and Route update in SaiSwitch --- fboss/agent/hw/sai/SaiSwitch.cpp | 74 ++++++++++++++++---------------- 1 file changed, 38 insertions(+), 36 deletions(-) diff --git a/fboss/agent/hw/sai/SaiSwitch.cpp b/fboss/agent/hw/sai/SaiSwitch.cpp index b6172c3f9f773..cc0fbdcef9b37 100644 --- a/fboss/agent/hw/sai/SaiSwitch.cpp +++ b/fboss/agent/hw/sai/SaiSwitch.cpp @@ -686,7 +686,11 @@ void SaiSwitch::ProcessChangedRoute(const RouterID id, VLOG(2) << "Non-resolved route HW programming is skipped"; ProcessRemovedRoute(id, oldRoute); } else { - routeTable_->AddRoute(id, newRoute.get()); + try { + routeTable_->AddRoute(id, newRoute.get()); + } catch (const SaiError &e) { + LOG(ERROR) << e.what(); + } } } @@ -703,7 +707,11 @@ void SaiSwitch::ProcessAddedRoute(const RouterID id, return; } - routeTable_->AddRoute(id, route.get()); + try { + routeTable_->AddRoute(id, route.get()); + } catch (const SaiError &e) { + LOG(ERROR) << e.what(); + } } template @@ -718,7 +726,11 @@ void SaiSwitch::ProcessRemovedRoute(const RouterID id, return; } - routeTable_->DeleteRoute(id, route.get()); + try { + routeTable_->DeleteRoute(id, route.get()); + } catch (const SaiError &e) { + LOG(ERROR) << e.what(); + } } void SaiSwitch::ProcessRemovedRoutes(const StateDelta &delta) { @@ -783,54 +795,49 @@ void SaiSwitch::ProcessChangedVlan(const shared_ptr &oldVlan, std::vector removedPorts; const auto &oldPorts = oldVlan->getPorts(); const auto &newPorts = newVlan->getPorts(); - auto oldIter = oldPorts.begin(); - auto newIter = newPorts.begin(); - while ((oldIter != oldPorts.end()) && - (newIter != newPorts.end())) { - - if ((oldIter == oldPorts.end()) || - ((newIter != newPorts.end()) && (newIter->first < oldIter->first))) { + // Populate the list of removed ports + for (auto entry : oldPorts) { + if (newPorts.find(entry.first) == newPorts.end()) { sai_vlan_port_t vlanPort; try { - vlanPort.port_id = portTable_->GetSaiPortId(newIter->first); + vlanPort.port_id = portTable_->GetSaiPortId(entry.first); } catch (const SaiError &e) { LOG(ERROR) << e.what(); - ++newIter; continue; } - vlanPort.tagging_mode = newIter->second.tagged ? SAI_VLAN_PORT_TAGGED : - SAI_VLAN_PORT_UNTAGGED; - addedPorts.push_back(vlanPort); - ++newIter; - } else if (newIter == newPorts.end() || - (oldIter != oldPorts.end() && (oldIter->first < newIter->first))) { + vlanPort.tagging_mode = entry.second.tagged ? SAI_VLAN_PORT_TAGGED : + SAI_VLAN_PORT_UNTAGGED; + removedPorts.push_back(vlanPort); + } + } + + // Populate the list of added ports + for (auto entry : newPorts) { + if (oldPorts.find(entry.first) == oldPorts.end()) { sai_vlan_port_t vlanPort; try { - vlanPort.port_id = portTable_->GetSaiPortId(oldIter->first); + vlanPort.port_id = portTable_->GetSaiPortId(entry.first); } catch (const SaiError &e) { LOG(ERROR) << e.what(); - ++oldIter; continue; } - removedPorts.push_back(vlanPort); - - } else { - ++oldIter; - ++newIter; + vlanPort.tagging_mode = entry.second.tagged ? SAI_VLAN_PORT_TAGGED : + SAI_VLAN_PORT_UNTAGGED; + addedPorts.push_back(vlanPort); } } - VLOG(2) << "updating VLAN " << newVlan->getID() << ": " << addedPorts.size() - << " ports added, " << removedPorts.size() << " ports removed"; - if (removedPorts.size() > 0) { + + VLOG(2) << "updating VLAN " << newVlan->getID() << ": " << removedPorts.size() << " ports removed"; + saiStatus = pSaiVlanApi_->remove_ports_from_vlan(vlanId, removedPorts.size(), removedPorts.data()); if (saiStatus != SAI_STATUS_SUCCESS) { @@ -839,20 +846,15 @@ void SaiSwitch::ProcessChangedVlan(const shared_ptr &oldVlan, } if (addedPorts.size() > 0) { + + VLOG(2) << "updating VLAN " << newVlan->getID() << ": " << addedPorts.size() << " ports added"; + saiStatus = pSaiVlanApi_->add_ports_to_vlan(vlanId, addedPorts.size(), addedPorts.data()); - if (saiStatus != SAI_STATUS_SUCCESS) { LOG(ERROR) << "Failed to add ports to VLAN " << vlanId; } } - - if ((addedPorts.size() == 0) && - (removedPorts.size() == 0)) - { - // Nothing changed means that it's new VLAN - ProcessAddedVlan(newVlan); - } } void SaiSwitch::ProcessAddedVlan(const shared_ptr &vlan) { From 14a079fbc9095c6123b06dad7965477e1e744c20 Mon Sep 17 00:00:00 2001 From: Vitaliy Senchyshyn Date: Mon, 11 Apr 2016 19:06:30 +0300 Subject: [PATCH 17/24] Append trapped packet to 64 bytes. --- fboss/agent/hw/sai/SaiRxPacket.cpp | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/fboss/agent/hw/sai/SaiRxPacket.cpp b/fboss/agent/hw/sai/SaiRxPacket.cpp index eb988e212dde6..957436208ab2f 100644 --- a/fboss/agent/hw/sai/SaiRxPacket.cpp +++ b/fboss/agent/hw/sai/SaiRxPacket.cpp @@ -37,6 +37,8 @@ SaiRxPacket::SaiRxPacket(const void* buf, 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) { @@ -44,13 +46,15 @@ SaiRxPacket::SaiRxPacket(const void* buf, } } - len_ = buf_size; + // Append ingress packet to 64 bytes. + len_ = (buf_size < PKT_MIN_LEN) ? PKT_MIN_LEN : buf_size; - void *pkt_buf = new uint8_t[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 - buf_size, // uint32_t capacity + len_, // uint32_t capacity freeRxBufCallback, // Free Function freeFn NULL); // void* userData From f2f526ff7c37c1b1e26d964ec9f9a8271eb92123 Mon Sep 17 00:00:00 2001 From: Vitaliy Senchyshyn Date: Mon, 11 Apr 2016 19:11:53 +0300 Subject: [PATCH 18/24] Fixed issue with port admin mode enable/disable --- fboss/agent/hw/sai/SaiPortBase.cpp | 58 +++++++++++++++++++----- fboss/agent/hw/sai/SaiPortBase.h | 9 +++- fboss/agent/hw/sai/SaiPortTable.cpp | 2 +- fboss/agent/hw/sai/SaiSwitch.cpp | 70 +++++++++++++++-------------- fboss/agent/hw/sai/SaiSwitch.h | 5 ++- 5 files changed, 94 insertions(+), 50 deletions(-) diff --git a/fboss/agent/hw/sai/SaiPortBase.cpp b/fboss/agent/hw/sai/SaiPortBase.cpp index 53695c2674e9b..9be122b61535d 100644 --- a/fboss/agent/hw/sai/SaiPortBase.cpp +++ b/fboss/agent/hw/sai/SaiPortBase.cpp @@ -42,31 +42,23 @@ void SaiPortBase::Init(bool warmBoot) { LOG(ERROR) << e.what(); } - SetPortStatus(true); + disable(); initDone_ = true; } -void SaiPortBase::SetPortStatus(bool linkStatus) { +void SaiPortBase::setPortStatus(bool linkStatus) { VLOG(6) << "Entering " << __FUNCTION__; if (initDone_ && (linkStatus_ == linkStatus)) { return; } - sai_attribute_t attr {0}; - attr.id = SAI_PORT_ATTR_ADMIN_STATE; - attr.value.booldata = linkStatus; - - sai_status_t saiStatus = pSaiPortApi_->set_port_attribute(saiPortId_, &attr); - if(SAI_STATUS_SUCCESS != saiStatus) { - LOG(ERROR) << "failed to set port status: " << linkStatus << "error: " << saiStatus; - } - 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. - pPlatformPort_->linkStatusChanged(linkStatus, true); + pPlatformPort_->linkStatusChanged(linkStatus, adminMode_); + linkStatus_ = linkStatus; } void SaiPortBase::SetIngressVlan(VlanID vlan) { @@ -92,6 +84,48 @@ void SaiPortBase::SetIngressVlan(VlanID vlan) { 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 = pSaiPortApi_->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 = pSaiPortApi_->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__; diff --git a/fboss/agent/hw/sai/SaiPortBase.h b/fboss/agent/hw/sai/SaiPortBase.h index c7fae0d7d800a..25d98326633b1 100644 --- a/fboss/agent/hw/sai/SaiPortBase.h +++ b/fboss/agent/hw/sai/SaiPortBase.h @@ -65,10 +65,16 @@ class SaiPortBase { VlanID GetIngressVlan() { return pvId_; } + + bool isEnabled() { + return adminMode_; + } /* * Setters. */ - void SetPortStatus(bool linkStatus); + void enable(); + void disable(); + void setPortStatus(bool linkStatus); void SetIngressVlan(VlanID vlan); @@ -95,6 +101,7 @@ class SaiPortBase { sai_object_id_t saiPortId_ {0}; PortID fbossPortId_ {0}; VlanID pvId_ {0}; + bool adminMode_ {false}; bool linkStatus_ {true}; bool initDone_ {false}; diff --git a/fboss/agent/hw/sai/SaiPortTable.cpp b/fboss/agent/hw/sai/SaiPortTable.cpp index 23f6b0f505b2e..cdde364ef8dcb 100644 --- a/fboss/agent/hw/sai/SaiPortTable.cpp +++ b/fboss/agent/hw/sai/SaiPortTable.cpp @@ -87,7 +87,7 @@ void SaiPortTable::SetPortStatus(sai_object_id_t portId, int status) { VLOG(6) << "Entering " << __FUNCTION__; auto port = GetSaiPort(portId); - port->SetPortStatus(status); + port->setPortStatus(status); } void SaiPortTable::UpdatePortStats() { diff --git a/fboss/agent/hw/sai/SaiSwitch.cpp b/fboss/agent/hw/sai/SaiSwitch.cpp index cc0fbdcef9b37..997caeffa8f8b 100644 --- a/fboss/agent/hw/sai/SaiSwitch.cpp +++ b/fboss/agent/hw/sai/SaiSwitch.cpp @@ -319,17 +319,7 @@ void SaiSwitch::stateChanged(const StateDelta &delta) { 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. - forEachChanged(delta.getPortsDelta(), - [&] (const shared_ptr &oldPort, const shared_ptr &newPort) { - if (oldPort->getState() == newPort->getState()) { - return; - } - - if (newPort->getState() == cfg::PortState::DOWN || - newPort->getState() == cfg::PortState::POWER_DOWN) { - ChangePortState(oldPort, newPort); - } - }); + processDisabledPorts(delta); // remove all routes to be deleted ProcessRemovedRoutes(delta); @@ -379,17 +369,8 @@ void SaiSwitch::stateChanged(const StateDelta &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. - forEachChanged(delta.getPortsDelta(), - [&] (const shared_ptr &oldPort, const shared_ptr &newPort) { - if (oldPort->getState() == newPort->getState()) { - return; - } + processEnabledPorts(delta); - if (newPort->getState() != cfg::PortState::DOWN && - newPort->getState() != cfg::PortState::POWER_DOWN) { - ChangePortState(oldPort, newPort); - } - }); } catch(SaiError &e) { LOG(ERROR) << e.what(); } catch(SaiFatal &e) { @@ -463,19 +444,6 @@ bool SaiSwitch::sendPacketOutOfPort(std::unique_ptr pkt, PortID portID return true; } -void SaiSwitch::ChangePortState(const shared_ptr &oldPort, - const shared_ptr &newPort) { - VLOG(4) << "Entering " << __FUNCTION__; - - try { - uint16_t o = portTable_->GetSaiPortId(oldPort->getID()); - uint16_t n = portTable_->GetSaiPortId(newPort->getID()); - VLOG(3) << "changePortState(" << o << ", " << n << ")"; - } catch (SaiError &e) { - LOG(ERROR) << e.what(); - } -} - void SaiSwitch::UpdateIngressVlan(const std::shared_ptr &oldPort, const std::shared_ptr &newPort) { VLOG(4) << "Entering " << __FUNCTION__; @@ -492,6 +460,40 @@ void SaiSwitch::UpdatePortSpeed(const std::shared_ptr &oldPort, 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__; diff --git a/fboss/agent/hw/sai/SaiSwitch.h b/fboss/agent/hw/sai/SaiSwitch.h index 55dadfcb0aa85..75b552f954d23 100644 --- a/fboss/agent/hw/sai/SaiSwitch.h +++ b/fboss/agent/hw/sai/SaiSwitch.h @@ -234,8 +234,9 @@ class SaiSwitch : public HwSwitch { void UpdatePortSpeed(const std::shared_ptr &oldPort, const std::shared_ptr &newPort); - void ChangePortState(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. From c113044636f6dca3e76cddc67ac5f57296d97f6f Mon Sep 17 00:00:00 2001 From: Vitaliy Senchyshyn Date: Mon, 11 Apr 2016 20:56:59 +0300 Subject: [PATCH 19/24] Updated function header for removeSaiHost --- fboss/agent/hw/sai/SaiHostTable.h | 5 ----- 1 file changed, 5 deletions(-) diff --git a/fboss/agent/hw/sai/SaiHostTable.h b/fboss/agent/hw/sai/SaiHostTable.h index 07dbaad7f887a..15a4677e13a0f 100644 --- a/fboss/agent/hw/sai/SaiHostTable.h +++ b/fboss/agent/hw/sai/SaiHostTable.h @@ -40,11 +40,6 @@ class SaiHostTable { /** * Remove an existing SaiHost entry - * - * @return nullptr, if the SaiHost entry is deleted - * @retrun the SaiHost object that has reference counter - * decreased by 1, but the object is still valid as it is - * still referred in somewhere else */ void removeSaiHost(InterfaceID intf, const folly::IPAddress &ip) noexcept; From 544a123761e2658d138ee4b2d8e63aeb2f3ce341 Mon Sep 17 00:00:00 2001 From: Vitaliy Senchyshyn Date: Thu, 14 Apr 2016 17:24:23 +0300 Subject: [PATCH 20/24] Convert some for loops to C++11 style nad use more appropriate variable names --- fboss/agent/hw/sai/SaiNextHop.cpp | 24 ++++++++++++------------ fboss/agent/hw/sai/SaiNextHopTable.cpp | 4 ++-- fboss/agent/hw/sai/SaiSwitch.cpp | 26 +++++++++++++------------- 3 files changed, 27 insertions(+), 27 deletions(-) diff --git a/fboss/agent/hw/sai/SaiNextHop.cpp b/fboss/agent/hw/sai/SaiNextHop.cpp index 4612a6a2ccb6d..d710c35b1099f 100644 --- a/fboss/agent/hw/sai/SaiNextHop.cpp +++ b/fboss/agent/hw/sai/SaiNextHop.cpp @@ -46,20 +46,20 @@ SaiNextHop::~SaiNextHop() { sai_status_t saiRetVal = SAI_STATUS_FAILURE; // Remove all next hops and group(if it was created) from the HW. - for (auto iter : hwNextHops_) { + for (auto& nhPair : hwNextHops_) { if (nhGroupId_ != SAI_NULL_OBJECT_ID) { - saiRetVal = pSaiNextHopGroupApi_->remove_next_hop_from_group(nhGroupId_, 1, &iter.first); + saiRetVal = pSaiNextHopGroupApi_->remove_next_hop_from_group(nhGroupId_, 1, &nhPair.first); if (saiRetVal != SAI_STATUS_SUCCESS) { - LOG(ERROR) << "Could not remove next hop " << iter.second.str() << " from next hop group: " + LOG(ERROR) << "Could not remove next hop " << nhPair.second.str() << " from next hop group: " << nhGroupId_ << " of next hops: " << fwdInfo_.str() << "Error: " << saiRetVal; } } - saiRetVal = pSaiNextHopApi_->remove_next_hop(iter.first); + saiRetVal = pSaiNextHopApi_->remove_next_hop(nhPair.first); if (saiRetVal != SAI_STATUS_SUCCESS) { - LOG(ERROR) << "Could not remove next hop " << iter.second.str() + LOG(ERROR) << "Could not remove next hop " << nhPair.second.str() << " from HW. Error: " << saiRetVal; } } @@ -154,12 +154,12 @@ void SaiNextHop::Program() { std::vector nextHopIds; // Create all next hops and fill the hwNextHops_ map and nextHopIds array - for (auto iter : resolvedNextHops_) { + for (auto& nhPair : resolvedNextHops_) { - sai_object_id_t nhId = programNh(iter.intf, iter.nexthop); + sai_object_id_t nhId = programNh(nhPair.intf, nhPair.nexthop); if (nhId != SAI_NULL_OBJECT_ID) { - hwNextHops_.emplace(nhId, iter); + hwNextHops_.emplace(nhId, nhPair); nextHopIds.push_back(nhId); } } @@ -174,15 +174,15 @@ void SaiNextHop::Program() { sai_object_id_t nhGroupId = programNhGroup(nextHopIds.size(), nextHopIds.data()); if (nhGroupId == SAI_NULL_OBJECT_ID) { // Cleanup hosts - for (auto iter : hwNextHops_) { + for (auto& nhPair : hwNextHops_) { - saiRetVal = pSaiNextHopApi_->remove_next_hop(iter.first); + saiRetVal = pSaiNextHopApi_->remove_next_hop(nhPair.first); if (saiRetVal != SAI_STATUS_SUCCESS) { - LOG(ERROR) << "Could not remove next hop " << iter.second.str() + LOG(ERROR) << "Could not remove next hop " << nhPair.second.str() << " from HW. Error: " << saiRetVal; } - unresolvedNextHops_.emplace(iter.second); + unresolvedNextHops_.emplace(nhPair.second); } resolvedNextHops_.clear(); diff --git a/fboss/agent/hw/sai/SaiNextHopTable.cpp b/fboss/agent/hw/sai/SaiNextHopTable.cpp index b9b987df72d7b..43edcee35a70d 100644 --- a/fboss/agent/hw/sai/SaiNextHopTable.cpp +++ b/fboss/agent/hw/sai/SaiNextHopTable.cpp @@ -83,9 +83,9 @@ SaiNextHop* SaiNextHopTable::DerefSaiNextHop(const RouteForwardInfo &fwdInfo) no void SaiNextHopTable::onResolved(InterfaceID intf, const folly::IPAddress &ip) { - for (auto iter = nextHops_.begin(); iter != nextHops_.end(); ++iter) { + for (auto& entry : nextHops_) { try { - iter->second.first->onResolved(intf, ip); + entry.second.first->onResolved(intf, ip); } catch (const SaiError &e) { LOG(ERROR) << e.what(); } diff --git a/fboss/agent/hw/sai/SaiSwitch.cpp b/fboss/agent/hw/sai/SaiSwitch.cpp index 997caeffa8f8b..4dec48bed0ae7 100644 --- a/fboss/agent/hw/sai/SaiSwitch.cpp +++ b/fboss/agent/hw/sai/SaiSwitch.cpp @@ -545,9 +545,9 @@ void SaiSwitch::updateStats(SwitchStats *switchStats) { // Update thread-local per-port statistics. PortStatsMap *portStatsMap = switchStats->getPortStats(); - BOOST_FOREACH(auto& it, *portStatsMap) { - PortID portId = it.first; - PortStats *portStats = it.second.get(); + for (auto& entry : *portStatsMap) { + PortID portId = entry.first; + PortStats *portStats = entry.second.get(); UpdatePortStats(portId, portStats); } @@ -799,39 +799,39 @@ void SaiSwitch::ProcessChangedVlan(const shared_ptr &oldVlan, const auto &newPorts = newVlan->getPorts(); // Populate the list of removed ports - for (auto entry : oldPorts) { - if (newPorts.find(entry.first) == newPorts.end()) { + for (auto& oldPortPair : oldPorts) { + if (newPorts.find(oldPortPair.first) == newPorts.end()) { sai_vlan_port_t vlanPort; try { - vlanPort.port_id = portTable_->GetSaiPortId(entry.first); + vlanPort.port_id = portTable_->GetSaiPortId(oldPortPair.first); } catch (const SaiError &e) { LOG(ERROR) << e.what(); continue; } - vlanPort.tagging_mode = entry.second.tagged ? SAI_VLAN_PORT_TAGGED : - SAI_VLAN_PORT_UNTAGGED; + 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 entry : newPorts) { - if (oldPorts.find(entry.first) == oldPorts.end()) { + for (auto& newPortPair : newPorts) { + if (oldPorts.find(newPortPair.first) == oldPorts.end()) { sai_vlan_port_t vlanPort; try { - vlanPort.port_id = portTable_->GetSaiPortId(entry.first); + vlanPort.port_id = portTable_->GetSaiPortId(newPortPair.first); } catch (const SaiError &e) { LOG(ERROR) << e.what(); continue; } - vlanPort.tagging_mode = entry.second.tagged ? SAI_VLAN_PORT_TAGGED : - SAI_VLAN_PORT_UNTAGGED; + vlanPort.tagging_mode = newPortPair.second.tagged ? SAI_VLAN_PORT_TAGGED : + SAI_VLAN_PORT_UNTAGGED; addedPorts.push_back(vlanPort); } } From 2eee581fabc2b415809d0f2291197a2116161b0f Mon Sep 17 00:00:00 2001 From: Vitaliy Senchyshyn Date: Thu, 14 Apr 2016 17:27:23 +0300 Subject: [PATCH 21/24] Removed hungarian notation sprinklings --- fboss/agent/hw/sai/SaiHost.cpp | 10 ++--- fboss/agent/hw/sai/SaiHost.h | 2 +- fboss/agent/hw/sai/SaiIntf.cpp | 10 ++--- fboss/agent/hw/sai/SaiIntf.h | 2 +- fboss/agent/hw/sai/SaiNextHop.cpp | 24 +++++----- fboss/agent/hw/sai/SaiNextHop.h | 4 +- fboss/agent/hw/sai/SaiPortBase.cpp | 18 ++++---- fboss/agent/hw/sai/SaiPortBase.h | 21 ++++----- fboss/agent/hw/sai/SaiPortTable.cpp | 8 ++-- fboss/agent/hw/sai/SaiPortTable.h | 4 +- fboss/agent/hw/sai/SaiRoute.cpp | 12 ++--- fboss/agent/hw/sai/SaiRoute.h | 2 +- fboss/agent/hw/sai/SaiSwitch.cpp | 70 ++++++++++++++--------------- fboss/agent/hw/sai/SaiSwitch.h | 46 +++++++++---------- fboss/agent/hw/sai/SaiVrf.cpp | 6 +-- fboss/agent/hw/sai/SaiVrf.h | 2 +- 16 files changed, 118 insertions(+), 123 deletions(-) diff --git a/fboss/agent/hw/sai/SaiHost.cpp b/fboss/agent/hw/sai/SaiHost.cpp index b36549d7440f2..76d0cc45836d0 100644 --- a/fboss/agent/hw/sai/SaiHost.cpp +++ b/fboss/agent/hw/sai/SaiHost.cpp @@ -30,7 +30,7 @@ SaiHost::SaiHost(const SaiSwitch *hw, InterfaceID intf, VLOG(4) << "Entering " << __FUNCTION__; - pSaiNeighborApi_ = hw_->GetSaiNeighborApi(); + saiNeighborApi_ = hw_->GetSaiNeighborApi(); } SaiHost::~SaiHost() { @@ -40,7 +40,7 @@ SaiHost::~SaiHost() { return; } - pSaiNeighborApi_->remove_neighbor_entry(&neighborEntry_); + saiNeighborApi_->remove_neighbor_entry(&neighborEntry_); VLOG(3) << "Deleted L3 host object for " << ip_; } @@ -85,7 +85,7 @@ void SaiHost::Program(sai_packet_action_t action, const folly::MacAddress &mac) attrList.push_back(attr); // create neighbor - sai_status_t saiRetVal = pSaiNeighborApi_->create_neighbor_entry(&neighborEntry_, + sai_status_t saiRetVal = saiNeighborApi_->create_neighbor_entry(&neighborEntry_, attrList.size(), attrList.data()); if (saiRetVal != SAI_STATUS_SUCCESS) { @@ -107,7 +107,7 @@ void SaiHost::Program(sai_packet_action_t action, const folly::MacAddress &mac) memcpy(macAttr.value.mac, mac.bytes(), sizeof(macAttr.value.mac)); // set action attribute - sai_status_t saiRetVal = pSaiNeighborApi_->set_neighbor_attribute(&neighborEntry_, + sai_status_t saiRetVal = saiNeighborApi_->set_neighbor_attribute(&neighborEntry_, &macAttr); if (saiRetVal != SAI_STATUS_SUCCESS) { throw SaiError("Could not set MAC: ", mac, @@ -127,7 +127,7 @@ void SaiHost::Program(sai_packet_action_t action, const folly::MacAddress &mac) actionAttr.value.u32 = action; // set action attribute - sai_status_t saiRetVal = pSaiNeighborApi_->set_neighbor_attribute(&neighborEntry_, + sai_status_t saiRetVal = saiNeighborApi_->set_neighbor_attribute(&neighborEntry_, &actionAttr); if (saiRetVal != SAI_STATUS_SUCCESS) { throw SaiError("Could not set packet action attribute: ", action, diff --git a/fboss/agent/hw/sai/SaiHost.h b/fboss/agent/hw/sai/SaiHost.h index 379b055f5c899..bb7d78b96d75f 100644 --- a/fboss/agent/hw/sai/SaiHost.h +++ b/fboss/agent/hw/sai/SaiHost.h @@ -77,7 +77,7 @@ class SaiHost { bool added_ {false}; // if added to the HW host table or not sai_neighbor_entry_t neighborEntry_; - sai_neighbor_api_t *pSaiNeighborApi_ { nullptr }; + sai_neighbor_api_t *saiNeighborApi_ { nullptr }; }; }} // facebook::fboss diff --git a/fboss/agent/hw/sai/SaiIntf.cpp b/fboss/agent/hw/sai/SaiIntf.cpp index 83e2ca19c3f63..90b71029865aa 100644 --- a/fboss/agent/hw/sai/SaiIntf.cpp +++ b/fboss/agent/hw/sai/SaiIntf.cpp @@ -28,7 +28,7 @@ SaiIntf::SaiIntf(const SaiSwitch *hw) : hw_(hw) { VLOG(4) << "Entering " << __FUNCTION__; - pSaiRouterInterfaceApi_ = hw->GetSaiRouterIntfApi(); + saiRouterInterfaceApi_ = hw->GetSaiRouterIntfApi(); } SaiIntf::~SaiIntf() { @@ -43,7 +43,7 @@ SaiIntf::~SaiIntf() { iter = hosts_.begin(); } - pSaiRouterInterfaceApi_->remove_router_interface(saiIfId_); + saiRouterInterfaceApi_->remove_router_interface(saiIfId_); } hw_->WritableVrfTable()->DerefSaiVrf(intf_->getRouterID()); @@ -114,7 +114,7 @@ void SaiIntf::Program(const shared_ptr &intf) { attr_count++; sai_object_id_t rif_id = 0; - sai_status_t saiRetVal = pSaiRouterInterfaceApi_->create_router_interface(&rif_id, attr_count, attr_list); + 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); @@ -128,7 +128,7 @@ void SaiIntf::Program(const shared_ptr &intf) { 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 = pSaiRouterInterfaceApi_->set_router_interface_attribute(saiIfId_, &attr); + 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); @@ -139,7 +139,7 @@ void SaiIntf::Program(const shared_ptr &intf) { attr.id = SAI_ROUTER_INTERFACE_ATTR_MTU; //TODO verify what MTU should be set here attr.value.u32 = 1500; //ifParams.l3a_mtu = 1500; - saiRetVal = pSaiRouterInterfaceApi_->set_router_interface_attribute(rif_id, &attr); + saiRetVal = saiRouterInterfaceApi_->set_router_interface_attribute(rif_id, &attr); if (saiRetVal != SAI_STATUS_SUCCESS) { throw SaiError("SAI create_router_interface() failed : retVal ", saiRetVal); } diff --git a/fboss/agent/hw/sai/SaiIntf.h b/fboss/agent/hw/sai/SaiIntf.h index 9dbdac7a1e00f..642ccca9b78ce 100644 --- a/fboss/agent/hw/sai/SaiIntf.h +++ b/fboss/agent/hw/sai/SaiIntf.h @@ -57,7 +57,7 @@ class SaiIntf { // The interface addresses that have SaiHost object created std::set hosts_; - sai_router_interface_api_t *pSaiRouterInterfaceApi_ { nullptr }; + sai_router_interface_api_t *saiRouterInterfaceApi_ { nullptr }; // TODO: we now generate one station entry per interface, even if all diff --git a/fboss/agent/hw/sai/SaiNextHop.cpp b/fboss/agent/hw/sai/SaiNextHop.cpp index d710c35b1099f..c135c96613354 100644 --- a/fboss/agent/hw/sai/SaiNextHop.cpp +++ b/fboss/agent/hw/sai/SaiNextHop.cpp @@ -36,8 +36,8 @@ SaiNextHop::SaiNextHop(const SaiSwitch *hw, const RouteForwardInfo &fwdInfo) isGroup_ = (fwdInfo_.getNexthops().size() > 1); - pSaiNextHopApi_ = hw->GetSaiNextHopApi(); - pSaiNextHopGroupApi_ = hw->GetSaiNextHopGroupApi(); + saiNextHopApi_ = hw->GetSaiNextHopApi(); + saiNextHopGroupApi_ = hw->GetSaiNextHopGroupApi(); } SaiNextHop::~SaiNextHop() { @@ -50,14 +50,14 @@ SaiNextHop::~SaiNextHop() { if (nhGroupId_ != SAI_NULL_OBJECT_ID) { - saiRetVal = pSaiNextHopGroupApi_->remove_next_hop_from_group(nhGroupId_, 1, &nhPair.first); + 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 = pSaiNextHopApi_->remove_next_hop(nhPair.first); + 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; @@ -65,7 +65,7 @@ SaiNextHop::~SaiNextHop() { } if (nhGroupId_ != SAI_NULL_OBJECT_ID) { - saiRetVal = pSaiNextHopGroupApi_->remove_next_hop_group(nhGroupId_); + 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; @@ -111,10 +111,10 @@ void SaiNextHop::onResolved(InterfaceID intf, const folly::IPAddress &ip) { if (isGroup_) { if (nhGroupId_ != SAI_NULL_OBJECT_ID) { // NH Group already exists, so just add NH to it - saiRetVal = pSaiNextHopGroupApi_->add_next_hop_to_group(nhGroupId_, 1, &nhId); + 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; - pSaiNextHopApi_->remove_next_hop(nhId); + saiNextHopApi_->remove_next_hop(nhId); return; } @@ -122,7 +122,7 @@ void SaiNextHop::onResolved(InterfaceID intf, const folly::IPAddress &ip) { // 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) { - pSaiNextHopApi_->remove_next_hop(nhId); + saiNextHopApi_->remove_next_hop(nhId); return; } } @@ -176,7 +176,7 @@ void SaiNextHop::Program() { // Cleanup hosts for (auto& nhPair : hwNextHops_) { - saiRetVal = pSaiNextHopApi_->remove_next_hop(nhPair.first); + 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; @@ -225,7 +225,7 @@ sai_object_id_t SaiNextHop::programNh(InterfaceID intf, const folly::IPAddress & attrList.push_back(attr); // Create NH in SAI - saiRetVal = pSaiNextHopApi_->create_next_hop(&nhId, attrList.size(), attrList.data()); + 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; } @@ -258,7 +258,7 @@ sai_object_id_t SaiNextHop::programNhGroup(uint32_t nhCount, sai_object_id_t *nh groupAttrList.push_back(attr); // Create NH group in SAI - saiRetVal = pSaiNextHopGroupApi_->create_next_hop_group(&nhGroupId, groupAttrList.size(), groupAttrList.data()); + 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; @@ -268,7 +268,7 @@ sai_object_id_t SaiNextHop::programNhGroup(uint32_t nhCount, sai_object_id_t *nh } else { // Add NH to group in SAI - saiRetVal = pSaiNextHopGroupApi_->add_next_hop_to_group(nhGroupId_, nhCount, nhIds); + 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; diff --git a/fboss/agent/hw/sai/SaiNextHop.h b/fboss/agent/hw/sai/SaiNextHop.h index 5abd62283c791..3e91906cde201 100644 --- a/fboss/agent/hw/sai/SaiNextHop.h +++ b/fboss/agent/hw/sai/SaiNextHop.h @@ -110,8 +110,8 @@ class SaiNextHop { std::map hwNextHops_; sai_object_id_t nhGroupId_ {SAI_NULL_OBJECT_ID}; - sai_next_hop_api_t *pSaiNextHopApi_ {nullptr}; - sai_next_hop_group_api_t *pSaiNextHopGroupApi_ {nullptr}; + sai_next_hop_api_t *saiNextHopApi_ {nullptr}; + sai_next_hop_group_api_t *saiNextHopGroupApi_ {nullptr}; }; }} // facebook::fboss diff --git a/fboss/agent/hw/sai/SaiPortBase.cpp b/fboss/agent/hw/sai/SaiPortBase.cpp index 9be122b61535d..aacda89845567 100644 --- a/fboss/agent/hw/sai/SaiPortBase.cpp +++ b/fboss/agent/hw/sai/SaiPortBase.cpp @@ -19,14 +19,14 @@ extern "C" { namespace facebook { namespace fboss { -SaiPortBase::SaiPortBase(SaiSwitch *pSwitch, sai_object_id_t saiPortId, PortID fbossPortId, SaiPlatformPort *pPlatformPort) - : pHw_(pSwitch), - pPlatformPort_(pPlatformPort), +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 **) &pSaiPortApi_); + sai_api_query(SAI_API_PORT, (void **) &saiPortApi_); } SaiPortBase::~SaiPortBase() { @@ -57,7 +57,7 @@ void SaiPortBase::setPortStatus(bool linkStatus) { 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. - pPlatformPort_->linkStatusChanged(linkStatus, adminMode_); + platformPort_->linkStatusChanged(linkStatus, adminMode_); linkStatus_ = linkStatus; } @@ -74,7 +74,7 @@ void SaiPortBase::SetIngressVlan(VlanID vlan) { attr.id = SAI_PORT_ATTR_PORT_VLAN_ID; attr.value.u16 = vlan.t; - saiStatus = pSaiPortApi_->set_port_attribute(saiPortId_, &attr); + saiStatus = saiPortApi_->set_port_attribute(saiPortId_, &attr); if(SAI_STATUS_SUCCESS != saiStatus) { throw SaiError("Failed to update ingress VLAN for port ", fbossPortId_.t); } @@ -94,7 +94,7 @@ void SaiPortBase::enable() { attr.id = SAI_PORT_ATTR_ADMIN_STATE; attr.value.booldata = true; - sai_status_t saiStatus = pSaiPortApi_->set_port_attribute(saiPortId_, &attr); + 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; } @@ -115,7 +115,7 @@ void SaiPortBase::disable() { attr.id = SAI_PORT_ATTR_ADMIN_STATE; attr.value.booldata = false; - sai_status_t saiStatus = pSaiPortApi_->set_port_attribute(saiPortId_, &attr); + 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; } @@ -129,7 +129,7 @@ void SaiPortBase::disable() { std::string SaiPortBase::StatName(folly::StringPiece name) const { VLOG(6) << "Entering " << __FUNCTION__; - return folly::to("port", pPlatformPort_->getPortID(), ".", name); + return folly::to("port", platformPort_->getPortID(), ".", name); } void SaiPortBase::UpdateStats() { diff --git a/fboss/agent/hw/sai/SaiPortBase.h b/fboss/agent/hw/sai/SaiPortBase.h index 25d98326633b1..0025ab95a782c 100644 --- a/fboss/agent/hw/sai/SaiPortBase.h +++ b/fboss/agent/hw/sai/SaiPortBase.h @@ -29,12 +29,12 @@ class SaiPortBase { public: /** * @brief Constructor. All initialization steps should be done through init - * @param pSwitch, pointer to SaiSwitch object + * @param hw, pointer to SaiSwitch object * @param saiPortId, sai_port_id_t port ID * @param fbossPortId, fboss port ID - * @param pPlatform, pointer to SaiPlatformPort object + * @param platformPort, pointer to SaiPlatformPort object */ - SaiPortBase(SaiSwitch *pSwitch, sai_object_id_t saiPortId, PortID fbossPortId, SaiPlatformPort *pPlatformPort); + SaiPortBase(SaiSwitch *hw, sai_object_id_t saiPortId, PortID fbossPortId, SaiPlatformPort *platformPort); virtual ~SaiPortBase(); /** @@ -47,11 +47,11 @@ class SaiPortBase { * Getters. */ SaiPlatformPort *GetPlatformPort() const { - return pPlatformPort_; + return platformPort_; } SaiSwitch *GetHwSwitch() const { - return pHw_; + return hw_; } sai_object_id_t GetSaiPortId() const { @@ -90,13 +90,8 @@ class SaiPortBase { std::string StatName(folly::StringPiece name) const; - SaiSwitch *const pHw_ { - nullptr - }; // Pointer to HW Switch - - SaiPlatformPort *const pPlatformPort_ { - nullptr - }; // Pointer to Platform port + 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}; @@ -105,7 +100,7 @@ class SaiPortBase { bool linkStatus_ {true}; bool initDone_ {false}; - sai_port_api_t *pSaiPortApi_ { nullptr }; + sai_port_api_t *saiPortApi_ { nullptr }; private: class MonotonicCounter : public stats::MonotonicCounter { diff --git a/fboss/agent/hw/sai/SaiPortTable.cpp b/fboss/agent/hw/sai/SaiPortTable.cpp index cdde364ef8dcb..144352053b6a3 100644 --- a/fboss/agent/hw/sai/SaiPortTable.cpp +++ b/fboss/agent/hw/sai/SaiPortTable.cpp @@ -21,8 +21,8 @@ using std::make_pair; namespace facebook { namespace fboss { -SaiPortTable::SaiPortTable(SaiSwitch *pSwitch) - :pHw_(pSwitch) { +SaiPortTable::SaiPortTable(SaiSwitch *hw) + :hw_(hw) { VLOG(4) << "Entering " << __FUNCTION__; } @@ -33,14 +33,14 @@ SaiPortTable::~SaiPortTable() { void SaiPortTable::InitPorts(bool warmBoot) { VLOG(4) << "Entering " << __FUNCTION__; - auto platformPorts = pHw_->GetPlatform()->initPorts(); + 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(pHw_, saiPortId, fbossPortID, platformPort); + auto saiPortBase = make_unique(hw_, saiPortId, fbossPortID, platformPort); platformPort->SetPort(saiPortBase.get()); saiPortBase->Init(warmBoot); diff --git a/fboss/agent/hw/sai/SaiPortTable.h b/fboss/agent/hw/sai/SaiPortTable.h index 430ec0ef14834..b549103ad6e30 100644 --- a/fboss/agent/hw/sai/SaiPortTable.h +++ b/fboss/agent/hw/sai/SaiPortTable.h @@ -25,7 +25,7 @@ class SaiSwitch; class SaiPortTable { public: - explicit SaiPortTable(SaiSwitch *pSwitch); + explicit SaiPortTable(SaiSwitch *hw); virtual ~SaiPortTable(); /* @@ -60,7 +60,7 @@ class SaiPortTable { typedef boost::container::flat_map> SaiPortMap; typedef boost::container::flat_map FbossPortMap; - SaiSwitch *pHw_ {nullptr}; + SaiSwitch *hw_ {nullptr}; // Mappings for the physical ports. // The set of physical ports is defined in initPorts(), and is read-only diff --git a/fboss/agent/hw/sai/SaiRoute.cpp b/fboss/agent/hw/sai/SaiRoute.cpp index f18750fcd5ea6..edb9e5d50ecf7 100644 --- a/fboss/agent/hw/sai/SaiRoute.cpp +++ b/fboss/agent/hw/sai/SaiRoute.cpp @@ -44,7 +44,7 @@ SaiRoute::SaiRoute(const SaiSwitch *hw, intfPtr = hw_->GetIntfTable()->GetNextIntfIf(intfPtr); } - pSaiRouteApi_ = hw->GetSaiRouteApi(); + saiRouteApi_ = hw->GetSaiRouteApi(); } SaiRoute::~SaiRoute() { @@ -54,7 +54,7 @@ SaiRoute::~SaiRoute() { return; } - sai_status_t saiRetVal = pSaiRouteApi_->remove_route(&routeEntry_); + 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; @@ -165,7 +165,7 @@ void SaiRoute::Program(const RouteForwardInfo &fwd) { } // create route - sai_status_t saiRetVal = pSaiRouteApi_->create_route(&routeEntry_, 0, NULL); + 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_, @@ -202,7 +202,7 @@ void SaiRoute::Program(const RouteForwardInfo &fwd) { } // Set packet action route attribute in SAI - sai_status_t saiRetVal = pSaiRouteApi_->set_route_attribute(&routeEntry_, &routeAttr); + 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_, @@ -216,7 +216,7 @@ void SaiRoute::Program(const RouteForwardInfo &fwd) { if (routeAttr.value.oid != SAI_NULL_OBJECT_ID) { // Set next hop ID route attribute in SAI - saiRetVal = pSaiRouteApi_->set_route_attribute(&routeEntry_, &routeAttr); + 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_, @@ -251,7 +251,7 @@ void SaiRoute::onResolved(InterfaceID intf, const folly::IPAddress &ip) { if (routeAttr.value.oid != SAI_NULL_OBJECT_ID) { // Set next hop ID route attribute in SAI - saiRetVal = pSaiRouteApi_->set_route_attribute(&routeEntry_, &routeAttr); + 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; diff --git a/fboss/agent/hw/sai/SaiRoute.h b/fboss/agent/hw/sai/SaiRoute.h index 146d5e08ec9ab..7a96ab5c26cc0 100644 --- a/fboss/agent/hw/sai/SaiRoute.h +++ b/fboss/agent/hw/sai/SaiRoute.h @@ -87,7 +87,7 @@ class SaiRoute { bool added_ {false}; bool isLocal_ {false}; - sai_route_api_t *pSaiRouteApi_ { nullptr }; + sai_route_api_t *saiRouteApi_ { nullptr }; }; }} // facebook::fboss diff --git a/fboss/agent/hw/sai/SaiSwitch.cpp b/fboss/agent/hw/sai/SaiSwitch.cpp index 4dec48bed0ae7..af34bb56c09c3 100644 --- a/fboss/agent/hw/sai/SaiSwitch.cpp +++ b/fboss/agent/hw/sai/SaiSwitch.cpp @@ -105,17 +105,17 @@ SaiSwitch::SaiSwitch(SaiPlatformBase *platform) sai_api_initialize(0, &service_); - sai_api_query(SAI_API_SWITCH, (void **) &pSaiSwitchApi_); - sai_api_query(SAI_API_VLAN, (void **) &pSaiVlanApi_); - //sai_api_query(SAI_API_PORT, (void **) &pSaiPortApi_); - sai_api_query(SAI_API_ROUTER_INTERFACE, (void **) &pSaiRouterIntfApi_); - sai_api_query(SAI_API_ROUTE, (void **) &pSaiRouteApi_); - //sai_api_query(SAI_API_ACL, (void **) &pSaiAclApi_); - sai_api_query(SAI_API_VIRTUAL_ROUTER, (void **) &pSaiVrfApi_); - sai_api_query(SAI_API_NEIGHBOR, (void **) &pSaiNeighborApi_); - sai_api_query(SAI_API_NEXT_HOP, (void **) &pSaiNextHopApi_); - sai_api_query(SAI_API_NEXT_HOP_GROUP, (void **) &pSaiNextHopGroupApi_); - sai_api_query(SAI_API_HOST_INTERFACE, (void **) &pSaiHostIntfApi_); + 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; @@ -124,17 +124,17 @@ SaiSwitch::SaiSwitch(SaiPlatformBase *platform) SaiSwitch::~SaiSwitch() { VLOG(4) << "Entering " << __FUNCTION__; - pSaiNextHopApi_ = nullptr; - pSaiAclApi_ = nullptr; - pSaiHostInterfaceApi_ = nullptr; - pSaiNeighborApi_ = nullptr; - pSaiRouterIntfApi_ = nullptr; - pSaiRouteApi_ = nullptr; - pSaiVrfApi_ = nullptr; - pSaiPortApi_ = nullptr; - pSaiVlanApi_ = nullptr; - pSaiSwitchApi_ = nullptr; - pSaiHostIntfApi_ = nullptr; + 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(); @@ -211,7 +211,7 @@ SaiSwitch::init(HwSwitch::Callback *callback) { sai_switch_notification_t swNotif = {NULL, NULL, NULL, NULL, NULL, NULL}; swNotif.on_packet_event = PacketRxCallback; - sai_status_t saiStatus = pSaiSwitchApi_->initialize_switch(profileId, + sai_status_t saiStatus = saiSwitchApi_->initialize_switch(profileId, const_cast(hwId_.c_str()), NULL, &swNotif); if(SAI_STATUS_SUCCESS != saiStatus) { @@ -226,7 +226,7 @@ SaiSwitch::init(HwSwitch::Callback *callback) { attr.value.s32 = SAI_HOSTIF_TYPE_FD; hostIfAttrList.push_back(attr); - saiStatus = pSaiHostIntfApi_->create_hostif(&hostIfFdId_, + saiStatus = saiHostIntfApi_->create_hostif(&hostIfFdId_, hostIfAttrList.size(), hostIfAttrList.data()); if(SAI_STATUS_SUCCESS != saiStatus) { @@ -236,12 +236,12 @@ SaiSwitch::init(HwSwitch::Callback *callback) { attr.id = SAI_HOSTIF_TRAP_ATTR_PACKET_ACTION; attr.value.s32 = SAI_PACKET_ACTION_TRAP; - saiStatus = pSaiHostIntfApi_->set_trap_attribute(SAI_HOSTIF_TRAP_ID_ARP_REQUEST, &attr); + 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 = pSaiHostIntfApi_->set_trap_attribute(SAI_HOSTIF_TRAP_ID_ARP_RESPONSE, &attr); + 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); } @@ -395,7 +395,7 @@ bool SaiSwitch::sendPacketSwitched(std::unique_ptr pkt) noexcept { attr.value.s32 = SAI_HOSTIF_TX_TYPE_PIPELINE_LOOKUP; attrList.push_back(attr); - sai_status_t status = pSaiHostIntfApi_->send_packet(hostIfFdId_, + sai_status_t status = saiHostIntfApi_->send_packet(hostIfFdId_, pkt->buf()->writableData(), pkt->buf()->length(), attrList.size(), @@ -431,7 +431,7 @@ bool SaiSwitch::sendPacketOutOfPort(std::unique_ptr pkt, PortID portID attr.value.oid = portId; attrList.push_back(attr); - sai_status_t status = pSaiHostIntfApi_->send_packet(hostIfFdId_, + sai_status_t status = saiHostIntfApi_->send_packet(hostIfFdId_, pkt->buf()->writableData(), pkt->buf()->length(), attrList.size(), @@ -840,7 +840,7 @@ void SaiSwitch::ProcessChangedVlan(const shared_ptr &oldVlan, VLOG(2) << "updating VLAN " << newVlan->getID() << ": " << removedPorts.size() << " ports removed"; - saiStatus = pSaiVlanApi_->remove_ports_from_vlan(vlanId, removedPorts.size(), + 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; @@ -851,7 +851,7 @@ void SaiSwitch::ProcessChangedVlan(const shared_ptr &oldVlan, VLOG(2) << "updating VLAN " << newVlan->getID() << ": " << addedPorts.size() << " ports added"; - saiStatus = pSaiVlanApi_->add_ports_to_vlan(vlanId, addedPorts.size(), + 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; @@ -935,13 +935,13 @@ void SaiSwitch::ProcessAddedVlan(const shared_ptr &vlan) { VLOG(3) << "Creating VLAN " << (uint16_t)vlan->getID() << " with " << vlan->getPorts().size() << " ports."; - saiStatus = pSaiVlanApi_->create_vlan(vlanId); + saiStatus = saiVlanApi_->create_vlan(vlanId); if (saiStatus != SAI_STATUS_SUCCESS) { LOG(ERROR) << "Failed to create VLAN " << vlanId; return; } - saiStatus = pSaiVlanApi_->add_ports_to_vlan(vlanId, portList.size(), portList.data()); + 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; @@ -973,12 +973,12 @@ void SaiSwitch::PreprocessRemovedVlan(const shared_ptr &vlan) { portList.push_back(vlanPort); } - saiStatus = pSaiVlanApi_->remove_ports_from_vlan(vlanId, portList.size(), portList.data()); + saiStatus = saiVlanApi_->remove_ports_from_vlan(vlanId, portList.size(), portList.data()); if (saiStatus != SAI_STATUS_SUCCESS) { LOG(ERROR) << "Failed to remove VLAN " << vlanId; } - saiStatus = pSaiVlanApi_->remove_vlan(vlanId); + saiStatus = saiVlanApi_->remove_vlan(vlanId); if (saiStatus != SAI_STATUS_SUCCESS) { LOG(ERROR) << "Failed to remove VLAN " << vlanId; } @@ -991,7 +991,7 @@ void SaiSwitch::ProcessRemovedVlan(const shared_ptr &vlan) { VLOG(2) << "removing VLAN " << vlan->getID(); - //saiStatus = pSaiVlanApi_->remove_vlan(vlan->getID()); + //saiStatus = saiVlanApi_->remove_vlan(vlan->getID()); if (saiStatus != SAI_STATUS_SUCCESS) throw SaiError("Failed to remove VLAN ", vlan->getID()); diff --git a/fboss/agent/hw/sai/SaiSwitch.h b/fboss/agent/hw/sai/SaiSwitch.h index 75b552f954d23..44d95e875ca2e 100644 --- a/fboss/agent/hw/sai/SaiSwitch.h +++ b/fboss/agent/hw/sai/SaiSwitch.h @@ -147,47 +147,47 @@ class SaiSwitch : public HwSwitch { /* SAI API lists */ sai_switch_api_t *GetSaiSwitchApi() const { - return pSaiSwitchApi_; + return saiSwitchApi_; } sai_vlan_api_t *GetSaiVlanApi() const { - return pSaiVlanApi_; + return saiVlanApi_; } sai_port_api_t *GetSaiPortApi() const { - return pSaiPortApi_; + return saiPortApi_; } sai_virtual_router_api_t *GetSaiVrfApi() const { - return pSaiVrfApi_; + return saiVrfApi_; } sai_route_api_t *GetSaiRouteApi() const { - return pSaiRouteApi_; + return saiRouteApi_; } sai_router_interface_api_t *GetSaiRouterIntfApi() const { - return pSaiRouterIntfApi_; + return saiRouterIntfApi_; } sai_neighbor_api_t *GetSaiNeighborApi() const { - return pSaiNeighborApi_; + return saiNeighborApi_; } sai_hostif_api_t *GetSaiHostInterfaceApi() const { - return pSaiHostInterfaceApi_; + return saiHostInterfaceApi_; } sai_acl_api_t *GetSaiAclApi() const { - return pSaiAclApi_; + return saiAclApi_; } sai_next_hop_api_t *GetSaiNextHopApi() const { - return pSaiNextHopApi_; + return saiNextHopApi_; } sai_next_hop_group_api_t *GetSaiNextHopGroupApi() const { - return pSaiNextHopGroupApi_; + return saiNextHopGroupApi_; } private: @@ -273,18 +273,18 @@ class SaiSwitch : public HwSwitch { sai_object_list_t saiPortList_; sai_object_id_t hostIfFdId_ {SAI_NULL_OBJECT_ID}; - sai_switch_api_t *pSaiSwitchApi_ {nullptr}; - sai_vlan_api_t *pSaiVlanApi_ {nullptr}; - sai_port_api_t *pSaiPortApi_ {nullptr}; - sai_virtual_router_api_t *pSaiVrfApi_ {nullptr}; - sai_route_api_t *pSaiRouteApi_ {nullptr}; - sai_router_interface_api_t *pSaiRouterIntfApi_ {nullptr}; - sai_neighbor_api_t *pSaiNeighborApi_ {nullptr}; - sai_hostif_api_t *pSaiHostInterfaceApi_ {nullptr}; - sai_acl_api_t *pSaiAclApi_ {nullptr}; - sai_next_hop_api_t *pSaiNextHopApi_ {nullptr}; - sai_next_hop_group_api_t *pSaiNextHopGroupApi_ {nullptr}; - sai_hostif_api_t *pSaiHostIntfApi_ {nullptr}; + 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. diff --git a/fboss/agent/hw/sai/SaiVrf.cpp b/fboss/agent/hw/sai/SaiVrf.cpp index 6e1e0f3aa2d0c..f0f821107343c 100644 --- a/fboss/agent/hw/sai/SaiVrf.cpp +++ b/fboss/agent/hw/sai/SaiVrf.cpp @@ -20,14 +20,14 @@ SaiVrf::SaiVrf(const SaiSwitch *hw, RouterID fbossVrfId) VLOG(4) << "Entering " << __FUNCTION__; - pSaiVirtualRouterApi_ = hw->GetSaiVrfApi(); + saiVirtualRouterApi_ = hw->GetSaiVrfApi(); } SaiVrf::~SaiVrf() { VLOG(4) << "Entering " << __FUNCTION__; if (saiVrfId_ != SAI_NULL_OBJECT_ID) { - pSaiVirtualRouterApi_->remove_virtual_router(saiVrfId_); + saiVirtualRouterApi_->remove_virtual_router(saiVrfId_); } } @@ -62,7 +62,7 @@ void SaiVrf::Program() { VLOG(2) << "Create virtual router " << fbossVrfId_.t << " through SAI"; - saiRetVal = pSaiVirtualRouterApi_->create_virtual_router(&vrf_id, SAI_VRF_ATTR_COUNT, attr_list); + 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); } diff --git a/fboss/agent/hw/sai/SaiVrf.h b/fboss/agent/hw/sai/SaiVrf.h index b97dd1961ce1d..7cc42d2f1433d 100644 --- a/fboss/agent/hw/sai/SaiVrf.h +++ b/fboss/agent/hw/sai/SaiVrf.h @@ -75,7 +75,7 @@ class SaiVrf { RouterID fbossVrfId_ {0}; sai_object_id_t saiVrfId_ {SAI_NULL_OBJECT_ID}; - sai_virtual_router_api_t *pSaiVirtualRouterApi_ {nullptr}; + sai_virtual_router_api_t *saiVirtualRouterApi_ {nullptr}; }; }} // facebook::fboss From 1caea1d34187502b0cc4de7afc5f3d9a844348fc Mon Sep 17 00:00:00 2001 From: Vitaliy Senchyshyn Date: Thu, 14 Apr 2016 17:34:22 +0300 Subject: [PATCH 22/24] Converted FBOSS SAI adapter APIs to lower case style --- fboss/agent/hw/sai/SaiHost.cpp | 6 +- fboss/agent/hw/sai/SaiHost.h | 4 +- fboss/agent/hw/sai/SaiIntf.cpp | 24 ++-- fboss/agent/hw/sai/SaiIntf.h | 6 +- fboss/agent/hw/sai/SaiIntfTable.cpp | 34 ++--- fboss/agent/hw/sai/SaiIntfTable.h | 19 +-- fboss/agent/hw/sai/SaiNextHop.cpp | 12 +- fboss/agent/hw/sai/SaiNextHop.h | 8 +- fboss/agent/hw/sai/SaiNextHopTable.cpp | 10 +- fboss/agent/hw/sai/SaiNextHopTable.h | 6 +- fboss/agent/hw/sai/SaiPlatformBase.h | 2 +- fboss/agent/hw/sai/SaiPlatformPort.h | 6 +- fboss/agent/hw/sai/SaiPortBase.cpp | 10 +- fboss/agent/hw/sai/SaiPortBase.h | 28 ++-- fboss/agent/hw/sai/SaiPortTable.cpp | 28 ++-- fboss/agent/hw/sai/SaiPortTable.h | 14 +- fboss/agent/hw/sai/SaiRoute.cpp | 28 ++-- fboss/agent/hw/sai/SaiRoute.h | 4 +- fboss/agent/hw/sai/SaiRouteTable.cpp | 20 +-- fboss/agent/hw/sai/SaiRouteTable.h | 8 +- fboss/agent/hw/sai/SaiRxPacket.cpp | 4 +- fboss/agent/hw/sai/SaiStation.cpp | 6 +- fboss/agent/hw/sai/SaiStation.h | 2 +- fboss/agent/hw/sai/SaiSwitch.cpp | 166 +++++++++++----------- fboss/agent/hw/sai/SaiSwitch.h | 96 ++++++------- fboss/agent/hw/sai/SaiVrf.cpp | 6 +- fboss/agent/hw/sai/SaiVrf.h | 10 +- fboss/agent/hw/sai/SaiVrfTable.cpp | 8 +- fboss/agent/hw/sai/SaiVrfTable.h | 6 +- fboss/agent/hw/sai/SaiWarmBootCache.cpp | 18 +-- fboss/agent/hw/sai/SaiWarmBootCache.h | 4 +- fboss/agent/platforms/sai/SaiPlatform.cpp | 2 +- fboss/agent/platforms/sai/SaiPlatform.h | 2 +- fboss/agent/platforms/sai/SaiPort.cpp | 2 +- fboss/agent/platforms/sai/SaiPort.h | 4 +- 35 files changed, 307 insertions(+), 306 deletions(-) diff --git a/fboss/agent/hw/sai/SaiHost.cpp b/fboss/agent/hw/sai/SaiHost.cpp index 76d0cc45836d0..ff10b5047ffc4 100644 --- a/fboss/agent/hw/sai/SaiHost.cpp +++ b/fboss/agent/hw/sai/SaiHost.cpp @@ -30,7 +30,7 @@ SaiHost::SaiHost(const SaiSwitch *hw, InterfaceID intf, VLOG(4) << "Entering " << __FUNCTION__; - saiNeighborApi_ = hw_->GetSaiNeighborApi(); + saiNeighborApi_ = hw_->getSaiNeighborApi(); } SaiHost::~SaiHost() { @@ -44,7 +44,7 @@ SaiHost::~SaiHost() { VLOG(3) << "Deleted L3 host object for " << ip_; } -void SaiHost::Program(sai_packet_action_t action, const folly::MacAddress &mac) { +void SaiHost::program(sai_packet_action_t action, const folly::MacAddress &mac) { VLOG(4) << "Entering " << __FUNCTION__; if (!added_) { @@ -54,7 +54,7 @@ void SaiHost::Program(sai_packet_action_t action, const folly::MacAddress &mac) } // fill neighborEntry_ - neighborEntry_.rif_id = hw_->GetIntfTable()->GetIntf(intf_)->GetIfId(); + neighborEntry_.rif_id = hw_->getIntfTable()->getIntf(intf_)->getIfId(); if (ip_.isV4()) { // IPv4 diff --git a/fboss/agent/hw/sai/SaiHost.h b/fboss/agent/hw/sai/SaiHost.h index bb7d78b96d75f..3e07efc91780c 100644 --- a/fboss/agent/hw/sai/SaiHost.h +++ b/fboss/agent/hw/sai/SaiHost.h @@ -31,7 +31,7 @@ class SaiHost { * 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 + * program() will be called soon after construction, and any * actual initialization logic will be performed there. */ SaiHost(const SaiSwitch *hw, @@ -62,7 +62,7 @@ class SaiHost { * * @return none */ - void Program(sai_packet_action_t action, const folly::MacAddress &mac); + void program(sai_packet_action_t action, const folly::MacAddress &mac); private: // no copy or assignment diff --git a/fboss/agent/hw/sai/SaiIntf.cpp b/fboss/agent/hw/sai/SaiIntf.cpp index 90b71029865aa..7560a7a695997 100644 --- a/fboss/agent/hw/sai/SaiIntf.cpp +++ b/fboss/agent/hw/sai/SaiIntf.cpp @@ -28,7 +28,7 @@ SaiIntf::SaiIntf(const SaiSwitch *hw) : hw_(hw) { VLOG(4) << "Entering " << __FUNCTION__; - saiRouterInterfaceApi_ = hw->GetSaiRouterIntfApi(); + saiRouterInterfaceApi_ = hw->getSaiRouterIntfApi(); } SaiIntf::~SaiIntf() { @@ -46,10 +46,10 @@ SaiIntf::~SaiIntf() { saiRouterInterfaceApi_->remove_router_interface(saiIfId_); } - hw_->WritableVrfTable()->DerefSaiVrf(intf_->getRouterID()); + hw_->writableVrfTable()->derefSaiVrf(intf_->getRouterID()); } -void SaiIntf::Program(const shared_ptr &intf) { +void SaiIntf::program(const shared_ptr &intf) { VLOG(4) << "Entering " << __FUNCTION__; sai_status_t saiRetVal = SAI_STATUS_FAILURE; @@ -68,12 +68,12 @@ void SaiIntf::Program(const shared_ptr &intf) { " ) is changed during programming"); } } else { - auto vrf = hw_->WritableVrfTable()->IncRefOrCreateSaiVrf(intf->getRouterID()); + auto vrf = hw_->writableVrfTable()->incRefOrCreateSaiVrf(intf->getRouterID()); try { - vrf->Program(); + vrf->program(); } catch (const FbossError &e) { - hw_->WritableVrfTable()->DerefSaiVrf(intf->getRouterID()); + hw_->writableVrfTable()->derefSaiVrf(intf->getRouterID()); // let handle this in the caller. throw; } @@ -95,7 +95,7 @@ void SaiIntf::Program(const shared_ptr &intf) { //Router ID attr_list[2].id = SAI_ROUTER_INTERFACE_ATTR_VIRTUAL_ROUTER_ID; - attr_list[2].value.oid = hw_->WritableVrfTable()->GetSaiVrfId(intf->getRouterID()); + attr_list[2].value.oid = hw_->writableVrfTable()->getSaiVrfId(intf->getRouterID()); attr_count++; //Admin V4 state @@ -155,14 +155,14 @@ void SaiIntf::Program(const shared_ptr &intf) { } auto createHost = [&](const IPAddress& addr) { - auto host = hw_->WritableHostTable()->createOrUpdateSaiHost(intf->getID(), + auto host = hw_->writableHostTable()->createOrUpdateSaiHost(intf->getID(), addr, intf->getMac()); try { - host->Program(SAI_PACKET_ACTION_TRAP, intf->getMac()); + host->program(SAI_PACKET_ACTION_TRAP, intf->getMac()); } catch (const FbossError &e) { - hw_->WritableHostTable()->removeSaiHost(intf->getID(), addr); + hw_->writableHostTable()->removeSaiHost(intf->getID(), addr); // let handle this in the caller. throw; } @@ -170,7 +170,7 @@ void SaiIntf::Program(const shared_ptr &intf) { auto ret = hosts_.insert(addr); CHECK(ret.second); SCOPE_FAIL { - hw_->WritableHostTable()->removeSaiHost(intf->getID(), addr); + hw_->writableHostTable()->removeSaiHost(intf->getID(), addr); hosts_.erase(ret.first); }; }; @@ -227,7 +227,7 @@ void SaiIntf::removeHost(const folly::IPAddress& addr) { return; } - hw_->WritableHostTable()->removeSaiHost(intf_->getID(), addr); + hw_->writableHostTable()->removeSaiHost(intf_->getID(), addr); hosts_.erase(iter); } diff --git a/fboss/agent/hw/sai/SaiIntf.h b/fboss/agent/hw/sai/SaiIntf.h index 642ccca9b78ce..d70d08a6930b9 100644 --- a/fboss/agent/hw/sai/SaiIntf.h +++ b/fboss/agent/hw/sai/SaiIntf.h @@ -31,14 +31,14 @@ class SaiIntf { explicit SaiIntf(const SaiSwitch *hw); virtual ~SaiIntf(); - sai_object_id_t GetIfId() const { + sai_object_id_t getIfId() const { return saiIfId_; } - const std::shared_ptr &GetInterface() const { + const std::shared_ptr &getInterface() const { return intf_; } - void Program(const std::shared_ptr &intf); + void program(const std::shared_ptr &intf); private: // no copy or assignment diff --git a/fboss/agent/hw/sai/SaiIntfTable.cpp b/fboss/agent/hw/sai/SaiIntfTable.cpp index 597f67296ff58..95d7c94ae41f0 100644 --- a/fboss/agent/hw/sai/SaiIntfTable.cpp +++ b/fboss/agent/hw/sai/SaiIntfTable.cpp @@ -27,7 +27,7 @@ SaiIntfTable::~SaiIntfTable() { VLOG(4) << "Entering " << __FUNCTION__; } -SaiIntf *SaiIntfTable::GetIntfIf(sai_object_id_t id) const { +SaiIntf *SaiIntfTable::getIntfIf(sai_object_id_t id) const { VLOG(4) << "Entering " << __FUNCTION__; auto iter = saiIntfs_.find(id); @@ -39,9 +39,9 @@ SaiIntf *SaiIntfTable::GetIntfIf(sai_object_id_t id) const { return iter->second; } -SaiIntf *SaiIntfTable::GetIntf(sai_object_id_t id) const { +SaiIntf *SaiIntfTable::getIntf(sai_object_id_t id) const { VLOG(4) << "Entering " << __FUNCTION__; - auto ptr = GetIntfIf(id); + auto ptr = getIntfIf(id); if (ptr == nullptr) { throw SaiError("Cannot find interface ", id); @@ -50,7 +50,7 @@ SaiIntf *SaiIntfTable::GetIntf(sai_object_id_t id) const { return ptr; } -SaiIntf *SaiIntfTable::GetIntfIf(InterfaceID id) const { +SaiIntf *SaiIntfTable::getIntfIf(InterfaceID id) const { VLOG(4) << "Entering " << __FUNCTION__; auto iter = intfs_.find(id); @@ -62,10 +62,10 @@ SaiIntf *SaiIntfTable::GetIntfIf(InterfaceID id) const { return iter->second.get(); } -SaiIntf *SaiIntfTable::GetIntf(InterfaceID id) const { +SaiIntf *SaiIntfTable::getIntf(InterfaceID id) const { VLOG(4) << "Entering " << __FUNCTION__; - auto ptr = GetIntfIf(id); + auto ptr = getIntfIf(id); if (ptr == nullptr) { throw SaiError("Cannot find interface ", id); @@ -74,7 +74,7 @@ SaiIntf *SaiIntfTable::GetIntf(InterfaceID id) const { return ptr; } -SaiIntf* SaiIntfTable::GetFirstIntfIf() const { +SaiIntf* SaiIntfTable::getFirstIntfIf() const { VLOG(4) << "Entering " << __FUNCTION__; auto iter = intfs_.begin(); @@ -86,13 +86,13 @@ SaiIntf* SaiIntfTable::GetFirstIntfIf() const { return iter->second.get(); } -SaiIntf* SaiIntfTable::GetNextIntfIf(const SaiIntf *intf) const { +SaiIntf* SaiIntfTable::getNextIntfIf(const SaiIntf *intf) const { if (intf == nullptr) { return nullptr; } - auto iter = intfs_.find(intf->GetInterface()->getID()); + auto iter = intfs_.find(intf->getInterface()->getID()); if (iter != intfs_.end()) { if(++iter != intfs_.end()) { @@ -103,7 +103,7 @@ SaiIntf* SaiIntfTable::GetNextIntfIf(const SaiIntf *intf) const { return nullptr; } -void SaiIntfTable::AddIntf(const shared_ptr &intf) { +void SaiIntfTable::addIntf(const shared_ptr &intf) { VLOG(4) << "Entering " << __FUNCTION__; auto newIntf = unique_ptr(new SaiIntf(hw_)); @@ -114,19 +114,19 @@ void SaiIntfTable::AddIntf(const shared_ptr &intf) { throw SaiError("Adding an existing interface ", intf->getID()); } - intfPtr->Program(intf); - auto ret2 = saiIntfs_.insert(std::make_pair(intfPtr->GetIfId(), intfPtr)); + 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) { +void SaiIntfTable::programIntf(const shared_ptr &intf) { VLOG(4) << "Entering " << __FUNCTION__; - auto intfPtr = GetIntf(intf->getID()); - intfPtr->Program(intf); + auto intfPtr = getIntf(intf->getID()); + intfPtr->program(intf); } -void SaiIntfTable::DeleteIntf(const std::shared_ptr &intf) { +void SaiIntfTable::deleteIntf(const std::shared_ptr &intf) { VLOG(4) << "Entering " << __FUNCTION__; auto iter = intfs_.find(intf->getID()); @@ -136,7 +136,7 @@ void SaiIntfTable::DeleteIntf(const std::shared_ptr &intf) { intf->getID()); } - auto saiIfId = iter->second->GetIfId(); + auto saiIfId = iter->second->getIfId(); intfs_.erase(iter); saiIntfs_.erase(saiIfId); } diff --git a/fboss/agent/hw/sai/SaiIntfTable.h b/fboss/agent/hw/sai/SaiIntfTable.h index 045e7348167b0..583c39ea51865 100644 --- a/fboss/agent/hw/sai/SaiIntfTable.h +++ b/fboss/agent/hw/sai/SaiIntfTable.h @@ -30,19 +30,20 @@ class SaiIntfTable { virtual ~SaiIntfTable(); // throw an error if not found - SaiIntf *GetIntf(InterfaceID id) const; - SaiIntf *GetIntf(sai_object_id_t id) const; + 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; + 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 - void AddIntf(const std::shared_ptr &intf); - void ProgramIntf(const std::shared_ptr &intf); - void DeleteIntf(const std::shared_ptr &intf); + // 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_; diff --git a/fboss/agent/hw/sai/SaiNextHop.cpp b/fboss/agent/hw/sai/SaiNextHop.cpp index c135c96613354..c2726a3b954ed 100644 --- a/fboss/agent/hw/sai/SaiNextHop.cpp +++ b/fboss/agent/hw/sai/SaiNextHop.cpp @@ -27,7 +27,7 @@ SaiNextHop::SaiNextHop(const SaiSwitch *hw, const RouteForwardInfo &fwdInfo) for(auto nh : fwdInfo_.getNexthops()) { - if (hw_->GetHostTable()->getSaiHost(nh.intf, nh.nexthop)) { + if (hw_->getHostTable()->getSaiHost(nh.intf, nh.nexthop)) { resolvedNextHops_.emplace(nh); } else { unresolvedNextHops_.emplace(nh); @@ -36,8 +36,8 @@ SaiNextHop::SaiNextHop(const SaiSwitch *hw, const RouteForwardInfo &fwdInfo) isGroup_ = (fwdInfo_.getNexthops().size() > 1); - saiNextHopApi_ = hw->GetSaiNextHopApi(); - saiNextHopGroupApi_ = hw->GetSaiNextHopGroupApi(); + saiNextHopApi_ = hw->getSaiNextHopApi(); + saiNextHopGroupApi_ = hw->getSaiNextHopGroupApi(); } SaiNextHop::~SaiNextHop() { @@ -73,7 +73,7 @@ SaiNextHop::~SaiNextHop() { } } -sai_object_id_t SaiNextHop::GetSaiNextHopId() const { +sai_object_id_t SaiNextHop::getSaiNextHopId() const { if (isGroup_) { return nhGroupId_; @@ -135,7 +135,7 @@ void SaiNextHop::onResolved(InterfaceID intf, const folly::IPAddress &ip) { resolved_ = true; } -void SaiNextHop::Program() { +void SaiNextHop::program() { VLOG(4) << "Entering " << __FUNCTION__; @@ -221,7 +221,7 @@ sai_object_id_t SaiNextHop::programNh(InterfaceID intf, const folly::IPAddress & // Router interface ID attr.id = SAI_NEXT_HOP_ATTR_ROUTER_INTERFACE_ID; - attr.value.oid = hw_->GetIntfTable()->GetIntfIf(intf)->GetIfId(); + attr.value.oid = hw_->getIntfTable()->getIntfIf(intf)->getIfId(); attrList.push_back(attr); // Create NH in SAI diff --git a/fboss/agent/hw/sai/SaiNextHop.h b/fboss/agent/hw/sai/SaiNextHop.h index 3e91906cde201..bba10b13800a9 100644 --- a/fboss/agent/hw/sai/SaiNextHop.h +++ b/fboss/agent/hw/sai/SaiNextHop.h @@ -32,7 +32,7 @@ class SaiNextHop { * 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 + * program() will be called soon after construction, and any * actual initialization logic is be performed there. */ explicit SaiNextHop(const SaiSwitch *hw, const RouteForwardInfo &fwdInfo); @@ -50,7 +50,7 @@ class SaiNextHop { * @return ID of Next Hop if it's already programmed * to HW, otherwise SAI_NULL_OBJECT_ID. */ - sai_object_id_t GetSaiNextHopId() const; + sai_object_id_t getSaiNextHopId() const; /** * Checks whether the Next Hop is resolved. @@ -78,7 +78,7 @@ class SaiNextHop { * * @return none */ - void Program(); + void program(); private: // no copy or assignment @@ -93,7 +93,7 @@ class SaiNextHop { */ sai_object_id_t programNh(InterfaceID intf, const folly::IPAddress &ip); /** - * Programs Next Hop group with Next Hops IDs "nhIds" on HW. + * 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. diff --git a/fboss/agent/hw/sai/SaiNextHopTable.cpp b/fboss/agent/hw/sai/SaiNextHopTable.cpp index 43edcee35a70d..413259fd799e7 100644 --- a/fboss/agent/hw/sai/SaiNextHopTable.cpp +++ b/fboss/agent/hw/sai/SaiNextHopTable.cpp @@ -25,7 +25,7 @@ SaiNextHopTable::~SaiNextHopTable() { VLOG(4) << "Entering " << __FUNCTION__; } -sai_object_id_t SaiNextHopTable::GetSaiNextHopId(const RouteForwardInfo &fwdInfo) const { +sai_object_id_t SaiNextHopTable::getSaiNextHopId(const RouteForwardInfo &fwdInfo) const { VLOG(4) << "Entering " << __FUNCTION__; auto iter = nextHops_.find(fwdInfo.getNexthops()); @@ -34,10 +34,10 @@ sai_object_id_t SaiNextHopTable::GetSaiNextHopId(const RouteForwardInfo &fwdInfo return SAI_NULL_OBJECT_ID; } - return iter->second.first->GetSaiNextHopId(); + return iter->second.first->getSaiNextHopId(); } -SaiNextHop* SaiNextHopTable::IncRefOrCreateSaiNextHop(const RouteForwardInfo &fwdInfo) { +SaiNextHop* SaiNextHopTable::incRefOrCreateSaiNextHop(const RouteForwardInfo &fwdInfo) { VLOG(4) << "Entering " << __FUNCTION__; auto ret = nextHops_.emplace(fwdInfo.getNexthops(), std::make_pair(nullptr, 1)); @@ -54,14 +54,14 @@ SaiNextHop* SaiNextHopTable::IncRefOrCreateSaiNextHop(const RouteForwardInfo &fw }; auto newNextHop = folly::make_unique(hw_, fwdInfo); - newNextHop->Program(); + newNextHop->program(); auto nextHopPtr = newNextHop.get(); iter->second.first = std::move(newNextHop); return nextHopPtr; } -SaiNextHop* SaiNextHopTable::DerefSaiNextHop(const RouteForwardInfo &fwdInfo) noexcept { +SaiNextHop* SaiNextHopTable::derefSaiNextHop(const RouteForwardInfo &fwdInfo) noexcept { VLOG(4) << "Entering " << __FUNCTION__; auto iter = nextHops_.find(fwdInfo.getNexthops()); diff --git a/fboss/agent/hw/sai/SaiNextHopTable.h b/fboss/agent/hw/sai/SaiNextHopTable.h index f61f7a1e40de8..1a4af80bc72ad 100644 --- a/fboss/agent/hw/sai/SaiNextHopTable.h +++ b/fboss/agent/hw/sai/SaiNextHopTable.h @@ -34,7 +34,7 @@ class SaiNextHopTable { * @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; + sai_object_id_t getSaiNextHopId(const RouteForwardInfo &fwdInfo) const; /* * The following functions will modify the object. They rely on the global @@ -50,7 +50,7 @@ class SaiNextHopTable { * * @return The SaiNextHop pointer just created or found. */ - SaiNextHop* IncRefOrCreateSaiNextHop(const RouteForwardInfo &fwdInfo); + SaiNextHop* incRefOrCreateSaiNextHop(const RouteForwardInfo &fwdInfo); /** * Decreases an existing SaiNextHop entry's reference counter by 1. @@ -62,7 +62,7 @@ class SaiNextHopTable { * decreased by 1, but the object is still valid as it is * still referred in somewhere else */ - SaiNextHop *DerefSaiNextHop(const RouteForwardInfo &fwdInfo) noexcept; + SaiNextHop *derefSaiNextHop(const RouteForwardInfo &fwdInfo) noexcept; /** * Loops trough all the Next Hops and resolves(if not resolved yet) diff --git a/fboss/agent/hw/sai/SaiPlatformBase.h b/fboss/agent/hw/sai/SaiPlatformBase.h index 837d8ee1471b3..6b7064f5d29cd 100644 --- a/fboss/agent/hw/sai/SaiPlatformBase.h +++ b/fboss/agent/hw/sai/SaiPlatformBase.h @@ -29,7 +29,7 @@ class SaiPlatformBase : public Platform { SaiPlatformBase() {} /* - * InitPorts() will be called during port initialization. + * 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 diff --git a/fboss/agent/hw/sai/SaiPlatformPort.h b/fboss/agent/hw/sai/SaiPlatformPort.h index a468e0bc5aa68..616caf3381951 100644 --- a/fboss/agent/hw/sai/SaiPlatformPort.h +++ b/fboss/agent/hw/sai/SaiPlatformPort.h @@ -23,10 +23,10 @@ class SaiPlatformPort : public PlatformPort { SaiPlatformPort &operator=(SaiPlatformPort&&) = default; /* - * SetPort() should be called once during port initialization. + * setPort() should be called once during port initialization. */ - virtual void SetPort(SaiPortBase *port) = 0; - virtual SaiPortBase *GetPort() const = 0; + virtual void setPort(SaiPortBase *port) = 0; + virtual SaiPortBase *getPort() const = 0; private: // Forbidden copy constructor and assignment operator diff --git a/fboss/agent/hw/sai/SaiPortBase.cpp b/fboss/agent/hw/sai/SaiPortBase.cpp index aacda89845567..d0c8887a50425 100644 --- a/fboss/agent/hw/sai/SaiPortBase.cpp +++ b/fboss/agent/hw/sai/SaiPortBase.cpp @@ -33,11 +33,11 @@ SaiPortBase::~SaiPortBase() { VLOG(4) << "Entering " << __FUNCTION__; } -void SaiPortBase::Init(bool warmBoot) { +void SaiPortBase::init(bool warmBoot) { VLOG(6) << "Entering " << __FUNCTION__; try { - SetIngressVlan(pvId_); + setIngressVlan(pvId_); } catch (const SaiError &e) { LOG(ERROR) << e.what(); } @@ -61,7 +61,7 @@ void SaiPortBase::setPortStatus(bool linkStatus) { linkStatus_ = linkStatus; } -void SaiPortBase::SetIngressVlan(VlanID vlan) { +void SaiPortBase::setIngressVlan(VlanID vlan) { VLOG(6) << "Entering " << __FUNCTION__; if (initDone_ && (pvId_ == vlan)) { @@ -126,13 +126,13 @@ void SaiPortBase::disable() { setPortStatus(adminMode_); } -std::string SaiPortBase::StatName(folly::StringPiece name) const { +std::string SaiPortBase::statName(folly::StringPiece name) const { VLOG(6) << "Entering " << __FUNCTION__; return folly::to("port", platformPort_->getPortID(), ".", name); } -void SaiPortBase::UpdateStats() { +void SaiPortBase::updateStats() { VLOG(6) << "Entering " << __FUNCTION__; } diff --git a/fboss/agent/hw/sai/SaiPortBase.h b/fboss/agent/hw/sai/SaiPortBase.h index 0025ab95a782c..c5a8caa0ab4cd 100644 --- a/fboss/agent/hw/sai/SaiPortBase.h +++ b/fboss/agent/hw/sai/SaiPortBase.h @@ -38,31 +38,31 @@ class SaiPortBase { virtual ~SaiPortBase(); /** - * @brief Initialize port + * @brief initialize port * @param warmBoot, switch boot type */ - void Init(bool warmBoot); + void init(bool warmBoot); /* * Getters. */ - SaiPlatformPort *GetPlatformPort() const { + SaiPlatformPort *getPlatformPort() const { return platformPort_; } - SaiSwitch *GetHwSwitch() const { + SaiSwitch *getHwSwitch() const { return hw_; } - sai_object_id_t GetSaiPortId() const { + sai_object_id_t getSaiPortId() const { return saiPortId_; } - PortID GetFbossPortId() const { + PortID getFbossPortId() const { return fbossPortId_; } - VlanID GetIngressVlan() { + VlanID getIngressVlan() { return pvId_; } @@ -76,19 +76,19 @@ class SaiPortBase { void disable(); void setPortStatus(bool linkStatus); - void SetIngressVlan(VlanID vlan); + void setIngressVlan(VlanID vlan); /* * Update this port's statistics. */ - void UpdateStats(); + 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; + std::string statName(folly::StringPiece name) const; SaiSwitch *const hw_ { nullptr }; // Pointer to HW Switch SaiPlatformPort *const platformPort_ { nullptr }; // Pointer to Platform port @@ -113,11 +113,11 @@ class SaiPortBase { : 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 inBytes_ { statName("in_bytes") }; + MonotonicCounter inMulticastPkts_ { statName("in_multicast_pkts") }; + MonotonicCounter inBroadcastPkts_ { statName("in_broadcast_pkts") }; - MonotonicCounter outBytes_ { StatName("out_bytes") }; + MonotonicCounter outBytes_ { statName("out_bytes") }; }; }} // facebook::fboss diff --git a/fboss/agent/hw/sai/SaiPortTable.cpp b/fboss/agent/hw/sai/SaiPortTable.cpp index 144352053b6a3..2b4d220b1688f 100644 --- a/fboss/agent/hw/sai/SaiPortTable.cpp +++ b/fboss/agent/hw/sai/SaiPortTable.cpp @@ -30,10 +30,10 @@ SaiPortTable::~SaiPortTable() { VLOG(4) << "Entering " << __FUNCTION__; } -void SaiPortTable::InitPorts(bool warmBoot) { +void SaiPortTable::initPorts(bool warmBoot) { VLOG(4) << "Entering " << __FUNCTION__; - auto platformPorts = hw_->GetPlatform()->initPorts(); + auto platformPorts = hw_->getPlatform()->initPorts(); for (const auto& entry : platformPorts) { sai_object_id_t saiPortId = entry.first; @@ -41,23 +41,23 @@ void SaiPortTable::InitPorts(bool warmBoot) { PortID fbossPortID = platformPort->getPortID(); auto saiPortBase = make_unique(hw_, saiPortId, fbossPortID, platformPort); - platformPort->SetPort(saiPortBase.get()); - saiPortBase->Init(warmBoot); + 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(); +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(); +PortID SaiPortTable::getPortId(sai_object_id_t portId) const { + return getSaiPort(portId)->getFbossPortId(); } -SaiPortBase *SaiPortTable::GetSaiPort(PortID id) const { +SaiPortBase *SaiPortTable::getSaiPort(PortID id) const { VLOG(6) << "Entering " << __FUNCTION__; auto iter = fbossPhysicalPorts_.find(id); @@ -69,7 +69,7 @@ SaiPortBase *SaiPortTable::GetSaiPort(PortID id) const { return iter->second; } -SaiPortBase *SaiPortTable::GetSaiPort(sai_object_id_t id) const { +SaiPortBase *SaiPortTable::getSaiPort(sai_object_id_t id) const { VLOG(6) << "Entering " << __FUNCTION__; auto iter = saiPhysicalPorts_.find(id); @@ -83,19 +83,19 @@ SaiPortBase *SaiPortTable::GetSaiPort(sai_object_id_t id) const { return iter->second.get(); } -void SaiPortTable::SetPortStatus(sai_object_id_t portId, int status) { +void SaiPortTable::setPortStatus(sai_object_id_t portId, int status) { VLOG(6) << "Entering " << __FUNCTION__; - auto port = GetSaiPort(portId); + auto port = getSaiPort(portId); port->setPortStatus(status); } -void SaiPortTable::UpdatePortStats() { +void SaiPortTable::updatePortStats() { VLOG(6) << "Entering " << __FUNCTION__; for (const auto& entry : saiPhysicalPorts_) { SaiPortBase *saiPort = entry.second.get(); - saiPort->UpdateStats(); + saiPort->updateStats(); } } diff --git a/fboss/agent/hw/sai/SaiPortTable.h b/fboss/agent/hw/sai/SaiPortTable.h index b549103ad6e30..4821767eaf0ff 100644 --- a/fboss/agent/hw/sai/SaiPortTable.h +++ b/fboss/agent/hw/sai/SaiPortTable.h @@ -34,27 +34,27 @@ class SaiPortTable { * No other SaiPortTable methods should be accessed before initPorts() * completes. */ - void InitPorts(bool warmBoot); + void initPorts(bool warmBoot); /* * Getters. */ - sai_object_id_t GetSaiPortId(PortID id) const; - PortID GetPortId(sai_object_id_t portId) const; + 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; + 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); + void setPortStatus(sai_object_id_t portId, int status); /* * Update all ports' statistics. */ - void UpdatePortStats(); + void updatePortStats(); private: typedef boost::container::flat_map> SaiPortMap; diff --git a/fboss/agent/hw/sai/SaiRoute.cpp b/fboss/agent/hw/sai/SaiRoute.cpp index edb9e5d50ecf7..acdff810465e8 100644 --- a/fboss/agent/hw/sai/SaiRoute.cpp +++ b/fboss/agent/hw/sai/SaiRoute.cpp @@ -31,9 +31,9 @@ SaiRoute::SaiRoute(const SaiSwitch *hw, VLOG(4) << "Entering " << __FUNCTION__; - auto intfPtr = hw_->GetIntfTable()->GetFirstIntfIf(); + auto intfPtr = hw_->getIntfTable()->getFirstIntfIf(); while (intfPtr != nullptr) { - for (const auto& addrPair : intfPtr->GetInterface()->getAddresses()) { + 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)) { @@ -41,10 +41,10 @@ SaiRoute::SaiRoute(const SaiSwitch *hw, break; } } - intfPtr = hw_->GetIntfTable()->GetNextIntfIf(intfPtr); + intfPtr = hw_->getIntfTable()->getNextIntfIf(intfPtr); } - saiRouteApi_ = hw->GetSaiRouteApi(); + saiRouteApi_ = hw->getSaiRouteApi(); } SaiRoute::~SaiRoute() { @@ -62,14 +62,14 @@ SaiRoute::~SaiRoute() { if (fwd_.getAction() == RouteForwardAction::NEXTHOPS) { // derefernce next hop - hw_->WritableNextHopTable()->DerefSaiNextHop(fwd_); + hw_->writableNextHopTable()->derefSaiNextHop(fwd_); } // dereference vrf_ - hw_->WritableVrfTable()->DerefSaiVrf(vrf_); + hw_->writableVrfTable()->derefSaiVrf(vrf_); } -void SaiRoute::Program(const RouteForwardInfo &fwd) { +void SaiRoute::program(const RouteForwardInfo &fwd) { VLOG(4) << "Entering " << __FUNCTION__; if (isLocal_) { @@ -99,23 +99,23 @@ void SaiRoute::Program(const RouteForwardInfo &fwd) { if (fwd_.getAction() == RouteForwardAction::NEXTHOPS) { // derefernce old next hop - hw_->WritableNextHopTable()->DerefSaiNextHop(fwd_); + 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_); + auto vrf = hw_->writableVrfTable()->incRefOrCreateSaiVrf(vrf_); try { - vrf->Program(); + vrf->program(); } catch (const SaiError &e) { - hw_->WritableVrfTable()->DerefSaiVrf(vrf_); + hw_->writableVrfTable()->derefSaiVrf(vrf_); // let handle this in the caller. throw; } - routeEntry_.vr_id = vrf->GetSaiVrfId(); + routeEntry_.vr_id = vrf->getSaiVrfId(); if (ipAddr_.empty()) { throw SaiError("Could not program route with empty subnet IP address"); @@ -212,7 +212,7 @@ void SaiRoute::Program(const RouteForwardInfo &fwd) { if (newAction == RouteForwardAction::NEXTHOPS) { // fill Next Hop ID routeAttr.id = SAI_ROUTE_ATTR_NEXT_HOP_ID; - routeAttr.value.oid = hw_->WritableNextHopTable()->IncRefOrCreateSaiNextHop(fwd)->GetSaiNextHopId(); + routeAttr.value.oid = hw_->writableNextHopTable()->incRefOrCreateSaiNextHop(fwd)->getSaiNextHopId(); if (routeAttr.value.oid != SAI_NULL_OBJECT_ID) { // Set next hop ID route attribute in SAI @@ -246,7 +246,7 @@ void SaiRoute::onResolved(InterfaceID intf, const folly::IPAddress &ip) { sai_attribute_t routeAttr {0, 0}; routeAttr.id = SAI_ROUTE_ATTR_NEXT_HOP_ID; - routeAttr.value.oid = hw_->GetNextHopTable()->GetSaiNextHopId(fwd_); + routeAttr.value.oid = hw_->getNextHopTable()->getSaiNextHopId(fwd_); if (routeAttr.value.oid != SAI_NULL_OBJECT_ID) { diff --git a/fboss/agent/hw/sai/SaiRoute.h b/fboss/agent/hw/sai/SaiRoute.h index 7a96ab5c26cc0..c545b5a6d2ea3 100644 --- a/fboss/agent/hw/sai/SaiRoute.h +++ b/fboss/agent/hw/sai/SaiRoute.h @@ -31,7 +31,7 @@ class SaiRoute { * 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 + * program() will be called soon after construction, and any * actual initialization logic is be performed there. */ SaiRoute(const SaiSwitch *hw, @@ -53,7 +53,7 @@ class SaiRoute { * * @return none */ - void Program(const RouteForwardInfo &fwd); + void program(const RouteForwardInfo &fwd); /** * Checks whether the Route is resolved. diff --git a/fboss/agent/hw/sai/SaiRouteTable.cpp b/fboss/agent/hw/sai/SaiRouteTable.cpp index ed5d99bf0b443..cc11c13f27369 100644 --- a/fboss/agent/hw/sai/SaiRouteTable.cpp +++ b/fboss/agent/hw/sai/SaiRouteTable.cpp @@ -43,7 +43,7 @@ SaiRouteTable::~SaiRouteTable() { VLOG(4) << "Entering " << __FUNCTION__; } -SaiRoute *SaiRouteTable::GetRouteIf( +SaiRoute *SaiRouteTable::getRouteIf( RouterID vrf, const folly::IPAddress &network, uint8_t mask) const { @@ -59,13 +59,13 @@ SaiRoute *SaiRouteTable::GetRouteIf( return iter->second.get(); } -SaiRoute *SaiRouteTable::GetRoute( +SaiRoute *SaiRouteTable::getRoute( RouterID vrf, const folly::IPAddress &network, uint8_t mask) const { VLOG(4) << "Entering " << __FUNCTION__; - auto rt = GetRouteIf(vrf, network, mask); + auto rt = getRouteIf(vrf, network, mask); if (!rt) { throw SaiError("Cannot find route for ", network, "/", mask, ", vrf ", vrf.t); @@ -75,7 +75,7 @@ SaiRoute *SaiRouteTable::GetRoute( } template -void SaiRouteTable::AddRoute(RouterID vrf, const RouteT *route) { +void SaiRouteTable::addRoute(RouterID vrf, const RouteT *route) { VLOG(4) << "Entering " << __FUNCTION__; const auto &prefix = route->prefix(); @@ -92,7 +92,7 @@ void SaiRouteTable::AddRoute(RouterID vrf, const RouteT *route) { } auto pRoute = ret.first->second.get(); - pRoute->Program(route->getForwardInfo()); + pRoute->program(route->getForwardInfo()); if (!pRoute->isResolved()) { unresolvedRoutes_.insert(ret.first->second.get()); @@ -100,7 +100,7 @@ void SaiRouteTable::AddRoute(RouterID vrf, const RouteT *route) { } template -void SaiRouteTable::DeleteRoute(RouterID vrf, const RouteT *route) { +void SaiRouteTable::deleteRoute(RouterID vrf, const RouteT *route) { VLOG(4) << "Entering " << __FUNCTION__; const auto &prefix = route->prefix(); @@ -142,9 +142,9 @@ void SaiRouteTable::onResolved(InterfaceID intf, const folly::IPAddress &ip) { } } -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 *); +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 index db3d274c5e576..9f2dc9aa9d719 100644 --- a/fboss/agent/hw/sai/SaiRouteTable.h +++ b/fboss/agent/hw/sai/SaiRouteTable.h @@ -26,10 +26,10 @@ class SaiRouteTable { explicit SaiRouteTable(const SaiSwitch *hw); ~SaiRouteTable(); // throw an error if not found - SaiRoute *GetRoute( + SaiRoute *getRoute( RouterID vrf, const folly::IPAddress &prefix, uint8_t len) const; // return nullptr if not found - SaiRoute *GetRouteIf( + SaiRoute *getRouteIf( RouterID vrf, const folly::IPAddress &prefix, uint8_t len) const; /* @@ -37,9 +37,9 @@ class SaiRouteTable { * HW update lock for the protection. */ template - void AddRoute(RouterID vrf, const RouteT *route); + void addRoute(RouterID vrf, const RouteT *route); template - void DeleteRoute(RouterID vrf, const RouteT *route); + void deleteRoute(RouterID vrf, const RouteT *route); /** * Loops trough all the unresolved Routes and resolves those which have diff --git a/fboss/agent/hw/sai/SaiRxPacket.cpp b/fboss/agent/hw/sai/SaiRxPacket.cpp index 957436208ab2f..a51e9cbbf1ba3 100644 --- a/fboss/agent/hw/sai/SaiRxPacket.cpp +++ b/fboss/agent/hw/sai/SaiRxPacket.cpp @@ -42,7 +42,7 @@ SaiRxPacket::SaiRxPacket(const void* buf, 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); + srcPort_ = hw->getPortTable()->getPortId(attr_list[i].value.oid); } } @@ -68,7 +68,7 @@ SaiRxPacket::SaiRxPacket(const void* buf, 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(); + srcVlan_ = hw->getPortTable()->getSaiPort(srcPort_)->getIngressVlan(); } } diff --git a/fboss/agent/hw/sai/SaiStation.cpp b/fboss/agent/hw/sai/SaiStation.cpp index 272cced7f9676..01d85cccdff10 100644 --- a/fboss/agent/hw/sai/SaiStation.cpp +++ b/fboss/agent/hw/sai/SaiStation.cpp @@ -29,12 +29,12 @@ SaiStation::~SaiStation() { return; } - hw_->GetSaiAclApi()->delete_acl_table(id_); + hw_->getSaiAclApi()->delete_acl_table(id_); VLOG(3) << "deleted SAI station entry " << id_; } -void SaiStation::Program(folly::MacAddress mac, sai_object_id_t aclTableId) { +void SaiStation::program(folly::MacAddress mac, sai_object_id_t aclTableId) { VLOG(4) << "Entering " << __FUNCTION__; CHECK_EQ(INVALID, id_); @@ -51,7 +51,7 @@ void SaiStation::Program(folly::MacAddress mac, sai_object_id_t aclTableId) { 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); + hw_->getSaiAclApi()->create_acl_table(&tableId, 3, attrList); id_ = aclTableId; VLOG (1) << "Adding SAI station with Mac : " << mac <<" and " << aclTableId; diff --git a/fboss/agent/hw/sai/SaiStation.h b/fboss/agent/hw/sai/SaiStation.h index c6aa7d8803777..29cf978d73e5e 100644 --- a/fboss/agent/hw/sai/SaiStation.h +++ b/fboss/agent/hw/sai/SaiStation.h @@ -29,7 +29,7 @@ class SaiStation { explicit SaiStation(const SaiSwitch *hw); ~SaiStation(); - void Program(folly::MacAddress mac, sai_object_id_t aclTableId); + void program(folly::MacAddress mac, sai_object_id_t aclTableId); private: // no copy or assignment diff --git a/fboss/agent/hw/sai/SaiSwitch.cpp b/fboss/agent/hw/sai/SaiSwitch.cpp index af34bb56c09c3..fb9611f40ef03 100644 --- a/fboss/agent/hw/sai/SaiSwitch.cpp +++ b/fboss/agent/hw/sai/SaiSwitch.cpp @@ -80,7 +80,7 @@ namespace facebook { namespace fboss { // TODO: remove this when SAI callbacks support pointer to user data storage. SaiSwitch* SaiSwitch::instance_ = nullptr; -facebook::fboss::cfg::PortSpeed GetPortSpeed(int port) { +facebook::fboss::cfg::PortSpeed getPortSpeed(int port) { VLOG(4) << "Entering " << __FUNCTION__; return facebook::fboss::cfg::PortSpeed::XG; @@ -140,21 +140,21 @@ SaiSwitch::~SaiSwitch() { service_ = {nullptr, nullptr}; - ReleaseLock(); + releaseLock(); } -std::shared_ptr SaiSwitch::GetColdBootSwitchState() const { +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"); + 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); + PortID portID = portTable_->getPortId(saiPort); string name = folly::to("port", portID); bootState->registerPort(portID, name); @@ -166,13 +166,13 @@ std::shared_ptr SaiSwitch::GetColdBootSwitchState() const { return bootState; } -std::shared_ptr SaiSwitch::GetWarmBootSwitchState() const { - auto warmBootState = GetColdBootSwitchState(); +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())); + port->setSpeed(getPortSpeed(port->getID())); } warmBootState->resetIntfs(warmBootCache_->reconstructInterfaceMap()); warmBootState->resetVlans(warmBootCache_->reconstructVlanMap()); @@ -209,7 +209,7 @@ SaiSwitch::init(HwSwitch::Callback *callback) { } sai_switch_notification_t swNotif = {NULL, NULL, NULL, NULL, NULL, NULL}; - swNotif.on_packet_event = PacketRxCallback; + swNotif.on_packet_event = packetRxCallback; sai_status_t saiStatus = saiSwitchApi_->initialize_switch(profileId, const_cast(hwId_.c_str()), @@ -250,7 +250,7 @@ SaiSwitch::init(HwSwitch::Callback *callback) { bootType = BootType::COLD_BOOT; attr.id = SAI_SWITCH_ATTR_PORT_NUMBER; - saiStatus = GetSaiSwitchApi()->get_switch_attribute(1, &attr); + saiStatus = getSaiSwitchApi()->get_switch_attribute(1, &attr); if(SAI_STATUS_SUCCESS != saiStatus) throw SaiFatal("Retrieve port number error."); @@ -260,7 +260,7 @@ SaiSwitch::init(HwSwitch::Callback *callback) { attr.value.objlist = saiPortList_; attr.id = SAI_SWITCH_ATTR_PORT_LIST; - saiStatus = GetSaiSwitchApi()->get_switch_attribute(1, &attr); + saiStatus = getSaiSwitchApi()->get_switch_attribute(1, &attr); if(SAI_STATUS_SUCCESS != saiStatus) throw SaiFatal("Retrieve port list error."); @@ -270,7 +270,7 @@ SaiSwitch::init(HwSwitch::Callback *callback) { else { VLOG(2) << "Performing warm boot"; } - portTable_->InitPorts(false); + 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(). @@ -285,19 +285,19 @@ SaiSwitch::init(HwSwitch::Callback *callback) { attr.value.mac[3] = 3; attr.value.mac[4] = 4; attr.value.mac[5] = 5; - saiStatus = GetSaiSwitchApi()->set_switch_attribute(&attr); + 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(); + tryGetLock(); if (warmBoot) { - state = GetWarmBootSwitchState(); + state = getWarmBootSwitchState(); stateChanged(StateDelta(make_shared(), state)); } else - state = GetColdBootSwitchState(); + state = getColdBootSwitchState(); return std::make_pair(state, bootType); } @@ -306,7 +306,7 @@ void SaiSwitch::unregisterCallbacks() { VLOG(4) << "Entering " << __FUNCTION__; } -void SaiSwitch::EcmpHashSetup() { +void SaiSwitch::ecmpHashSetup() { VLOG(4) << "Entering " << __FUNCTION__; } @@ -322,11 +322,11 @@ void SaiSwitch::stateChanged(const StateDelta &delta) { processDisabledPorts(delta); // remove all routes to be deleted - ProcessRemovedRoutes(delta); + processRemovedRoutes(delta); // delete all interface not existing anymore. that should stop // all traffic on that interface now - forEachRemoved(delta.getIntfsDelta(), &SaiSwitch::ProcessRemovedIntf, this); + 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 @@ -334,37 +334,37 @@ void SaiSwitch::stateChanged(const StateDelta &delta) { // VLAN will still switch use this VLAN until we get the new VLAN fully // configured. forEachChanged(delta.getVlansDelta(), - &SaiSwitch::ProcessChangedVlan, - &SaiSwitch::ProcessAddedVlan, - &SaiSwitch::PreprocessRemovedVlan, + &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); + updateIngressVlan(oldPort, newPort); } if (oldPort->getSpeed() != newPort->getSpeed()) { - UpdatePortSpeed(oldPort, newPort); + updatePortSpeed(oldPort, newPort); } }); // Update changed interfaces - forEachChanged(delta.getIntfsDelta(), &SaiSwitch::ProcessChangedIntf, this); + forEachChanged(delta.getIntfsDelta(), &SaiSwitch::processChangedIntf, this); // Remove deleted VLANs - forEachRemoved(delta.getVlansDelta(), &SaiSwitch::ProcessRemovedVlan, this); + forEachRemoved(delta.getVlansDelta(), &SaiSwitch::processRemovedVlan, this); // Add all new interfaces - forEachAdded(delta.getIntfsDelta(), &SaiSwitch::ProcessAddedIntf, this); + forEachAdded(delta.getIntfsDelta(), &SaiSwitch::processAddedIntf, this); // Any ARP changes - ProcessArpChanges(delta); + processArpChanges(delta); // Process any new routes or route changes - ProcessAddedChangedRoutes(delta); + processAddedChangedRoutes(delta); // As the last step, enable newly enabled ports. // Doing this as the last step ensures that we only start forwarding traffic @@ -413,7 +413,7 @@ bool SaiSwitch::sendPacketOutOfPort(std::unique_ptr pkt, PortID portID sai_object_id_t portId {SAI_NULL_OBJECT_ID}; try { - portId = GetPortTable()->GetSaiPortId(portID); + portId = getPortTable()->getSaiPortId(portID); } catch (const SaiError &e) { LOG(ERROR) << "Could not sent packet out of port:" << portID.t << " Reason: " << e.what(); @@ -444,18 +444,18 @@ bool SaiSwitch::sendPacketOutOfPort(std::unique_ptr pkt, PortID portID return true; } -void SaiSwitch::UpdateIngressVlan(const std::shared_ptr &oldPort, +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()); + portTable_->getSaiPort(newPort->getID())->setIngressVlan(newPort->getIngressVlan()); } catch (const SaiError &e) { LOG(ERROR) << e.what(); } } -void SaiSwitch::UpdatePortSpeed(const std::shared_ptr &oldPort, +void SaiSwitch::updatePortSpeed(const std::shared_ptr &oldPort, const std::shared_ptr &newPort) { VLOG(4) << "Entering " << __FUNCTION__; } @@ -468,7 +468,7 @@ void SaiSwitch::processDisabledPorts(const StateDelta& delta) { if (!oldPort->isDisabled() && newPort->isDisabled()) { try { - portTable_->GetSaiPort(newPort->getID())->disable(); + portTable_->getSaiPort(newPort->getID())->disable(); } catch (const SaiError &e) { LOG(ERROR) << e.what(); } @@ -485,7 +485,7 @@ void SaiSwitch::processEnabledPorts(const StateDelta& delta) { if (oldPort->isDisabled() && !newPort->isDisabled()) { try { - portTable_->GetSaiPort(newPort->getID())->enable(); + portTable_->getSaiPort(newPort->getID())->enable(); } catch (const SaiError &e) { LOG(ERROR) << e.what(); } @@ -514,7 +514,7 @@ folly::dynamic SaiSwitch::gracefulExit() { LOG(WARNING) << "Exit graceful"; folly::dynamic hwSwitch = toFollyDynamic(); // TODO: Save cache for next warm boot - GetSaiSwitchApi()->disconnect_switch(); + getSaiSwitchApi()->disconnect_switch(); return hwSwitch; } @@ -540,7 +540,7 @@ bool SaiSwitch::isPortUp(PortID port) const { void SaiSwitch::updateStats(SwitchStats *switchStats) { VLOG(4) << "Entering " << __FUNCTION__; - UpdateSwitchStats(switchStats); + updateSwitchStats(switchStats); // Update thread-local per-port statistics. PortStatsMap *portStatsMap = switchStats->getPortStats(); @@ -548,11 +548,11 @@ void SaiSwitch::updateStats(SwitchStats *switchStats) { for (auto& entry : *portStatsMap) { PortID portId = entry.first; PortStats *portStats = entry.second.get(); - UpdatePortStats(portId, portStats); + updatePortStats(portId, portStats); } // Update global statistics. - portTable_->UpdatePortStats(); + portTable_->updatePortStats(); } int SaiSwitch::getHighresSamplers( @@ -582,24 +582,24 @@ cfg::PortSpeed SaiSwitch::getMaxPortSpeed(PortID port) const { return cfg::PortSpeed(10000); } -void SaiSwitch::UpdateSwitchStats(SwitchStats *switchStats) { +void SaiSwitch::updateSwitchStats(SwitchStats *switchStats) { VLOG(4) << "Entering " << __FUNCTION__; // TODO } -void SaiSwitch::UpdatePortStats(PortID portID, PortStats *portStats) { +void SaiSwitch::updatePortStats(PortID portID, PortStats *portStats) { VLOG(4) << "Entering " << __FUNCTION__; // TODO } -SaiPlatformBase *SaiSwitch::GetPlatform() const { +SaiPlatformBase *SaiSwitch::getPlatform() const { VLOG(4) << "Entering " << __FUNCTION__; return platform_; } template -void SaiSwitch::ProcessNeighborEntryDelta(const DELTA &delta) { +void SaiSwitch::processNeighborEntryDelta(const DELTA &delta) { VLOG(4) << "Entering " << __FUNCTION__; const auto *oldEntry = delta.getOld().get(); @@ -617,7 +617,7 @@ void SaiSwitch::ProcessNeighborEntryDelta(const DELTA &delta) { try { auto oldAction = host->getSaiAction(); - host->Program(action, newEntry->getMac()); + host->program(action, newEntry->getMac()); if ((oldAction != action) && (action == SAI_PACKET_ACTION_FORWARD)) { @@ -660,22 +660,22 @@ void SaiSwitch::ProcessNeighborEntryDelta(const DELTA &delta) { } } -void SaiSwitch::ProcessArpChanges(const StateDelta &delta) { +void SaiSwitch::processArpChanges(const StateDelta &delta) { VLOG(4) << "Entering " << __FUNCTION__; for (const auto& vlanDelta : delta.getVlansDelta()) { for (const auto& arpDelta : vlanDelta.getArpDelta()) { - ProcessNeighborEntryDelta(arpDelta); + processNeighborEntryDelta(arpDelta); } for (const auto& ndpDelta : vlanDelta.getNdpDelta()) { - ProcessNeighborEntryDelta(ndpDelta); + processNeighborEntryDelta(ndpDelta); } } } template -void SaiSwitch::ProcessChangedRoute(const RouterID id, +void SaiSwitch::processChangedRoute(const RouterID id, const shared_ptr &oldRoute, const shared_ptr &newRoute) { VLOG(4) << "Entering " << __FUNCTION__; @@ -686,10 +686,10 @@ void SaiSwitch::ProcessChangedRoute(const RouterID id, // 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); + processRemovedRoute(id, oldRoute); } else { try { - routeTable_->AddRoute(id, newRoute.get()); + routeTable_->addRoute(id, newRoute.get()); } catch (const SaiError &e) { LOG(ERROR) << e.what(); } @@ -697,7 +697,7 @@ void SaiSwitch::ProcessChangedRoute(const RouterID id, } template -void SaiSwitch::ProcessAddedRoute(const RouterID id, +void SaiSwitch::processAddedRoute(const RouterID id, const shared_ptr &route) { VLOG(4) << "Entering " << __FUNCTION__; @@ -710,14 +710,14 @@ void SaiSwitch::ProcessAddedRoute(const RouterID id, } try { - routeTable_->AddRoute(id, route.get()); + routeTable_->addRoute(id, route.get()); } catch (const SaiError &e) { LOG(ERROR) << e.what(); } } template -void SaiSwitch::ProcessRemovedRoute(const RouterID id, +void SaiSwitch::processRemovedRoute(const RouterID id, const shared_ptr &route) { VLOG(4) << "Entering " << __FUNCTION__; @@ -729,13 +729,13 @@ void SaiSwitch::ProcessRemovedRoute(const RouterID id, } try { - routeTable_->DeleteRoute(id, route.get()); + routeTable_->deleteRoute(id, route.get()); } catch (const SaiError &e) { LOG(ERROR) << e.what(); } } -void SaiSwitch::ProcessRemovedRoutes(const StateDelta &delta) { +void SaiSwitch::processRemovedRoutes(const StateDelta &delta) { VLOG(4) << "Entering " << __FUNCTION__; for (auto const& rtDelta : delta.getRouteTablesDelta()) { @@ -747,18 +747,18 @@ void SaiSwitch::ProcessRemovedRoutes(const StateDelta &delta) { RouterID id = rtDelta.getOld()->getID(); forEachRemoved( rtDelta.getRoutesV4Delta(), - &SaiSwitch::ProcessRemovedRoute, + &SaiSwitch::processRemovedRoute, this, id); forEachRemoved( rtDelta.getRoutesV6Delta(), - &SaiSwitch::ProcessRemovedRoute, + &SaiSwitch::processRemovedRoute, this, id); } } -void SaiSwitch::ProcessAddedChangedRoutes(const StateDelta &delta) { +void SaiSwitch::processAddedChangedRoutes(const StateDelta &delta) { VLOG(4) << "Entering " << __FUNCTION__; for (auto const& rtDelta : delta.getRouteTablesDelta()) { @@ -771,23 +771,23 @@ void SaiSwitch::ProcessAddedChangedRoutes(const StateDelta &delta) { forEachChanged( rtDelta.getRoutesV4Delta(), - &SaiSwitch::ProcessChangedRoute, - &SaiSwitch::ProcessAddedRoute, + &SaiSwitch::processChangedRoute, + &SaiSwitch::processAddedRoute, [&](SaiSwitch *, RouterID, const shared_ptr &) {}, this, id); forEachChanged( rtDelta.getRoutesV6Delta(), - &SaiSwitch::ProcessChangedRoute, - &SaiSwitch::ProcessAddedRoute, + &SaiSwitch::processChangedRoute, + &SaiSwitch::processAddedRoute, [&](SaiSwitch *, RouterID, const shared_ptr &) {}, this, id); } } -void SaiSwitch::ProcessChangedVlan(const shared_ptr &oldVlan, +void SaiSwitch::processChangedVlan(const shared_ptr &oldVlan, const shared_ptr &newVlan) { VLOG(4) << "Entering " << __FUNCTION__; @@ -805,7 +805,7 @@ void SaiSwitch::ProcessChangedVlan(const shared_ptr &oldVlan, sai_vlan_port_t vlanPort; try { - vlanPort.port_id = portTable_->GetSaiPortId(oldPortPair.first); + vlanPort.port_id = portTable_->getSaiPortId(oldPortPair.first); } catch (const SaiError &e) { LOG(ERROR) << e.what(); continue; @@ -824,7 +824,7 @@ void SaiSwitch::ProcessChangedVlan(const shared_ptr &oldVlan, sai_vlan_port_t vlanPort; try { - vlanPort.port_id = portTable_->GetSaiPortId(newPortPair.first); + vlanPort.port_id = portTable_->getSaiPortId(newPortPair.first); } catch (const SaiError &e) { LOG(ERROR) << e.what(); continue; @@ -859,7 +859,7 @@ void SaiSwitch::ProcessChangedVlan(const shared_ptr &oldVlan, } } -void SaiSwitch::ProcessAddedVlan(const shared_ptr &vlan) { +void SaiSwitch::processAddedVlan(const shared_ptr &vlan) { VLOG(4) << "Entering " << __FUNCTION__; VLOG(3) << "Creating VLAN " << (uint16_t)vlan->getID() << " with " @@ -873,7 +873,7 @@ void SaiSwitch::ProcessAddedVlan(const shared_ptr &vlan) { sai_vlan_port_t vlanPort; try { - vlanPort.port_id = portTable_->GetSaiPortId(entry.first); + vlanPort.port_id = portTable_->getSaiPortId(entry.first); } catch (const SaiError &e) { LOG(ERROR) << e.what(); continue; @@ -924,7 +924,7 @@ void SaiSwitch::ProcessAddedVlan(const shared_ptr &vlan) { auto oldVlan = vlan->clone(); warmBootCache_->fillVlanPortInfo(oldVlan.get()); - ProcessChangedVlan(oldVlan, vlan); + processChangedVlan(oldVlan, vlan); } else { LOG(WARNING) << "Vlan " << vlan->getID() << " already exists."; } @@ -947,11 +947,11 @@ void SaiSwitch::ProcessAddedVlan(const shared_ptr &vlan) { return; } - warmBootCache_->AddVlanInfo(vlan->getID(), portList); + warmBootCache_->addVlanInfo(vlan->getID(), portList); } } -void SaiSwitch::PreprocessRemovedVlan(const shared_ptr &vlan) { +void SaiSwitch::preprocessRemovedVlan(const shared_ptr &vlan) { VLOG(4) << "Entering " << __FUNCTION__; sai_status_t saiStatus = SAI_STATUS_SUCCESS; @@ -962,7 +962,7 @@ void SaiSwitch::PreprocessRemovedVlan(const shared_ptr &vlan) { sai_vlan_port_t vlanPort; try { - vlanPort.port_id = portTable_->GetSaiPortId(entry.first); + vlanPort.port_id = portTable_->getSaiPortId(entry.first); } catch (const SaiError &e) { LOG(ERROR) << e.what(); continue; @@ -984,7 +984,7 @@ void SaiSwitch::PreprocessRemovedVlan(const shared_ptr &vlan) { } } -void SaiSwitch::ProcessRemovedVlan(const shared_ptr &vlan) { +void SaiSwitch::processRemovedVlan(const shared_ptr &vlan) { VLOG(4) << "Entering " << __FUNCTION__; sai_status_t saiStatus = SAI_STATUS_SUCCESS; @@ -997,36 +997,36 @@ void SaiSwitch::ProcessRemovedVlan(const shared_ptr &vlan) { throw SaiError("Failed to remove VLAN ", vlan->getID()); } -void SaiSwitch::ProcessChangedIntf(const shared_ptr &oldIntf, +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); + intfTable_->programIntf(newIntf); } -void SaiSwitch::ProcessAddedIntf(const shared_ptr &intf) { +void SaiSwitch::processAddedIntf(const shared_ptr &intf) { VLOG(4) << "Entering " << __FUNCTION__; VLOG(2) << "adding interface " << intf->getID(); try { - intfTable_->AddIntf(intf); + intfTable_->addIntf(intf); } catch (const SaiError &e) { LOG(ERROR) << e.what(); return; } } -void SaiSwitch::ProcessRemovedIntf(const shared_ptr &intf) { +void SaiSwitch::processRemovedIntf(const shared_ptr &intf) { VLOG(4) << "Entering " << __FUNCTION__; VLOG(2) << "deleting interface " << intf->getID(); - intfTable_->DeleteIntf(intf); + intfTable_->deleteIntf(intf); } #define lockPath "sai_agent.lock" -int SaiSwitch::TryGetLock(){ +int SaiSwitch::tryGetLock(){ VLOG(4) << "Entering " << __FUNCTION__; if(lockFd != -1) @@ -1045,7 +1045,7 @@ int SaiSwitch::TryGetLock(){ return lockFd; } -void SaiSwitch::PacketRxCallback(const void *buf, +void SaiSwitch::packetRxCallback(const void *buf, sai_size_t buf_size, uint32_t attr_count, const sai_attribute_t *attr_list) { @@ -1054,10 +1054,10 @@ void SaiSwitch::PacketRxCallback(const void *buf, // when this is supported in SAI. auto* sw = SaiSwitch::instance_; - return sw->OnPacketReceived(buf, buf_size, attr_count, attr_list); + return sw->onPacketReceived(buf, buf_size, attr_count, attr_list); } -void SaiSwitch::OnPacketReceived(const void *buf, +void SaiSwitch::onPacketReceived(const void *buf, sai_size_t buf_size, uint32_t attr_count, const sai_attribute_t *attr_list) noexcept{ @@ -1081,7 +1081,7 @@ void SaiSwitch::OnPacketReceived(const void *buf, callback_->packetReceived(std::move(pkt)); } -void SaiSwitch::ReleaseLock() +void SaiSwitch::releaseLock() { VLOG(4) << "Entering " << __FUNCTION__; diff --git a/fboss/agent/hw/sai/SaiSwitch.h b/fboss/agent/hw/sai/SaiSwitch.h index 44d95e875ca2e..e2071d1cccc2c 100644 --- a/fboss/agent/hw/sai/SaiSwitch.h +++ b/fboss/agent/hw/sai/SaiSwitch.h @@ -42,11 +42,11 @@ class SaiSwitch : public HwSwitch { /* * Get default state switch is in on a cold boot */ - std::shared_ptr GetColdBootSwitchState() const; + std::shared_ptr getColdBootSwitchState() const; /* * Get state switch is in on a warm boot. */ - std::shared_ptr GetWarmBootSwitchState() const; + std::shared_ptr getWarmBootSwitchState() const; std::pair, BootType> init(Callback *callback) override; @@ -71,7 +71,7 @@ class SaiSwitch : public HwSwitch { void initialConfigApplied() override; - SaiPlatformBase *GetPlatform() const; + SaiPlatformBase *getPlatform() const; // TODO void updateStats(SwitchStats *switchStats) override; @@ -102,33 +102,33 @@ class SaiSwitch : public HwSwitch { cfg::PortSpeed getPortSpeed(PortID port) const override; cfg::PortSpeed getMaxPortSpeed(PortID port) const override; - SaiHostTable *WritableHostTable() const { + SaiHostTable *writableHostTable() const { return hostTable_.get(); } - SaiVrfTable *WritableVrfTable() const { + SaiVrfTable *writableVrfTable() const { return vrfTable_.get(); } - SaiNextHopTable *WritableNextHopTable() const { + SaiNextHopTable *writableNextHopTable() const { return nextHopTable_.get(); } - const SaiPortTable *GetPortTable() const { + const SaiPortTable *getPortTable() const { return portTable_.get(); } - const SaiIntfTable *GetIntfTable() const { + const SaiIntfTable *getIntfTable() const { return intfTable_.get(); } - const SaiHostTable *GetHostTable() const { + const SaiHostTable *getHostTable() const { return hostTable_.get(); } - const SaiVrfTable *GetVrfTable() const { + const SaiVrfTable *getVrfTable() const { return vrfTable_.get(); } - const SaiNextHopTable *GetNextHopTable() const { + const SaiNextHopTable *getNextHopTable() const { return nextHopTable_.get(); } @@ -141,52 +141,52 @@ class SaiSwitch : public HwSwitch { //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 { + const sai_object_list_t &getSaiPortList() const { return saiPortList_; } /* SAI API lists */ - sai_switch_api_t *GetSaiSwitchApi() const { + sai_switch_api_t *getSaiSwitchApi() const { return saiSwitchApi_; } - sai_vlan_api_t *GetSaiVlanApi() const { + sai_vlan_api_t *getSaiVlanApi() const { return saiVlanApi_; } - sai_port_api_t *GetSaiPortApi() const { + sai_port_api_t *getSaiPortApi() const { return saiPortApi_; } - sai_virtual_router_api_t *GetSaiVrfApi() const { + sai_virtual_router_api_t *getSaiVrfApi() const { return saiVrfApi_; } - sai_route_api_t *GetSaiRouteApi() const { + sai_route_api_t *getSaiRouteApi() const { return saiRouteApi_; } - sai_router_interface_api_t *GetSaiRouterIntfApi() const { + sai_router_interface_api_t *getSaiRouterIntfApi() const { return saiRouterIntfApi_; } - sai_neighbor_api_t *GetSaiNeighborApi() const { + sai_neighbor_api_t *getSaiNeighborApi() const { return saiNeighborApi_; } - sai_hostif_api_t *GetSaiHostInterfaceApi() const { + sai_hostif_api_t *getSaiHostInterfaceApi() const { return saiHostInterfaceApi_; } - sai_acl_api_t *GetSaiAclApi() const { + sai_acl_api_t *getSaiAclApi() const { return saiAclApi_; } - sai_next_hop_api_t *GetSaiNextHopApi() const { + sai_next_hop_api_t *getSaiNextHopApi() const { return saiNextHopApi_; } - sai_next_hop_group_api_t *GetSaiNextHopGroupApi() const { + sai_next_hop_group_api_t *getSaiNextHopGroupApi() const { return saiNextHopGroupApi_; } @@ -195,53 +195,53 @@ class SaiSwitch : public HwSwitch { SaiSwitch(SaiSwitch const &) = delete; SaiSwitch &operator=(SaiSwitch const &) = delete; - void UpdateSwitchStats(SwitchStats *switchStats); - void UpdatePortStats(PortID portID, PortStats *portStats); + void updateSwitchStats(SwitchStats *switchStats); + void updatePortStats(PortID portID, PortStats *portStats); - void UpdateIngressVlan(const std::shared_ptr &oldPort, + void updateIngressVlan(const std::shared_ptr &oldPort, const std::shared_ptr &newPort); - void ProcessChangedVlan(const std::shared_ptr &oldVlan, + 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 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, + 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 processAddedIntf(const std::shared_ptr &intf); + void processRemovedIntf(const std::shared_ptr &intf); - void Deinit(bool warm = true) const; + void deinit(bool warm = true) const; - void EcmpHashSetup(); + void ecmpHashSetup(); template - void ProcessNeighborEntryDelta(const DELTA &delta); - void ProcessArpChanges(const StateDelta &delta); + void processNeighborEntryDelta(const DELTA &delta); + void processArpChanges(const StateDelta &delta); template - void ProcessChangedRoute( + void processChangedRoute( const RouterID id, const std::shared_ptr &oldRoute, const std::shared_ptr &newRoute); template - void ProcessAddedRoute( + void processAddedRoute( const RouterID id, const std::shared_ptr &route); template - void ProcessRemovedRoute( + void processRemovedRoute( const RouterID id, const std::shared_ptr &route); - void ProcessRemovedRoutes(const StateDelta &delta); - void ProcessAddedChangedRoutes(const StateDelta &delta); + void processRemovedRoutes(const StateDelta &delta); + void processAddedChangedRoutes(const StateDelta &delta); - void UpdatePortSpeed(const std::shared_ptr &oldPort, + 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. + * SaiSwitch::onPacketReceived. */ - static void PacketRxCallback(const void *buf, + static void packetRxCallback(const void *buf, sai_size_t buf_size, uint32_t attr_count, const sai_attribute_t *attr_list); @@ -249,7 +249,7 @@ class SaiSwitch : public HwSwitch { * Private callback called by SaiSwitch::packetRxCallback. Dispatches to * callback_->packetReceived. */ - void OnPacketReceived(const void *buf, + void onPacketReceived(const void *buf, sai_size_t buf_size, uint32_t attr_count, const sai_attribute_t *attr_list) noexcept; @@ -294,8 +294,8 @@ class SaiSwitch : public HwSwitch { std::mutex lock_; int lockFd; - int TryGetLock(); - void ReleaseLock(); + int tryGetLock(); + void releaseLock(); }; }} // facebook::fboss diff --git a/fboss/agent/hw/sai/SaiVrf.cpp b/fboss/agent/hw/sai/SaiVrf.cpp index f0f821107343c..576837ef5821b 100644 --- a/fboss/agent/hw/sai/SaiVrf.cpp +++ b/fboss/agent/hw/sai/SaiVrf.cpp @@ -20,7 +20,7 @@ SaiVrf::SaiVrf(const SaiSwitch *hw, RouterID fbossVrfId) VLOG(4) << "Entering " << __FUNCTION__; - saiVirtualRouterApi_ = hw->GetSaiVrfApi(); + saiVirtualRouterApi_ = hw->getSaiVrfApi(); } SaiVrf::~SaiVrf() { @@ -31,7 +31,7 @@ SaiVrf::~SaiVrf() { } } -sai_object_id_t SaiVrf::GetSaiVrfId() const { +sai_object_id_t SaiVrf::getSaiVrfId() const { if (saiVrfId_ == SAI_NULL_OBJECT_ID) { throw SaiError("Attempt to get SAI ID of the VRF ", fbossVrfId_, @@ -41,7 +41,7 @@ sai_object_id_t SaiVrf::GetSaiVrfId() const { return saiVrfId_; } -void SaiVrf::Program() { +void SaiVrf::program() { VLOG(4) << "Entering " << __FUNCTION__; diff --git a/fboss/agent/hw/sai/SaiVrf.h b/fboss/agent/hw/sai/SaiVrf.h index 7cc42d2f1433d..b5611cca64e8d 100644 --- a/fboss/agent/hw/sai/SaiVrf.h +++ b/fboss/agent/hw/sai/SaiVrf.h @@ -27,7 +27,7 @@ class SaiVrf { * 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 + * program() will be called soon after construction, and any * actual initialization logic has to be performed there. */ explicit SaiVrf(const SaiSwitch *hw, RouterID fbossVrfId); @@ -39,18 +39,18 @@ class SaiVrf { virtual ~SaiVrf(); /** * Gets Sai Vrf ID of sai_object_id_t type. - * Should be called after Program() call. + * 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; + sai_object_id_t getSaiVrfId() const; /** * Gets FBOSS Vrf ID of RouterID type. * * @return FBOSS Vrf ID of RouterID type */ - RouterID GetFbossVrfId() const { + RouterID getFbossVrfId() const { return fbossVrfId_; } @@ -60,7 +60,7 @@ class SaiVrf { * * @return none */ - void Program(); + void program(); private: // no copy or assignment diff --git a/fboss/agent/hw/sai/SaiVrfTable.cpp b/fboss/agent/hw/sai/SaiVrfTable.cpp index 87c7aebc3b746..f7b95b1cddfb1 100644 --- a/fboss/agent/hw/sai/SaiVrfTable.cpp +++ b/fboss/agent/hw/sai/SaiVrfTable.cpp @@ -23,7 +23,7 @@ SaiVrfTable::~SaiVrfTable() { VLOG(4) << "Entering " << __FUNCTION__; } -sai_object_id_t SaiVrfTable::GetSaiVrfId(RouterID fbossVrfId) const { +sai_object_id_t SaiVrfTable::getSaiVrfId(RouterID fbossVrfId) const { VLOG(4) << "Entering " << __FUNCTION__; auto iter = vrfs_.find(fbossVrfId); @@ -32,10 +32,10 @@ sai_object_id_t SaiVrfTable::GetSaiVrfId(RouterID fbossVrfId) const { throw SaiError("Cannot find SaiVrf object for vrf: ", fbossVrfId); } - return iter->second.first->GetSaiVrfId(); + return iter->second.first->getSaiVrfId(); } -SaiVrf* SaiVrfTable::IncRefOrCreateSaiVrf(RouterID fbossVrfId) { +SaiVrf* SaiVrfTable::incRefOrCreateSaiVrf(RouterID fbossVrfId) { VLOG(4) << "Entering " << __FUNCTION__; auto ret = vrfs_.emplace(fbossVrfId, std::make_pair(nullptr, 1)); @@ -58,7 +58,7 @@ SaiVrf* SaiVrfTable::IncRefOrCreateSaiVrf(RouterID fbossVrfId) { return vrfPtr; } -SaiVrf* SaiVrfTable::DerefSaiVrf(RouterID fbossVrfId) noexcept { +SaiVrf* SaiVrfTable::derefSaiVrf(RouterID fbossVrfId) noexcept { VLOG(4) << "Entering " << __FUNCTION__; auto iter = vrfs_.find(fbossVrfId); diff --git a/fboss/agent/hw/sai/SaiVrfTable.h b/fboss/agent/hw/sai/SaiVrfTable.h index 7f2303ee83e85..c7cfe130fee29 100644 --- a/fboss/agent/hw/sai/SaiVrfTable.h +++ b/fboss/agent/hw/sai/SaiVrfTable.h @@ -33,7 +33,7 @@ class SaiVrfTable { * * @return Sai Vrf ID of sai_object_id_t type */ - sai_object_id_t GetSaiVrfId(RouterID fbossVrfId) const; + sai_object_id_t getSaiVrfId(RouterID fbossVrfId) const; /* * The following functions will modify the object. They rely on the global @@ -49,7 +49,7 @@ class SaiVrfTable { * * @return The SaiVrf pointer just created or found. */ - SaiVrf* IncRefOrCreateSaiVrf(RouterID fbossVrfId); + SaiVrf* incRefOrCreateSaiVrf(RouterID fbossVrfId); /** * Decrease an existing SaiVrf entry's reference counter by 1. @@ -61,7 +61,7 @@ class SaiVrfTable { * decreased by 1, but the object is still valid as it is * still referred in somewhere else */ - SaiVrf *DerefSaiVrf(RouterID fbossVrfId) noexcept; + SaiVrf *derefSaiVrf(RouterID fbossVrfId) noexcept; private: typedef std::pair, uint32_t> VrfMapNode; diff --git a/fboss/agent/hw/sai/SaiWarmBootCache.cpp b/fboss/agent/hw/sai/SaiWarmBootCache.cpp index 1143434394a30..da9924fa397e0 100644 --- a/fboss/agent/hw/sai/SaiWarmBootCache.cpp +++ b/fboss/agent/hw/sai/SaiWarmBootCache.cpp @@ -65,7 +65,7 @@ shared_ptr SaiWarmBootCache::reconstructInterfaceMap() const { saiAttr[2].id = SAI_ROUTER_INTERFACE_TYPE_PORT; - hw_->GetSaiRouterIntfApi()->get_router_interface_attribute(saiIntf, + hw_->getSaiRouterIntfApi()->get_router_interface_attribute(saiIntf, sizeof(saiAttr)/sizeof(sai_attribute_t), saiAttr); /* @@ -96,7 +96,7 @@ shared_ptr SaiWarmBootCache::reconstructVlanMap() const { PortID portId {0}; try { - portId = hw_->GetPortTable()->GetPortId(port.port_id); + portId = hw_->getPortTable()->getPortId(port.port_id); } catch (const SaiError &e) { LOG(ERROR) << e.what(); continue; @@ -124,10 +124,10 @@ shared_ptr SaiWarmBootCache::reconstructVlanMap() const { return vlans; } -void SaiWarmBootCache::Populate() { +void SaiWarmBootCache::populate() { } -void SaiWarmBootCache::AddVlanInfo(VlanID vlan, +void SaiWarmBootCache::addVlanInfo(VlanID vlan, const vector &ports) { vlan2VlanInfo_.insert(make_pair(vlan, VlanInfo(vlan, ports))); @@ -147,7 +147,7 @@ bool SaiWarmBootCache::fillVlanPortInfo(Vlan *vlan) { PortID portId {0}; try { - portId = hw_->GetPortTable()->GetPortId(port.port_id); + portId = hw_->getPortTable()->getPortId(port.port_id); } catch (const SaiError &e) { LOG(ERROR) << e.what(); continue; @@ -186,14 +186,14 @@ void SaiWarmBootCache::clear() { std::get<1>(vrfPfxAndRoute.first) << "/" << std::get<2>(vrfPfxAndRoute.first); - hw_->GetSaiRouteApi()->remove_route(&vrfPfxAndRoute.second); + 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); + hw_->getSaiNeighborApi()->remove_neighbor_entry(&vrfIpAndHost.second); } vrfIp2Host_.clear(); @@ -203,7 +203,7 @@ void SaiWarmBootCache::clear() { VLOG(1) <<"Deletingl3 interface for vlan: " << vlanMacAndIntf.first.first <<" and mac : " << vlanMacAndIntf.first.second; - hw_->GetSaiRouterIntfApi()->remove_router_interface(vlanMacAndIntf.second); + hw_->getSaiRouterIntfApi()->remove_router_interface(vlanMacAndIntf.second); } vlanAndMac2Intf_.clear(); @@ -213,7 +213,7 @@ void SaiWarmBootCache::clear() { vlanItr != vlan2VlanInfo_.end();) { VLOG(1) << "Deleting vlan : " << vlanItr->first; - hw_->GetSaiVlanApi()->remove_vlan(vlanItr->first); + hw_->getSaiVlanApi()->remove_vlan(vlanItr->first); vlanItr = vlan2VlanInfo_.erase(vlanItr); } } diff --git a/fboss/agent/hw/sai/SaiWarmBootCache.h b/fboss/agent/hw/sai/SaiWarmBootCache.h index 9e4747c4b04eb..b0d04bf7dc51c 100644 --- a/fboss/agent/hw/sai/SaiWarmBootCache.h +++ b/fboss/agent/hw/sai/SaiWarmBootCache.h @@ -33,7 +33,7 @@ class SaiWarmBootCache { public: explicit SaiWarmBootCache(const SaiSwitch *hw); - void Populate(); + void populate(); struct VlanInfo { VlanInfo(VlanID vlan, const std::vector &ports): @@ -48,7 +48,7 @@ class SaiWarmBootCache { std::vector ports_; }; - void AddVlanInfo(VlanID vlan, const std::vector &ports); + void addVlanInfo(VlanID vlan, const std::vector &ports); typedef sai_object_id_t EcmpEgressId; typedef sai_object_id_t EgressId; diff --git a/fboss/agent/platforms/sai/SaiPlatform.cpp b/fboss/agent/platforms/sai/SaiPlatform.cpp index 7545f859f5490..8e3b001a73153 100644 --- a/fboss/agent/platforms/sai/SaiPlatform.cpp +++ b/fboss/agent/platforms/sai/SaiPlatform.cpp @@ -69,7 +69,7 @@ SaiPlatform::InitPortMap SaiPlatform::initPorts() { InitPortMap ports; - const sai_object_list_t &pl = hw_->GetSaiPortList(); + const sai_object_list_t &pl = hw_->getSaiPortList(); for (uint32_t nPort = 0; nPort < pl.count; ++nPort) { PortID portID(nPort); diff --git a/fboss/agent/platforms/sai/SaiPlatform.h b/fboss/agent/platforms/sai/SaiPlatform.h index 01f4dddef72d2..582642839d3c6 100644 --- a/fboss/agent/platforms/sai/SaiPlatform.h +++ b/fboss/agent/platforms/sai/SaiPlatform.h @@ -39,7 +39,7 @@ class SaiPlatform : public SaiPlatformBase { std::string getPersistentStateDir() const override; InitPortMap initPorts() override; - SaiPortMap &GetPortMap() { + SaiPortMap &getPortMap() { return ports_; } diff --git a/fboss/agent/platforms/sai/SaiPort.cpp b/fboss/agent/platforms/sai/SaiPort.cpp index eb95d6bfc48f9..fb1bc844e2660 100644 --- a/fboss/agent/platforms/sai/SaiPort.cpp +++ b/fboss/agent/platforms/sai/SaiPort.cpp @@ -17,7 +17,7 @@ SaiPort::SaiPort(PortID id) : id_(id) { } -void SaiPort::SetPort(SaiPortBase *port) { +void SaiPort::setPort(SaiPortBase *port) { port_ = port; } diff --git a/fboss/agent/platforms/sai/SaiPort.h b/fboss/agent/platforms/sai/SaiPort.h index 656b122dfff85..c34e1617053bd 100644 --- a/fboss/agent/platforms/sai/SaiPort.h +++ b/fboss/agent/platforms/sai/SaiPort.h @@ -24,8 +24,8 @@ class SaiPort : public SaiPlatformPort { return id_; } - void SetPort(SaiPortBase *port) override; - SaiPortBase *GetPort() const override { + void setPort(SaiPortBase *port) override; + SaiPortBase *getPort() const override { return port_; } From bceb906251a8d633c8a0f25648dd2670903c4237 Mon Sep 17 00:00:00 2001 From: Vitaliy Senchyshyn Date: Fri, 1 Jul 2016 16:35:08 +0300 Subject: [PATCH 23/24] Updated getdeps.sh for SAI needs --- fboss/github/getdeps_SAI.sh | 80 +++++++++++++++++++++++++++++++++++++ 1 file changed, 80 insertions(+) create mode 100755 fboss/github/getdeps_SAI.sh 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 +) From 049dde084d9547b1f690055cd2477f43e8a85bbf Mon Sep 17 00:00:00 2001 From: Vitaliy Senchyshyn Date: Mon, 4 Jul 2016 17:08:13 +0300 Subject: [PATCH 24/24] Added default getters for service method table --- fboss/agent/hw/sai/SaiSwitch.cpp | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) diff --git a/fboss/agent/hw/sai/SaiSwitch.cpp b/fboss/agent/hw/sai/SaiSwitch.cpp index fb9611f40ef03..bc13c98f771e4 100644 --- a/fboss/agent/hw/sai/SaiSwitch.cpp +++ b/fboss/agent/hw/sai/SaiSwitch.cpp @@ -77,6 +77,20 @@ using facebook::fboss::DeltaFunctions::forEachRemoved; 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; @@ -98,6 +112,9 @@ SaiSwitch::SaiSwitch(SaiPlatformBase *platform) , 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);