diff --git a/Dockerfile.dev b/Dockerfile.dev index 6fac4204f..a8e647aee 100644 --- a/Dockerfile.dev +++ b/Dockerfile.dev @@ -18,3 +18,6 @@ USER $USER_NAME RUN (test "$GIT_GLOBAL_NAME" = "" || git config --global user.name "$GIT_GLOBAL_NAME") && \ (test "$GIT_GLOBAL_EMAIL" = "" || git config --global user.email "$GIT_GLOBAL_EMAIL") && \ (test "$GIT_GLOBAL_EDITOR" = "" || git config --global code.editor "$GIT_GLOBAL_EDITOR") + +RUN sudo pip3 install scapy +RUN sudo apt-get install tcpdump diff --git a/stratum/hal/bin/nikss/chassis_config.pb.txt b/stratum/hal/bin/nikss/chassis_config.pb.txt new file mode 100644 index 000000000..c275e34b9 --- /dev/null +++ b/stratum/hal/bin/nikss/chassis_config.pb.txt @@ -0,0 +1,34 @@ +description: "Sample config for nikss" +chassis { + platform: PLT_P4_SOFT_SWITCH + name: "nikss" +} +nodes { + id: 1 + slot: 1 + index: 1 +} +singleton_ports { + id: 1 + name: "veth1" + slot: 1 + port: 1 + channel: 1 + speed_bps: 100000000000 + config_params { + admin_state: ADMIN_STATE_ENABLED + } + node: 1 +} +singleton_ports { + id: 2 + name: "veth3" + slot: 1 + port: 2 + channel: 1 + speed_bps: 100000000000 + config_params { + admin_state: ADMIN_STATE_ENABLED + } + node: 1 +} diff --git a/stratum/hal/bin/nikss/main.cc b/stratum/hal/bin/nikss/main.cc index 3b485574f..a857da77c 100644 --- a/stratum/hal/bin/nikss/main.cc +++ b/stratum/hal/bin/nikss/main.cc @@ -35,7 +35,7 @@ ::util::Status Main(int argc, char* argv[]) { {node_id, nikss_node.get()}, }; auto nikss_chassis_manager = - NikssChassisManager::CreateInstance(phal_sim); + NikssChassisManager::CreateInstance(phal_sim, nikss_wrapper); auto nikss_switch = NikssSwitch::CreateInstance( phal_sim, nikss_chassis_manager.get(), node_id_to_nikss_node); diff --git a/stratum/hal/lib/nikss/BUILD b/stratum/hal/lib/nikss/BUILD index 5d45c870b..40a2b88f5 100644 --- a/stratum/hal/lib/nikss/BUILD +++ b/stratum/hal/lib/nikss/BUILD @@ -41,6 +41,7 @@ stratum_cc_library( "//stratum/glue/status:status_macros", "//stratum/hal/lib/common:phal_interface", "//stratum/hal/lib/common:switch_interface", + "//stratum/hal/lib/nikss:nikss_interface", "//stratum/lib:constants", "//stratum/lib:macros", "@com_google_absl//absl/base:core_headers", @@ -95,6 +96,7 @@ stratum_cc_library( hdrs = ["nikss_node.h"], deps = [ ":nikss_interface", + ":nikss_chassis_manager", "//stratum/glue:integral_types", "//stratum/glue:logging", "//stratum/glue/status:status_macros", diff --git a/stratum/hal/lib/nikss/nikss_chassis_manager.cc b/stratum/hal/lib/nikss/nikss_chassis_manager.cc index a302dbf37..42905dc8e 100644 --- a/stratum/hal/lib/nikss/nikss_chassis_manager.cc +++ b/stratum/hal/lib/nikss/nikss_chassis_manager.cc @@ -1,21 +1,64 @@ #include "stratum/hal/lib/nikss/nikss_chassis_manager.h" +#include "absl/synchronization/mutex.h" +#include "stratum/glue/gtl/map_util.h" +#include + namespace stratum { namespace hal { namespace nikss { +ABSL_CONST_INIT absl::Mutex chassis_lock(absl::kConstInit); + NikssChassisManager::NikssChassisManager( - PhalInterface* phal_interface) + PhalInterface* phal_interface, NikssInterface* nikss_interface) : initialized_(false), - phal_interface_(phal_interface) { + phal_interface_(phal_interface), + nikss_interface_(ABSL_DIE_IF_NULL(nikss_interface)) { } NikssChassisManager::~NikssChassisManager() = default; +namespace { + +::util::Status AddPortHelper(NikssInterface* nikss_interface_, uint64 node_id, const std::string& port_name) { + LOG(INFO) << "Adding port '" << port_name << "' to node " << node_id; + RETURN_IF_ERROR(nikss_interface_->AddPort(node_id, port_name)); + return ::util::OkStatus(); +} + +} + std::unique_ptr NikssChassisManager::CreateInstance( - PhalInterface* phal_interface) { + PhalInterface* phal_interface, NikssInterface* nikss_interface) { return absl::WrapUnique( - new NikssChassisManager(phal_interface)); + new NikssChassisManager(phal_interface, nikss_interface)); +} + +::util::Status NikssChassisManager::PushChassisConfig( + const ChassisConfig& config) { + ::util::Status status = ::util::OkStatus(); // errors to keep track of. + + std::map> chassis_config; + + for (const auto& singleton_port : config.singleton_ports()) { + PortConfig port = { + .port_id = singleton_port.id(), + .name = singleton_port.name(), + .admin_state = singleton_port.config_params().admin_state(), + }; + + auto node_id = singleton_port.node(); + chassis_config[node_id][port.port_id] = port; + } + + chassis_config_ = chassis_config; + + return status; +} + +::util::StatusOr>> NikssChassisManager::GetPortConfig() const { + return chassis_config_; } } // namespace nikss diff --git a/stratum/hal/lib/nikss/nikss_chassis_manager.h b/stratum/hal/lib/nikss/nikss_chassis_manager.h index e3c516776..c4b621e8e 100644 --- a/stratum/hal/lib/nikss/nikss_chassis_manager.h +++ b/stratum/hal/lib/nikss/nikss_chassis_manager.h @@ -5,6 +5,7 @@ #include "absl/synchronization/mutex.h" #include "stratum/hal/lib/common/phal_interface.h" +#include "stratum/hal/lib/nikss/nikss_interface.h" namespace stratum { namespace hal { @@ -17,9 +18,20 @@ class NikssChassisManager { public: virtual ~NikssChassisManager(); + struct PortConfig { + uint32 port_id; + std::string name; + AdminState admin_state; + }; + + virtual ::util::Status PushChassisConfig(const ChassisConfig& config) + EXCLUSIVE_LOCKS_REQUIRED(chassis_lock); + + virtual ::util::StatusOr>> GetPortConfig() const; + // Factory function for creating the instance of the class. static std::unique_ptr CreateInstance( - PhalInterface* phal_interface); + PhalInterface* phal_interface, NikssInterface* nikss_interface); // NikssChassisManager is neither copyable nor movable. NikssChassisManager(const NikssChassisManager&) = delete; @@ -30,13 +42,21 @@ class NikssChassisManager { private: // Private constructor. Use CreateInstance() to create an instance of this // class. - NikssChassisManager(PhalInterface* phal_interface); + NikssChassisManager(PhalInterface* phal_interface, NikssInterface* nikss_interface); + + ::util::Status AddPortHelper(NikssInterface* nikss_interface_, uint64 node_id, + const std::string& port_name); + + std::map> chassis_config_; bool initialized_ GUARDED_BY(chassis_lock); // Pointer to a PhalInterface implementation. PhalInterface* phal_interface_; // not owned by this class. + // Pointer to a NikssInterface implementation that wraps all the SDE calls. + // Not owned by this class. + NikssInterface* nikss_interface_ = nullptr; }; } // namespace nikss diff --git a/stratum/hal/lib/nikss/nikss_interface.h b/stratum/hal/lib/nikss/nikss_interface.h index c3c410635..ec41ab4bf 100644 --- a/stratum/hal/lib/nikss/nikss_interface.h +++ b/stratum/hal/lib/nikss/nikss_interface.h @@ -14,6 +14,9 @@ class NikssInterface { virtual ::util::Status AddPipeline(int pipeline_id, const std::string filepath) = 0; + // Add a new port with the given parameters. + virtual ::util::Status AddPort(int pipeline_id, const std::string& port_name) = 0; + protected: // Default constructor. To be called by the Mock class instance only. NikssInterface() {} diff --git a/stratum/hal/lib/nikss/nikss_node.cc b/stratum/hal/lib/nikss/nikss_node.cc index a87d3deff..400368c57 100644 --- a/stratum/hal/lib/nikss/nikss_node.cc +++ b/stratum/hal/lib/nikss/nikss_node.cc @@ -29,10 +29,11 @@ std::unique_ptr NikssNode::CreateInstance( } ::util::Status NikssNode::PushForwardingPipelineConfig( - const ::p4::v1::ForwardingPipelineConfig& config) { + const ::p4::v1::ForwardingPipelineConfig& config, + std::map> chassis_config) { // SaveForwardingPipelineConfig + CommitForwardingPipelineConfig RETURN_IF_ERROR(SaveForwardingPipelineConfig(config)); - return CommitForwardingPipelineConfig(); + return CommitForwardingPipelineConfig(chassis_config); } ::util::Status NikssNode::SaveForwardingPipelineConfig( @@ -41,8 +42,18 @@ ::util::Status NikssNode::SaveForwardingPipelineConfig( return ::util::OkStatus(); } -::util::Status NikssNode::CommitForwardingPipelineConfig() { +::util::Status NikssNode::CommitForwardingPipelineConfig(std::map> chassis_config) { + RETURN_IF_ERROR(nikss_interface_->AddPipeline(node_id_, config_.p4_device_config())); + + for (auto it = chassis_config[node_id_].begin(); it != chassis_config[node_id_].end(); it++) { + uint32 key = it->first; + NikssChassisManager::PortConfig config = it->second; + LOG(INFO) << "Adding new port with name " << config.name << "."; + RETURN_IF_ERROR(nikss_interface_->AddPort(node_id_, config.name)); + } + return ::util::OkStatus(); } diff --git a/stratum/hal/lib/nikss/nikss_node.h b/stratum/hal/lib/nikss/nikss_node.h index f5b7af8c2..5fa020de8 100644 --- a/stratum/hal/lib/nikss/nikss_node.h +++ b/stratum/hal/lib/nikss/nikss_node.h @@ -8,6 +8,7 @@ #include "stratum/glue/status/status.h" #include "stratum/hal/lib/nikss/nikss_interface.h" +#include "stratum/hal/lib/nikss/nikss_chassis_manager.h" namespace stratum { namespace hal { @@ -18,10 +19,12 @@ class NikssNode { virtual ~NikssNode(); virtual ::util::Status PushForwardingPipelineConfig( - const ::p4::v1::ForwardingPipelineConfig& config); + const ::p4::v1::ForwardingPipelineConfig& config, + std::map> chassis_config); virtual ::util::Status SaveForwardingPipelineConfig( const ::p4::v1::ForwardingPipelineConfig& config) LOCKS_EXCLUDED(lock_); - virtual ::util::Status CommitForwardingPipelineConfig() LOCKS_EXCLUDED(lock_); + virtual ::util::Status CommitForwardingPipelineConfig( + std::map> chassis_config) LOCKS_EXCLUDED(lock_); virtual ::util::Status VerifyForwardingPipelineConfig( const ::p4::v1::ForwardingPipelineConfig& config) const; diff --git a/stratum/hal/lib/nikss/nikss_switch.cc b/stratum/hal/lib/nikss/nikss_switch.cc index b406e79cb..e5808c22e 100644 --- a/stratum/hal/lib/nikss/nikss_switch.cc +++ b/stratum/hal/lib/nikss/nikss_switch.cc @@ -25,6 +25,8 @@ NikssSwitch::NikssSwitch(PhalInterface* phal_interface, NikssSwitch::~NikssSwitch() {} ::util::Status NikssSwitch::PushChassisConfig(const ChassisConfig& config) { + LOG(INFO) << "Pushing chassis config"; + RETURN_IF_ERROR(nikss_chassis_manager_->PushChassisConfig(config)); return ::util::OkStatus(); } @@ -38,7 +40,9 @@ ::util::Status NikssSwitch::PushForwardingPipelineConfig( LOG(INFO) << "Pushing P4-based forwarding pipeline to NIKSS"; ASSIGN_OR_RETURN(auto* node, GetNikssNodeFromNodeId(node_id)); - RETURN_IF_ERROR(node->PushForwardingPipelineConfig(config)); + ASSIGN_OR_RETURN(auto chassis_config, nikss_chassis_manager_->GetPortConfig()); + + RETURN_IF_ERROR(node->PushForwardingPipelineConfig(config, chassis_config)); LOG(INFO) << "P4-based forwarding pipeline config pushed successfully to " << "node with ID " << node_id << "."; @@ -64,9 +68,13 @@ ::util::Status NikssSwitch::Shutdown() { return ::util::OkStatus(); } -::util::Status NikssSwitch::Freeze() { return ::util::OkStatus(); } +::util::Status NikssSwitch::Freeze() { + return ::util::OkStatus(); +} -::util::Status NikssSwitch::Unfreeze() { return ::util::OkStatus(); } +::util::Status NikssSwitch::Unfreeze() { + return ::util::OkStatus(); +} ::util::Status NikssSwitch::WriteForwardingEntries( const ::p4::v1::WriteRequest& req, std::vector<::util::Status>* results) { diff --git a/stratum/hal/lib/nikss/nikss_wrapper.cc b/stratum/hal/lib/nikss/nikss_wrapper.cc index 654ec0d7e..db7b128ce 100644 --- a/stratum/hal/lib/nikss/nikss_wrapper.cc +++ b/stratum/hal/lib/nikss/nikss_wrapper.cc @@ -13,8 +13,8 @@ #include "stratum/lib/utils.h" #include "stratum/lib/macros.h" -#include -#include +#include "nikss/nikss.h" +#include "nikss/nikss_pipeline.h" // A macro for simplify checking the return value of NIKSS API. // For now, we always return ERR_INTERNAL. @@ -38,9 +38,37 @@ ABSL_CONST_INIT absl::Mutex NikssWrapper::init_lock_(absl::kConstInit); NikssWrapper::NikssWrapper() {} +::util::Status NikssWrapper::AddPort(int pipeline_id, + const std::string& port_name) { + auto ctx = absl::make_unique(); + nikss_context_init(ctx.get()); + nikss_context_set_pipeline(ctx.get(), static_cast(pipeline_id)); + + int port_id = -1; + LOG(INFO) << "Adding port " << port_name << " to pipeline " << pipeline_id << "."; + RETURN_IF_NIKSS_ERROR(nikss_pipeline_add_port(ctx.get(), port_name.c_str(), &port_id)); + LOG(INFO) << "Port added with port_id=" << port_id << "."; + nikss_context_free(ctx.get()); + + return ::util::OkStatus(); +} + +::util::Status NikssWrapper::DelPort(int pipeline_id, + const std::string& port_name) { + auto ctx = absl::make_unique(); + nikss_context_init(ctx.get()); + nikss_context_set_pipeline(ctx.get(), static_cast(pipeline_id)); + + RETURN_IF_NIKSS_ERROR(nikss_pipeline_del_port(ctx.get(), port_name.c_str())); + + nikss_context_free(ctx.get()); + + return ::util::OkStatus(); +} + ::util::Status NikssWrapper::AddPipeline(int pipeline_id, const std::string bpf_obj) { - std::string tmp_filepath = "/tmp/stratum_bpf.o"; + std::string tmp_filepath = "/etc/stratum/bpf.o"; // FIXME: nikss currently doesn't support loading BPF programs from memory. // So, we save it to the disk first and let NIKSS load it from the disk. RETURN_IF_ERROR(WriteStringToFile(bpf_obj, tmp_filepath)); @@ -48,15 +76,18 @@ ::util::Status NikssWrapper::AddPipeline(int pipeline_id, auto ctx = absl::make_unique(); nikss_context_init(ctx.get()); nikss_context_set_pipeline(ctx.get(), static_cast(pipeline_id)); - if (nikss_pipeline_exists(ctx.get())) { LOG(INFO) << "NIKSS pipeline already exists, re-pushing is not supported yet."; return ::util::OkStatus(); } + // FIXME: file is not removed if the load() fails RETURN_IF_NIKSS_ERROR(nikss_pipeline_load(ctx.get(), tmp_filepath.c_str())); + RemoveFile(tmp_filepath); + nikss_context_free(ctx.get()); + return ::util::OkStatus(); } diff --git a/stratum/hal/lib/nikss/nikss_wrapper.h b/stratum/hal/lib/nikss/nikss_wrapper.h index e7d20cf7e..3ce4d2ac1 100644 --- a/stratum/hal/lib/nikss/nikss_wrapper.h +++ b/stratum/hal/lib/nikss/nikss_wrapper.h @@ -16,6 +16,10 @@ namespace nikss { class NikssWrapper : public NikssInterface { public: // NikssInterface public methods. + ::util::Status AddPort(int pipeline_id, + const std::string& port_name); + ::util::Status DelPort(int pipeline_id, + const std::string& port_name); ::util::Status AddPipeline(int pipeline_id, const std::string filepath) override;