[weave] Add Thread WARM support
Add functionality to support addressing and routing for Thread networks
to WARM.
Bug: 57904
Test: fx test weavestack-adaptation-unittests
Change-Id: I87ac2514eb11240e35358f5ef87a6cda2fc670e6
Reviewed-on: https://fuchsia-review.googlesource.com/c/fuchsia/+/414744
Commit-Queue: Jason Graffius <jgraff@google.com>
Testability-Review: Jiaming (Charlie) Wang <jiamingw@google.com>
Reviewed-by: Prashanth Swaminathan <prashanthsw@google.com>
diff --git a/src/connectivity/weave/adaptation/tests/BUILD.gn b/src/connectivity/weave/adaptation/tests/BUILD.gn
index 61cd961..4e05285 100644
--- a/src/connectivity/weave/adaptation/tests/BUILD.gn
+++ b/src/connectivity/weave/adaptation/tests/BUILD.gn
@@ -49,6 +49,12 @@
{
name = "weave_platform_manager_unittests"
},
+ {
+ log_settings = {
+ max_severity = "ERROR"
+ }
+ name = "weave_warm_platform_support_unittests"
+ },
]
deps = [
":config",
@@ -60,6 +66,7 @@
":weave_connectivity_manager_unittests",
":weave_platform_manager_unittests",
":weave_thread_stack_manager_unittests",
+ ":weave_warm_platform_support_unittests",
]
resources = [
{
@@ -159,6 +166,13 @@
deps = [ ":common_test_deps" ]
}
+executable("weave_warm_platform_support_unittests") {
+ testonly = true
+ output_name = "weave_warm_platform_support_unittests"
+ sources = [ "warm_unittests.cpp" ]
+ deps = [ ":common_test_deps" ]
+}
+
group("common_test_deps") {
testonly = true
public_deps = [
diff --git a/src/connectivity/weave/adaptation/tests/meta/weave_warm_platform_support_unittests.cmx b/src/connectivity/weave/adaptation/tests/meta/weave_warm_platform_support_unittests.cmx
new file mode 100644
index 0000000..1c2e268
--- /dev/null
+++ b/src/connectivity/weave/adaptation/tests/meta/weave_warm_platform_support_unittests.cmx
@@ -0,0 +1,10 @@
+{
+ "program": {
+ "binary": "test/weave_warm_platform_support_unittests"
+ },
+ "sandbox": {
+ "services": [
+ "fuchsia.logger.LogSink"
+ ]
+ }
+}
diff --git a/src/connectivity/weave/adaptation/tests/warm_unittests.cpp b/src/connectivity/weave/adaptation/tests/warm_unittests.cpp
new file mode 100644
index 0000000..b1c445f
--- /dev/null
+++ b/src/connectivity/weave/adaptation/tests/warm_unittests.cpp
@@ -0,0 +1,658 @@
+
+// Copyright 2020 The Fuchsia Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include <fuchsia/net/cpp/fidl.h>
+#include <fuchsia/net/stack/cpp/fidl_test_base.h>
+#include <lib/fidl/cpp/binding_set.h>
+#include <lib/fit/function.h>
+#include <lib/sys/cpp/testing/component_context_provider.h>
+#include <lib/syslog/cpp/macros.h>
+
+#include <algorithm>
+#include <memory>
+#include <vector>
+
+#include <gtest/gtest.h>
+
+// clang-format off
+#include <Weave/DeviceLayer/internal/WeaveDeviceLayerInternal.h>
+#include <Weave/DeviceLayer/ConnectivityManager.h>
+#include <Weave/DeviceLayer/ThreadStackManager.h>
+#include <Warm/Warm.h>
+// clang-format on
+
+#include "src/connectivity/weave/adaptation/thread_stack_manager_delegate_impl.h"
+#include "weave_test_fixture.h"
+
+namespace nl {
+namespace Weave {
+namespace Warm {
+namespace Platform {
+namespace testing {
+
+namespace {
+using DeviceLayer::PlatformMgrImpl;
+using DeviceLayer::ThreadStackMgrImpl;
+using DeviceLayer::Internal::testing::WeaveTestFixture;
+
+using fuchsia::net::Subnet;
+using fuchsia::net::stack::AdministrativeStatus;
+using fuchsia::net::stack::ForwardingEntry;
+using fuchsia::net::stack::InterfaceInfo;
+using fuchsia::net::stack::PhysicalStatus;
+using fuchsia::net::stack::Stack_AddForwardingEntry_Response;
+using fuchsia::net::stack::Stack_AddForwardingEntry_Result;
+using fuchsia::net::stack::Stack_AddInterfaceAddress_Response;
+using fuchsia::net::stack::Stack_AddInterfaceAddress_Result;
+using fuchsia::net::stack::Stack_DelForwardingEntry_Response;
+using fuchsia::net::stack::Stack_DelForwardingEntry_Result;
+using fuchsia::net::stack::Stack_DelInterfaceAddress_Response;
+using fuchsia::net::stack::Stack_DelInterfaceAddress_Result;
+
+// This is the OpenWeave IP address, not to be confused with the similar IpAddress from Fuchsia.
+using Inet::IPAddress;
+using Inet::IPPrefix;
+
+constexpr char kTunInterfaceName[] = "weav-tun0";
+constexpr char kThreadInterfaceName[] = "thread0";
+constexpr size_t kIpAddressSize = 16;
+
+// Copies the IP address bytes in network order from a Weave Inet::IPAddress to a std::array.
+std::array<uint8_t, kIpAddressSize> WeaveIpAddressToArray(const IPAddress& addr) {
+ std::array<uint8_t, kIpAddressSize> array;
+ const uint8_t* data = reinterpret_cast<const uint8_t*>(addr.Addr);
+ std::copy(data, data + kIpAddressSize, array.begin());
+ return array;
+}
+} // namespace
+
+// Fake implementation of TSM delegate, only provides an interface name.
+class FakeThreadStackManagerDelegate : public DeviceLayer::ThreadStackManagerDelegateImpl {
+ public:
+ WEAVE_ERROR InitThreadStack() override {
+ interface_name_ = kThreadInterfaceName;
+ return WEAVE_NO_ERROR;
+ }
+
+ const std::string& GetInterfaceName() const override { return interface_name_; }
+
+ private:
+ std::string interface_name_;
+};
+
+// Fake implementation of fuchsia::net::stack::Stack that only provides the fake functionality
+// needed for WARM.
+class FakeNetStack : public fuchsia::net::stack::testing::Stack_TestBase {
+ private:
+ void NotImplemented_(const std::string& name) override { FAIL() << "Not implemented: " << name; }
+
+ // FIDL interface definitions
+
+ void ListInterfaces(ListInterfacesCallback callback) override {
+ std::vector<InterfaceInfo> result{interfaces_.size()};
+
+ for (size_t i = 0; i < result.size(); ++i) {
+ if (interfaces_[i].Clone(&result[i]) != ZX_OK) {
+ ADD_FAILURE() << "InterfaceInfo::Clone() failed.";
+ };
+ }
+
+ callback(std::move(result));
+ }
+
+ void AddInterfaceAddress(uint64_t interface_id, Subnet ifaddr,
+ AddInterfaceAddressCallback callback) override {
+ Stack_AddInterfaceAddress_Result result;
+ Stack_AddInterfaceAddress_Response response;
+
+ // Interface ID 0 is always invalid.
+ if (interface_id == 0) {
+ result.set_err(fuchsia::net::stack::Error::INVALID_ARGS);
+ callback(std::move(result));
+ return;
+ }
+
+ // Find the interface with the specified ID.
+ for (auto& interface : interfaces_) {
+ if (interface_id == interface.id) {
+ interface.properties.addresses.push_back(std::move(ifaddr));
+ result.set_response(std::move(response));
+ callback(std::move(result));
+ return;
+ }
+ }
+
+ // No interface was found with the given ID.
+ result.set_err(fuchsia::net::stack::Error::NOT_FOUND);
+ callback(std::move(result));
+ }
+
+ void DelInterfaceAddress(uint64_t interface_id, Subnet ifaddr,
+ DelInterfaceAddressCallback callback) override {
+ Stack_DelInterfaceAddress_Result result;
+ Stack_DelInterfaceAddress_Response response;
+
+ // Interface ID 0 is always invalid.
+ if (interface_id == 0) {
+ result.set_err(fuchsia::net::stack::Error::INVALID_ARGS);
+ callback(std::move(result));
+ return;
+ }
+
+ // Search the interfaces for the specified ID.
+ for (auto& interface : interfaces_) {
+ auto& addrs = interface.properties.addresses;
+ if (interface_id == interface.id) {
+ // Find the specified address.
+ auto it = std::find_if(addrs.cbegin(), addrs.cend(),
+ [&](const Subnet& addr) { return fidl::Equals(ifaddr, addr); });
+
+ // No matching address was found.
+ if (it == addrs.cend()) {
+ result.set_err(fuchsia::net::stack::Error::NOT_FOUND);
+ callback(std::move(result));
+ return;
+ }
+
+ // Remove the address.
+ addrs.erase(it);
+ result.set_response(std::move(response));
+ callback(std::move(result));
+ return;
+ }
+ }
+
+ // No interface was found with the given ID.
+ result.set_err(fuchsia::net::stack::Error::NOT_FOUND);
+ callback(std::move(result));
+ }
+
+ void AddForwardingEntry(ForwardingEntry entry, AddForwardingEntryCallback callback) override {
+ Stack_AddForwardingEntry_Result result;
+ Stack_AddForwardingEntry_Response response;
+
+ // Interface ID 0 is always invalid.
+ if (entry.destination.is_device_id() && entry.destination.device_id() == 0) {
+ result.set_err(fuchsia::net::stack::Error::INVALID_ARGS);
+ callback(std::move(result));
+ return;
+ }
+
+ // Check if there's an existing entry for this subnet.
+ auto it =
+ std::find_if(fwd_table_.cbegin(), fwd_table_.cend(), [&](const ForwardingEntry& existing) {
+ return fidl::Equals(existing.subnet, entry.subnet);
+ });
+ if (it != fwd_table_.cend()) {
+ result.set_err(fuchsia::net::stack::Error::ALREADY_EXISTS);
+ callback(std::move(result));
+ return;
+ }
+
+ // Add to the forwarding table.
+ fwd_table_.push_back(std::move(entry));
+ result.set_response(std::move(response));
+ callback(std::move(result));
+ }
+
+ void DelForwardingEntry(Subnet subnet, DelForwardingEntryCallback callback) override {
+ Stack_DelForwardingEntry_Result result;
+ Stack_DelForwardingEntry_Response response;
+
+ // Search for the entry with the given subnet.
+ auto it = std::find_if(
+ fwd_table_.cbegin(), fwd_table_.cend(),
+ [&](const ForwardingEntry& existing) { return fidl::Equals(existing.subnet, subnet); });
+ if (it == fwd_table_.cend()) {
+ result.set_err(fuchsia::net::stack::Error::NOT_FOUND);
+ callback(std::move(result));
+ return;
+ }
+
+ // Delete the entry.
+ fwd_table_.erase(it);
+ result.set_response(std::move(response));
+ callback(std::move(result));
+ }
+
+ public:
+ // Mutators, accessors, and helpers for tests.
+
+ // Add a fake interface with the given name. Does not check for duplicates.
+ FakeNetStack& AddFakeInterface(std::string name) {
+ if (name == "") {
+ ADD_FAILURE() << "Invalid name supplied in test data.";
+ }
+ interfaces_.push_back(InterfaceInfo{
+ .id = ++last_id_assigned,
+ .properties =
+ {
+ .name = std::move(name),
+ .administrative_status = AdministrativeStatus::DISABLED,
+ .physical_status = PhysicalStatus::DOWN,
+ },
+ });
+ return *this;
+ }
+
+ // Remove the fake interface with the given name. If it is not present, no change occurs.
+ FakeNetStack& RemoveFakeInterface(std::string name) {
+ auto it = std::find_if(
+ interfaces_.cbegin(), interfaces_.cend(),
+ [&](const InterfaceInfo& interface) { return interface.properties.name == name; });
+ if (it != interfaces_.cend()) {
+ interfaces_.erase(it);
+ }
+ return *this;
+ }
+
+ // Access the current interfaces.
+ const std::vector<InterfaceInfo>& interfaces() const { return interfaces_; }
+
+ // Access the current forwarding table.
+ const std::vector<ForwardingEntry>& fwd_table() const { return fwd_table_; }
+
+ // Get a pointer to an interface by name. Returns nullptr if not found.
+ const InterfaceInfo* GetInterface(const std::string& name) const {
+ auto it = std::find_if(
+ interfaces_.cbegin(), interfaces_.cend(),
+ [&](const InterfaceInfo& interface) { return interface.properties.name == name; });
+
+ if (it != interfaces_.cend()) {
+ return &(*it);
+ } else {
+ return nullptr;
+ }
+ }
+
+ // Get a pointer to the first forwarding entry that meets the given predicate. Returns nullptr if
+ // no entries met the predicate.
+ const ForwardingEntry* FindForwardingEntry(fit::function<bool(const ForwardingEntry&)> pred) {
+ auto it = std::find_if(fwd_table_.cbegin(), fwd_table_.cend(), std::move(pred));
+ if (it != fwd_table_.cend()) {
+ return &(*it);
+ } else {
+ return nullptr;
+ }
+ }
+
+ fidl::InterfaceRequestHandler<fuchsia::net::stack::Stack> GetHandler(
+ async_dispatcher_t* dispatcher) {
+ dispatcher_ = dispatcher;
+ return [this](fidl::InterfaceRequest<fuchsia::net::stack::Stack> request) {
+ binding_.Bind(std::move(request), dispatcher_);
+ };
+ }
+
+ private:
+ fidl::Binding<fuchsia::net::stack::Stack> binding_{this};
+ async_dispatcher_t* dispatcher_;
+ std::vector<InterfaceInfo> interfaces_;
+ std::vector<ForwardingEntry> fwd_table_;
+ uint64_t last_id_assigned = 0;
+};
+
+class WarmTest : public WeaveTestFixture {
+ public:
+ void SetUp() override {
+ WeaveTestFixture::SetUp();
+
+ // Initialize everything needed for the test.
+ context_provider_.service_directory_provider()->AddService(
+ fake_net_stack_.GetHandler(dispatcher()));
+ PlatformMgrImpl().SetComponentContextForProcess(context_provider_.TakeContext());
+ ThreadStackMgrImpl().SetDelegate(std::make_unique<FakeThreadStackManagerDelegate>());
+ ThreadStackMgrImpl().InitThreadStack();
+ Warm::Platform::Init(nullptr);
+
+ // Populate initial fake interfaces
+ fake_net_stack().AddFakeInterface(kTunInterfaceName);
+ fake_net_stack().AddFakeInterface(ThreadStackMgrImpl().GetInterfaceName());
+
+ RunFixtureLoop();
+ }
+
+ void TearDown() override {
+ StopFixtureLoop();
+ ThreadStackMgrImpl().SetDelegate(nullptr);
+ WeaveTestFixture::TearDown();
+ }
+
+ protected:
+ FakeNetStack& fake_net_stack() { return fake_net_stack_; }
+
+ const InterfaceInfo* GetThreadInterface() {
+ return fake_net_stack().GetInterface(ThreadStackMgrImpl().GetInterfaceName());
+ }
+
+ uint64_t GetThreadInterfaceId() {
+ const InterfaceInfo* iface = GetThreadInterface();
+ if (iface != nullptr) {
+ return iface->id;
+ } else {
+ // ID 0 is sentinel value for an invalid ID.
+ return 0;
+ }
+ }
+
+ const InterfaceInfo* GetTunnelInterface() {
+ return fake_net_stack().GetInterface(kTunInterfaceName);
+ }
+
+ uint64_t GetTunnelInterfaceId() {
+ const InterfaceInfo* iface = GetTunnelInterface();
+ if (iface != nullptr) {
+ return iface->id;
+ } else {
+ // ID 0 is sentinel value for an invalid ID.
+ return 0;
+ }
+ }
+
+ private:
+ FakeNetStack fake_net_stack_;
+ sys::testing::ComponentContextProvider context_provider_;
+};
+
+TEST_F(WarmTest, AddRemoveAddressThread) {
+ constexpr char kSubnetIp[] = "2001:0DB8:0042::";
+ constexpr uint8_t kPrefixLength = 48;
+ IPAddress addr;
+
+ // Sanity check - no addresses assigned.
+ const InterfaceInfo* iface = GetThreadInterface();
+ ASSERT_NE(iface, nullptr);
+ EXPECT_EQ(iface->properties.addresses.size(), 0u);
+
+ // Attempt to add the address.
+ ASSERT_TRUE(IPAddress::FromString(kSubnetIp, addr));
+ auto result = AddRemoveHostAddress(kInterfaceTypeThread, addr, kPrefixLength, /*add*/ true);
+ EXPECT_EQ(result, kPlatformResultSuccess);
+
+ // Confirm that it worked.
+ iface = GetThreadInterface();
+ ASSERT_NE(iface, nullptr);
+ ASSERT_EQ(iface->properties.addresses.size(), 1u);
+ ASSERT_TRUE(iface->properties.addresses[0].addr.is_ipv6());
+ EXPECT_EQ(WeaveIpAddressToArray(addr), iface->properties.addresses[0].addr.ipv6().addr);
+
+ // Attempt to remove the address.
+ result = AddRemoveHostAddress(kInterfaceTypeThread, addr, kPrefixLength, /*add*/ false);
+ EXPECT_EQ(result, kPlatformResultSuccess);
+
+ // Confirm that it worked.
+ iface = GetThreadInterface();
+ ASSERT_NE(iface, nullptr);
+ EXPECT_EQ(iface->properties.addresses.size(), 0u);
+}
+
+TEST_F(WarmTest, AddRemoveAddressTunnel) {
+ constexpr char kSubnetIp[] = "2001:0DB8:0042::";
+ constexpr uint8_t kPrefixLength = 48;
+ IPAddress addr;
+
+ // Sanity check - no addresses assigned.
+ const InterfaceInfo* iface = GetTunnelInterface();
+ ASSERT_NE(iface, nullptr);
+ EXPECT_EQ(iface->properties.addresses.size(), 0u);
+
+ // Attempt to add the address.
+ ASSERT_TRUE(IPAddress::FromString(kSubnetIp, addr));
+ auto result = AddRemoveHostAddress(kInterfaceTypeTunnel, addr, kPrefixLength, /*add*/ true);
+ EXPECT_EQ(result, kPlatformResultSuccess);
+
+ // Confirm that it worked.
+ iface = GetTunnelInterface();
+ ASSERT_NE(iface, nullptr);
+ ASSERT_EQ(iface->properties.addresses.size(), 1u);
+ ASSERT_TRUE(iface->properties.addresses[0].addr.is_ipv6());
+ EXPECT_EQ(WeaveIpAddressToArray(addr), iface->properties.addresses[0].addr.ipv6().addr);
+
+ // Attempt to remove the address.
+ result = AddRemoveHostAddress(kInterfaceTypeTunnel, addr, kPrefixLength, /*add*/ false);
+ EXPECT_EQ(result, kPlatformResultSuccess);
+
+ // Confirm that it worked.
+ iface = GetTunnelInterface();
+ ASSERT_NE(iface, nullptr);
+ EXPECT_EQ(iface->properties.addresses.size(), 0u);
+}
+
+TEST_F(WarmTest, RemoveAddressThreadNotFound) {
+ constexpr char kSubnetIp[] = "2001:0DB8:0042::";
+ constexpr uint8_t kPrefixLength = 48;
+ IPAddress addr;
+
+ // Sanity check - no addresses assigned.
+ const InterfaceInfo* iface = GetThreadInterface();
+ ASSERT_NE(iface, nullptr);
+ EXPECT_EQ(iface->properties.addresses.size(), 0u);
+
+ // Attempt to remove the address, expecting failure.
+ ASSERT_TRUE(IPAddress::FromString(kSubnetIp, addr));
+ auto result = AddRemoveHostAddress(kInterfaceTypeThread, addr, kPrefixLength, /*add*/ false);
+ EXPECT_EQ(result, kPlatformResultFailure);
+
+ // Sanity check - still no addresses assigned.
+ iface = GetThreadInterface();
+ ASSERT_NE(iface, nullptr);
+ EXPECT_EQ(iface->properties.addresses.size(), 0u);
+}
+
+TEST_F(WarmTest, RemoveAddressTunnelNotFound) {
+ constexpr char kSubnetIp[] = "2001:0DB8:0042::";
+ constexpr uint8_t kPrefixLength = 48;
+ IPAddress addr;
+
+ // Sanity check - no addresses assigned.
+ const InterfaceInfo* iface = GetTunnelInterface();
+ ASSERT_NE(iface, nullptr);
+ EXPECT_EQ(iface->properties.addresses.size(), 0u);
+
+ // Attempt to remove the address, expecting failure.
+ ASSERT_TRUE(IPAddress::FromString(kSubnetIp, addr));
+ auto result = AddRemoveHostAddress(kInterfaceTypeTunnel, addr, kPrefixLength, /*add*/ false);
+ EXPECT_EQ(result, kPlatformResultFailure);
+
+ // Sanity check - still no addresses assigned.
+ iface = GetTunnelInterface();
+ ASSERT_NE(iface, nullptr);
+ EXPECT_EQ(iface->properties.addresses.size(), 0u);
+}
+
+TEST_F(WarmTest, AddAddressThreadNoInterface) {
+ constexpr char kSubnetIp[] = "2001:0DB8:0042::";
+ constexpr uint8_t kPrefixLength = 48;
+ IPAddress addr;
+
+ fake_net_stack().RemoveFakeInterface(kThreadInterfaceName);
+
+ // Attempt to add to the inteface when there's no Thread interface. Expect failure.
+ ASSERT_TRUE(IPAddress::FromString(kSubnetIp, addr));
+ auto result = AddRemoveHostAddress(kInterfaceTypeThread, addr, kPrefixLength, /*add*/ true);
+ EXPECT_EQ(result, kPlatformResultFailure);
+}
+
+TEST_F(WarmTest, RemoveAddressThreadNoInterface) {
+ constexpr char kSubnetIp[] = "2001:0DB8:0042::";
+ constexpr uint8_t kPrefixLength = 48;
+ IPAddress addr;
+
+ fake_net_stack().RemoveFakeInterface(kThreadInterfaceName);
+
+ // Attempt to remove from the inteface when there's no Thread interface. Expect failure.
+ ASSERT_TRUE(IPAddress::FromString(kSubnetIp, addr));
+ auto result = AddRemoveHostAddress(kInterfaceTypeThread, addr, kPrefixLength, /*add*/ false);
+ EXPECT_EQ(result, kPlatformResultFailure);
+}
+
+TEST_F(WarmTest, AddAddressTunnelNoInterface) {
+ constexpr char kSubnetIp[] = "2001:0DB8:0042::";
+ constexpr uint8_t kPrefixLength = 48;
+ IPAddress addr;
+
+ fake_net_stack().RemoveFakeInterface(kTunInterfaceName);
+
+ // Attempt to add to the inteface when there's no Tunnel interface. Expect failure.
+ ASSERT_TRUE(IPAddress::FromString(kSubnetIp, addr));
+ auto result = AddRemoveHostAddress(kInterfaceTypeTunnel, addr, kPrefixLength, /*add*/ true);
+ EXPECT_EQ(result, kPlatformResultFailure);
+}
+
+TEST_F(WarmTest, RemoveAddressTunnelNoInterface) {
+ constexpr char kSubnetIp[] = "2001:0DB8:0042::";
+ constexpr uint8_t kPrefixLength = 48;
+ IPAddress addr;
+
+ fake_net_stack().RemoveFakeInterface(kTunInterfaceName);
+
+ // Attempt to remove from the inteface when there's no Tunnel interface. Expect failure.
+ ASSERT_TRUE(IPAddress::FromString(kSubnetIp, addr));
+ auto result = AddRemoveHostAddress(kInterfaceTypeTunnel, addr, kPrefixLength, /*add*/ false);
+ EXPECT_EQ(result, kPlatformResultFailure);
+}
+
+TEST_F(WarmTest, AddRemoveHostRouteThread) {
+ constexpr char kSubnetIp[] = "2001:0DB8:0042::";
+ constexpr uint8_t kPrefixLength = 48;
+ IPPrefix prefix;
+
+ ASSERT_TRUE(IPAddress::FromString(kSubnetIp, prefix.IPAddr));
+ prefix.Length = kPrefixLength;
+
+ // Sanity check - confirm no routes to the Thread interface exist.
+ uint64_t thread_iface_id = GetThreadInterfaceId();
+ ASSERT_NE(thread_iface_id, 0u);
+ const ForwardingEntry* route =
+ fake_net_stack().FindForwardingEntry([=](const ForwardingEntry& entry) {
+ return entry.destination.is_device_id() && entry.destination.device_id() == thread_iface_id;
+ });
+ ASSERT_EQ(route, nullptr);
+
+ // Attempt to add a route to the Thread interface.
+ auto result = AddRemoveHostRoute(kInterfaceTypeThread, prefix, kRoutePriorityLow, /*add*/ true);
+ EXPECT_EQ(result, kPlatformResultSuccess);
+
+ // Confirm that a route exists to the Thread interface with the given IP.
+ route = fake_net_stack().FindForwardingEntry([=](const ForwardingEntry& entry) {
+ return entry.destination.is_device_id() && entry.destination.device_id() == thread_iface_id;
+ });
+ ASSERT_NE(route, nullptr);
+ ASSERT_TRUE(route->subnet.addr.is_ipv6());
+ // Yo dawg, we heard you liked addrs, so we put an addr in your addr.
+ EXPECT_EQ(WeaveIpAddressToArray(prefix.IPAddr), route->subnet.addr.ipv6().addr);
+
+ // Remove the route to the Thread interface.
+ result = AddRemoveHostRoute(kInterfaceTypeThread, prefix, kRoutePriorityLow, /*add*/ false);
+ EXPECT_EQ(result, kPlatformResultSuccess);
+
+ // Confirm that the removal worked.
+ route = fake_net_stack().FindForwardingEntry([=](const ForwardingEntry& entry) {
+ return entry.destination.is_device_id() && entry.destination.device_id() == thread_iface_id;
+ });
+ ASSERT_EQ(route, nullptr);
+}
+
+TEST_F(WarmTest, AddRemoveHostRouteTunnel) {
+ constexpr char kSubnetIp[] = "2001:0DB8:0042::";
+ constexpr uint8_t kPrefixLength = 48;
+ IPPrefix prefix;
+
+ ASSERT_TRUE(IPAddress::FromString(kSubnetIp, prefix.IPAddr));
+ prefix.Length = kPrefixLength;
+
+ // Sanity check - confirm no routes to the Tunnel interface exist.
+ uint64_t tunnel_iface_id = GetTunnelInterfaceId();
+ ASSERT_NE(tunnel_iface_id, 0u);
+ const ForwardingEntry* route =
+ fake_net_stack().FindForwardingEntry([=](const ForwardingEntry& entry) {
+ return entry.destination.is_device_id() && entry.destination.device_id() == tunnel_iface_id;
+ });
+ ASSERT_EQ(route, nullptr);
+
+ // Attempt to add a route to the Tunnel interface.
+ auto result = AddRemoveHostRoute(kInterfaceTypeTunnel, prefix, kRoutePriorityLow, /*add*/ true);
+ EXPECT_EQ(result, kPlatformResultSuccess);
+
+ // Confirm that a route exists to the Tunnel interface with the given IP.
+ route = fake_net_stack().FindForwardingEntry([=](const ForwardingEntry& entry) {
+ return entry.destination.is_device_id() && entry.destination.device_id() == tunnel_iface_id;
+ });
+ ASSERT_NE(route, nullptr);
+ ASSERT_TRUE(route->subnet.addr.is_ipv6());
+ EXPECT_EQ(WeaveIpAddressToArray(prefix.IPAddr), route->subnet.addr.ipv6().addr);
+
+ // Remove the route to the Tunnel interface.
+ result = AddRemoveHostRoute(kInterfaceTypeTunnel, prefix, kRoutePriorityLow, /*add*/ false);
+ EXPECT_EQ(result, kPlatformResultSuccess);
+
+ // Confirm that the removal worked.
+ route = fake_net_stack().FindForwardingEntry([=](const ForwardingEntry& entry) {
+ return entry.destination.is_device_id() && entry.destination.device_id() == tunnel_iface_id;
+ });
+ ASSERT_EQ(route, nullptr);
+}
+
+TEST_F(WarmTest, RemoveHostRouteThreadNotFound) {
+ constexpr char kSubnetIp[] = "2001:0DB8:0042::";
+ constexpr uint8_t kPrefixLength = 48;
+ IPPrefix prefix;
+
+ ASSERT_TRUE(IPAddress::FromString(kSubnetIp, prefix.IPAddr));
+ prefix.Length = kPrefixLength;
+
+ // Sanity check - confirm no routes to the Thread interface exist.
+ uint64_t thread_iface_id = GetThreadInterfaceId();
+ ASSERT_NE(thread_iface_id, 0u);
+ const ForwardingEntry* route =
+ fake_net_stack().FindForwardingEntry([=](const ForwardingEntry& entry) {
+ return entry.destination.is_device_id() && entry.destination.device_id() == thread_iface_id;
+ });
+ ASSERT_EQ(route, nullptr);
+
+ // Remove the non-existent route to the Thread interface, expect failure.
+ auto result = AddRemoveHostRoute(kInterfaceTypeThread, prefix, kRoutePriorityLow, /*add*/ false);
+ EXPECT_EQ(result, kPlatformResultFailure);
+
+ // Sanity check - confirm still no routes to the Thread interface exist.
+ route = fake_net_stack().FindForwardingEntry([=](const ForwardingEntry& entry) {
+ return entry.destination.is_device_id() && entry.destination.device_id() == thread_iface_id;
+ });
+ ASSERT_EQ(route, nullptr);
+}
+
+TEST_F(WarmTest, RemoveHostRouteTunnelNotFound) {
+ constexpr char kSubnetIp[] = "2001:0DB8:0042::";
+ constexpr uint8_t kPrefixLength = 48;
+ IPPrefix prefix;
+
+ ASSERT_TRUE(IPAddress::FromString(kSubnetIp, prefix.IPAddr));
+ prefix.Length = kPrefixLength;
+
+ // Sanity check - confirm no routes to the Tunnel interface exist.
+ uint64_t tunnel_iface_id = GetTunnelInterfaceId();
+ ASSERT_NE(tunnel_iface_id, 0u);
+ const ForwardingEntry* route =
+ fake_net_stack().FindForwardingEntry([=](const ForwardingEntry& entry) {
+ return entry.destination.is_device_id() && entry.destination.device_id() == tunnel_iface_id;
+ });
+ ASSERT_EQ(route, nullptr);
+
+ // Remove the non-existent route to the Tunnel interface, expect failure.
+ auto result = AddRemoveHostRoute(kInterfaceTypeTunnel, prefix, kRoutePriorityLow, /*add*/ false);
+ EXPECT_EQ(result, kPlatformResultFailure);
+
+ // Sanity check - confirm still no routes to the Tunnel interface exist.
+ route = fake_net_stack().FindForwardingEntry([=](const ForwardingEntry& entry) {
+ return entry.destination.is_device_id() && entry.destination.device_id() == tunnel_iface_id;
+ });
+ ASSERT_EQ(route, nullptr);
+}
+
+} // namespace testing
+} // namespace Platform
+} // namespace Warm
+} // namespace Weave
+} // namespace nl
diff --git a/src/connectivity/weave/adaptation/thread_stack_manager_delegate_impl.cpp b/src/connectivity/weave/adaptation/thread_stack_manager_delegate_impl.cpp
index 309ffe4..593bbe5 100644
--- a/src/connectivity/weave/adaptation/thread_stack_manager_delegate_impl.cpp
+++ b/src/connectivity/weave/adaptation/thread_stack_manager_delegate_impl.cpp
@@ -418,6 +418,10 @@
return ZX_OK;
}
+const std::string& ThreadStackManagerDelegateImpl::GetInterfaceName() const {
+ return interface_name_;
+}
+
} // namespace DeviceLayer
} // namespace Weave
} // namespace nl
diff --git a/src/connectivity/weave/adaptation/thread_stack_manager_delegate_impl.h b/src/connectivity/weave/adaptation/thread_stack_manager_delegate_impl.h
index 000708a..b0e78d0 100644
--- a/src/connectivity/weave/adaptation/thread_stack_manager_delegate_impl.h
+++ b/src/connectivity/weave/adaptation/thread_stack_manager_delegate_impl.h
@@ -38,6 +38,7 @@
WEAVE_ERROR GetAndLogThreadStatsCounters() override;
WEAVE_ERROR GetAndLogThreadTopologyMinimal() override;
WEAVE_ERROR GetAndLogThreadTopologyFull() override;
+ const std::string& GetInterfaceName() const override;
private:
std::string interface_name_;
diff --git a/src/connectivity/weave/adaptation/thread_stack_manager_impl.cpp b/src/connectivity/weave/adaptation/thread_stack_manager_impl.cpp
index 1020eca..9f7efe9 100644
--- a/src/connectivity/weave/adaptation/thread_stack_manager_impl.cpp
+++ b/src/connectivity/weave/adaptation/thread_stack_manager_impl.cpp
@@ -81,6 +81,10 @@
return delegate_->GetAndLogThreadTopologyFull();
}
+const std::string& ThreadStackManagerImpl::GetInterfaceName() const {
+ return delegate_->GetInterfaceName();
+}
+
} // namespace DeviceLayer
} // namespace Weave
} // namespace nl
diff --git a/src/connectivity/weave/adaptation/thread_stack_manager_impl.h b/src/connectivity/weave/adaptation/thread_stack_manager_impl.h
index 6761187..62bce5e 100644
--- a/src/connectivity/weave/adaptation/thread_stack_manager_impl.h
+++ b/src/connectivity/weave/adaptation/thread_stack_manager_impl.h
@@ -64,6 +64,8 @@
virtual WEAVE_ERROR GetAndLogThreadTopologyMinimal() = 0;
// Log a Weave event for a full Thread topology.
virtual WEAVE_ERROR GetAndLogThreadTopologyFull() = 0;
+ // Get the name of the thread interface.
+ virtual const std::string& GetInterfaceName() const = 0;
};
// Sets the delegate containing the platform-specific implementation. It is
@@ -129,6 +131,9 @@
WEAVE_ERROR _GetAndLogThreadTopologyMinimal();
WEAVE_ERROR _GetAndLogThreadTopologyFull();
+ // ThreadStackManagerImpl-specific functionality.
+ const std::string& GetInterfaceName() const;
+
private:
static ThreadStackManagerImpl sInstance;
std::unique_ptr<Delegate> delegate_;
diff --git a/src/connectivity/weave/adaptation/warm_platform_config.h b/src/connectivity/weave/adaptation/warm_platform_config.h
index 7a8d444..1620326 100644
--- a/src/connectivity/weave/adaptation/warm_platform_config.h
+++ b/src/connectivity/weave/adaptation/warm_platform_config.h
@@ -6,13 +6,13 @@
// ==================== Platform Adaptations ====================
-#define WARM_CONFIG_SUPPORT_THREAD 0
-#define WARM_CONFIG_SUPPORT_THREAD_ROUTING 0
+#define WARM_CONFIG_SUPPORT_THREAD 1
+#define WARM_CONFIG_SUPPORT_THREAD_ROUTING 1
#define WARM_CONFIG_SUPPORT_LEGACY6LOWPAN_NETWORK 0
#define WARM_CONFIG_SUPPORT_WIFI 1
#define WARM_CONFIG_SUPPORT_CELLULAR 0
#define WARM_CONFIG_SUPPORT_WEAVE_TUNNEL 1
-#define WARM_CONFIG_SUPPORT_BORDER_ROUTING 0
+#define WARM_CONFIG_SUPPORT_BORDER_ROUTING 1
// ========== Platform-specific Configuration Overrides =========
diff --git a/src/connectivity/weave/adaptation/warm_support.cpp b/src/connectivity/weave/adaptation/warm_support.cpp
index 47f42ee..8c2b93e 100644
--- a/src/connectivity/weave/adaptation/warm_support.cpp
+++ b/src/connectivity/weave/adaptation/warm_support.cpp
@@ -5,6 +5,7 @@
// clang-format off
#include <Weave/DeviceLayer/internal/WeaveDeviceLayerInternal.h>
#include <Weave/DeviceLayer/ConnectivityManager.h>
+#include <Weave/DeviceLayer/ThreadStackManager.h>
#include <Warm/Warm.h>
// clang-format on
@@ -27,25 +28,31 @@
using namespace ::nl::Weave;
using namespace ::nl::Weave::Warm;
-constexpr char kInterfaceName[] = "weav-tun0";
+constexpr char kTunInterfaceName[] = "weav-tun0";
constexpr uint8_t kSubnetPrefixLen = 48;
-} // namespace
-WEAVE_ERROR Init(WarmFabricStateDelegate *inFabricStateDelegate) { return WEAVE_NO_ERROR; }
+// Get the interface name associated with the interface type. Returns true on success or false if
+// the type is not yet supported.
+bool GetInterfaceName(InterfaceType interface_type, std::string *interface_name) {
+ switch (interface_type) {
+ case kInterfaceTypeThread:
+ *interface_name = ThreadStackMgrImpl().GetInterfaceName();
+ return true;
+ case kInterfaceTypeTunnel:
+ *interface_name = kTunInterfaceName;
+ return true;
+ default:
+ return false;
+ }
+}
-void CriticalSectionEnter(void) {}
-
-void CriticalSectionExit(void) {}
-
-void RequestInvokeActions(void) { ::nl::Weave::Warm::InvokeActions(); }
-
-// Get tunnel interface id.
+// Get network interface id.
zx_status_t GetInterface(fuchsia::net::stack::StackSyncPtr &stack_sync_ptr,
- uint64_t *interface_id) {
+ std::string interface_name, uint64_t *interface_id) {
std::vector<fuchsia::net::stack::InterfaceInfo> ifs;
if (interface_id == NULL) {
- FX_LOGS(ERROR) << "interface_id is NULL";
+ FX_LOGS(ERROR) << "interface_id is NULL.";
return ZX_ERR_INVALID_ARGS;
}
@@ -56,13 +63,12 @@
}
std::vector<fuchsia::net::stack::InterfaceInfo>::iterator it;
-
- it = std::find_if(ifs.begin(), ifs.end(), [](const fuchsia::net::stack::InterfaceInfo &info) {
- return info.properties.name.compare(kInterfaceName) == 0;
+ it = std::find_if(ifs.begin(), ifs.end(), [&](const fuchsia::net::stack::InterfaceInfo &info) {
+ return info.properties.name == interface_name;
});
if (it == ifs.end()) {
- FX_LOGS(ERROR) << "Unable to find the tun interface";
+ FX_LOGS(ERROR) << "Unable to find interface \"" << interface_name << "\".";
return ZX_ERR_NOT_FOUND;
}
@@ -70,30 +76,52 @@
return ZX_OK;
}
+} // namespace
+
+WEAVE_ERROR Init(WarmFabricStateDelegate *inFabricStateDelegate) { return WEAVE_NO_ERROR; }
+
+void CriticalSectionEnter(void) {}
+
+void CriticalSectionExit(void) {}
+
+void RequestInvokeActions(void) { ::nl::Weave::Warm::InvokeActions(); }
+
// Add or remove address on tunnel interface.
-PlatformResult AddRemoveHostAddress(InterfaceType in_interface_type,
- const Inet::IPAddress &in_address, uint8_t in_prefix_length,
- bool in_add) {
+PlatformResult AddRemoveHostAddress(InterfaceType interface_type, const Inet::IPAddress &address,
+ uint8_t prefix_length, bool add) {
fuchsia::net::stack::StackSyncPtr stack_sync_ptr;
fuchsia::net::IpAddress addr;
fuchsia::net::Ipv6Address v6;
uint64_t interface_id = 0;
fuchsia::net::Subnet ifaddr;
auto svc = nl::Weave::DeviceLayer::PlatformMgrImpl().GetComponentContextForProcess()->svc();
+
+ // Determine interface name to add to/remove from.
+ std::string interface_name;
+ if (!GetInterfaceName(interface_type, &interface_name)) {
+ FX_LOGS(ERROR) << "Cannot handle interface type \"" << interface_type << "\".";
+ return kPlatformResultFailure;
+ }
+
+ // Connect to the net Stack and grab the interface ID requested.
zx_status_t status = svc->Connect(stack_sync_ptr.NewRequest());
if (status != ZX_OK) {
FX_LOGS(ERROR) << "Connect to netstack failed: " << status;
return kPlatformResultFailure;
}
- status = GetInterface(stack_sync_ptr, &interface_id);
+ status = GetInterface(stack_sync_ptr, interface_name, &interface_id);
if (status != ZX_OK) {
return kPlatformResultFailure;
}
- std::memcpy(v6.addr.data(), (uint8_t *)(in_address.Addr), v6.addr.size());
+
+ // Set up the ip address and prefix.
+ std::memcpy(v6.addr.data(), (uint8_t *)(address.Addr), v6.addr.size());
addr.set_ipv6(v6);
ifaddr.addr = std::move(addr);
- ifaddr.prefix_len = in_prefix_length;
- if (in_add) {
+ ifaddr.prefix_len = prefix_length;
+
+ if (add) {
+ // Add the address to the interface.
fuchsia::net::stack::Stack_AddInterfaceAddress_Result result;
status = stack_sync_ptr->AddInterfaceAddress(interface_id, std::move(ifaddr), &result);
if (status != ZX_OK || result.is_err()) {
@@ -102,6 +130,7 @@
return kPlatformResultFailure;
}
} else {
+ // Remove the address from the interface.
fuchsia::net::stack::Stack_DelInterfaceAddress_Result result;
status = stack_sync_ptr->DelInterfaceAddress(interface_id, std::move(ifaddr), &result);
if (status != ZX_OK || result.is_err()) {
@@ -111,31 +140,41 @@
}
}
- FX_LOGS(INFO) << "AddRemoveHostAddress successful";
+ FX_LOGS(INFO) << "AddRemoveHostAddress successful.";
return kPlatformResultSuccess;
}
// Add or remove route to/from forwarding table.
-PlatformResult AddRemoveHostRoute(InterfaceType in_interface_type, const Inet::IPPrefix &in_prefix,
- RoutePriority in_priority, bool in_add) {
+PlatformResult AddRemoveHostRoute(InterfaceType interface_type, const Inet::IPPrefix &prefix,
+ RoutePriority priority, bool add) {
uint64_t interface_id = 0;
fuchsia::net::stack::StackSyncPtr stack_sync_ptr;
fuchsia::net::Ipv6Address v6;
fuchsia::net::stack::ForwardingEntry entry;
auto svc = nl::Weave::DeviceLayer::PlatformMgrImpl().GetComponentContextForProcess()->svc();
+
+ // Determine interface name to add to/remove from.
+ std::string interface_name;
+ if (!GetInterfaceName(interface_type, &interface_name)) {
+ FX_LOGS(ERROR) << "Cannot handle interface type \"" << interface_type << "\".";
+ return kPlatformResultFailure;
+ }
+
+ // Connect to the net Stack and grab the interface ID requested.
zx_status_t status = svc->Connect(stack_sync_ptr.NewRequest());
if (status != ZX_OK) {
FX_LOGS(ERROR) << "Connect to netstack failed: " << status;
return kPlatformResultFailure;
}
- status = GetInterface(stack_sync_ptr, &interface_id);
+ status = GetInterface(stack_sync_ptr, interface_name, &interface_id);
if (status != ZX_OK) {
return kPlatformResultSuccess;
}
- std::memcpy(v6.addr.data(), (uint8_t *)(in_prefix.IPAddr.Addr), v6.addr.size());
- if (in_add) {
+ std::memcpy(v6.addr.data(), (uint8_t *)(prefix.IPAddr.Addr), v6.addr.size());
+ if (add) {
+ // Add the forwarding entry for the specified interface.
fuchsia::net::stack::Stack_AddForwardingEntry_Result result;
entry.subnet.addr.set_ipv6(v6);
entry.subnet.prefix_len = kSubnetPrefixLen;
@@ -151,9 +190,10 @@
return kPlatformResultFailure;
}
} else {
+ // Remove the forwarding entry for the specified interface.
fuchsia::net::Subnet subnet;
subnet.addr.set_ipv6(v6);
- subnet.prefix_len = in_prefix.Length;
+ subnet.prefix_len = prefix.Length;
fuchsia::net::stack::Stack_DelForwardingEntry_Result result;
status = stack_sync_ptr->DelForwardingEntry(std::move(subnet), &result);
if (status != ZX_OK) {
@@ -161,49 +201,43 @@
return kPlatformResultFailure;
}
if (result.is_err()) {
- FX_LOGS(ERROR) << "DelForwardingEntry failed result";
+ FX_LOGS(ERROR) << "DelForwardingEntry failed result.";
return kPlatformResultFailure;
}
}
- FX_LOGS(ERROR) << "AddRemoveHostRoute successful";
+ FX_LOGS(INFO) << "AddRemoveHostRoute successful.";
return kPlatformResultSuccess;
}
#if WARM_CONFIG_SUPPORT_THREAD
-
PlatformResult AddRemoveThreadAddress(InterfaceType inInterfaceType,
const Inet::IPAddress &inAddress, bool inAdd) {
+ // This will be handled during the subsequent AddRemoveHostAddress from WARM.
return kPlatformResultSuccess;
}
-
#endif // WARM_CONFIG_SUPPORT_THREAD
#if WARM_CONFIG_SUPPORT_THREAD_ROUTING
-
-#error "Weave Thread router support not implemented"
-
PlatformResult StartStopThreadAdvertisement(InterfaceType inInterfaceType,
const Inet::IPPrefix &inPrefix, bool inStart) {
- // TODO: implement me
+ // This is handled by the LoWPAN service, nothing to do here.
+ return kPlatformResultSuccess;
}
-
#endif // WARM_CONFIG_SUPPORT_THREAD_ROUTING
#if WARM_CONFIG_SUPPORT_BORDER_ROUTING
-
-#error "Weave border router support not implemented"
-
PlatformResult AddRemoveThreadRoute(InterfaceType inInterfaceType, const Inet::IPPrefix &inPrefix,
RoutePriority inPriority, bool inAdd) {
- // TODO: implement me
+ // This will be handled during the subsequent AddRemoveHostAddress from WARM.
+ return kPlatformResultSuccess;
}
PlatformResult SetThreadRoutePriority(InterfaceType inInterfaceType, const Inet::IPPrefix &inPrefix,
RoutePriority inPriority) {
- // TODO: implement me
+ // Route priority not supported.
+ return kPlatformResultSuccess;
}
-
#endif // WARM_CONFIG_SUPPORT_BORDER_ROUTING
} // namespace Platform