| // 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 <lib/syslog/cpp/macros.h> |
| |
| // clang-format off |
| #include <Weave/DeviceLayer/internal/WeaveDeviceLayerInternal.h> |
| #include <Weave/DeviceLayer/internal/DeviceNetworkInfo.h> |
| #include <Weave/DeviceLayer/PlatformManager.h> |
| #include <Weave/DeviceLayer/ThreadStackManager.h> |
| // clang-format on |
| |
| #include "thread_stack_manager_delegate_impl.h" |
| |
| namespace nl { |
| namespace Weave { |
| namespace DeviceLayer { |
| |
| namespace { |
| using fuchsia::lowpan::ConnectivityState; |
| using fuchsia::lowpan::Credential; |
| using fuchsia::lowpan::Identity; |
| using fuchsia::lowpan::ProvisioningParams; |
| using fuchsia::lowpan::Role; |
| using fuchsia::lowpan::device::DeviceExtraSyncPtr; |
| using fuchsia::lowpan::device::DeviceState; |
| using fuchsia::lowpan::device::DeviceSyncPtr; |
| using fuchsia::lowpan::device::Lookup_LookupDevice_Result; |
| using fuchsia::lowpan::device::LookupSyncPtr; |
| using fuchsia::lowpan::device::Protocols; |
| |
| using nl::Weave::DeviceLayer::PlatformMgrImpl; |
| using nl::Weave::DeviceLayer::Internal::DeviceNetworkInfo; |
| |
| using ThreadDeviceType = ConnectivityManager::ThreadDeviceType; |
| |
| constexpr uint16_t kMinThreadChannel = 11; |
| constexpr uint16_t kMaxThreadChannel = 26; |
| } // namespace |
| |
| // Note: Since the functions within this class are intended to function |
| // synchronously within the Device Layer, these functions all use SyncPtrs for |
| // interfacing with the LoWPAN FIDL protocols. |
| |
| WEAVE_ERROR ThreadStackManagerDelegateImpl::InitThreadStack() { |
| // See note at top to explain these SyncPtrs. |
| LookupSyncPtr lookup; |
| Lookup_LookupDevice_Result result; |
| Protocols protocols; |
| std::vector<std::string> interface_names; |
| zx_status_t status; |
| |
| // Access the LoWPAN service. |
| status = PlatformMgrImpl().GetComponentContextForProcess()->svc()->Connect(lookup.NewRequest()); |
| if (status != ZX_OK) { |
| FX_LOGS(ERROR) << "Failed to connect to fuchsia.lowpan.device.Lookup: " |
| << zx_status_get_string(status); |
| return status; |
| } |
| |
| // Retrieve LoWPAN interface names. |
| status = lookup->GetDevices(&interface_names); |
| if (status != ZX_OK) { |
| FX_LOGS(ERROR) << "Failed to retrieve LoWPAN interface names: " << zx_status_get_string(status); |
| return status; |
| } |
| |
| // Check returned interfaces for Thread support. |
| bool found_device = false; |
| for (auto& name : interface_names) { |
| std::vector<std::string> net_types; |
| |
| protocols.set_device(device_.NewRequest()); |
| |
| // Look up the device by interface name. |
| status = lookup->LookupDevice(name, std::move(protocols), &result); |
| if (status != ZX_OK) { |
| FX_LOGS(ERROR) << "Failed to lookup device: " << zx_status_get_string(status); |
| return status; |
| } |
| if (result.is_err()) { |
| FX_LOGS(WARNING) << "LoWPAN service error during lookup: " |
| << static_cast<int32_t>(result.err()); |
| continue; |
| } |
| |
| // Check if the device supports Thread. |
| status = device_->GetSupportedNetworkTypes(&net_types); |
| if (status != ZX_OK) { |
| FX_LOGS(ERROR) << "Failed to request supported network types from device \"" << name |
| << "\": " << zx_status_get_string(status); |
| return status; |
| } |
| |
| for (auto& net_type : net_types) { |
| if (net_type == fuchsia::lowpan::NET_TYPE_THREAD_1_X) { |
| // Found a Thread device. |
| interface_name_ = name; |
| found_device = true; |
| break; |
| } |
| } |
| |
| if (found_device) { |
| break; |
| } |
| } |
| |
| if (!found_device) { |
| FX_LOGS(ERROR) << "Could not find a device that supports Thread networks!"; |
| return ZX_ERR_NOT_FOUND; |
| } |
| |
| return WEAVE_NO_ERROR; |
| } |
| |
| bool ThreadStackManagerDelegateImpl::HaveRouteToAddress(const IPAddress& destAddr) { |
| return false; // TODO(fxbug.dev/55857) |
| } |
| |
| void ThreadStackManagerDelegateImpl::OnPlatformEvent(const WeaveDeviceEvent* event) {} |
| |
| bool ThreadStackManagerDelegateImpl::IsThreadEnabled() { |
| DeviceState device_state; |
| |
| // Get the device state. |
| if (GetDeviceState(&device_state) != ZX_OK) { |
| return false; |
| } |
| |
| // Determine whether Thread is enabled. |
| switch (device_state.connectivity_state()) { |
| case ConnectivityState::OFFLINE: |
| case ConnectivityState::ATTACHING: |
| case ConnectivityState::ATTACHED: |
| case ConnectivityState::ISOLATED: |
| case ConnectivityState::COMMISSIONING: |
| return true; |
| default: |
| return false; |
| } |
| } |
| |
| WEAVE_ERROR ThreadStackManagerDelegateImpl::SetThreadEnabled(bool val) { |
| // Enable or disable the device. |
| zx_status_t status = device_->SetActive(val); |
| if (status != ZX_OK) { |
| FX_LOGS(ERROR) << "Failed to " << (val ? "enable" : "disable") |
| << " Thread: " << zx_status_get_string(status); |
| return status; |
| } |
| |
| return WEAVE_NO_ERROR; |
| } |
| |
| bool ThreadStackManagerDelegateImpl::IsThreadProvisioned() { |
| DeviceState device_state; |
| |
| // Get the device state. |
| if (GetDeviceState(&device_state) != ZX_OK) { |
| return false; |
| } |
| |
| // Check for the provision. |
| switch (device_state.connectivity_state()) { |
| case ConnectivityState::INACTIVE: |
| case ConnectivityState::OFFLINE: |
| return false; |
| default: |
| return true; |
| } |
| } |
| |
| bool ThreadStackManagerDelegateImpl::IsThreadAttached() { |
| DeviceState device_state; |
| |
| // Get the device state. |
| if (GetDeviceState(&device_state) != ZX_OK) { |
| return false; |
| } |
| |
| return device_state.connectivity_state() == ConnectivityState::ATTACHED; |
| } |
| |
| WEAVE_ERROR ThreadStackManagerDelegateImpl::GetThreadProvision(DeviceNetworkInfo& netInfo, |
| bool includeCredentials) { |
| DeviceExtraSyncPtr device_extra; |
| Identity identity; |
| zx_status_t status; |
| |
| // Get the Device pointer. |
| status = GetProtocols(std::move(Protocols().set_device_extra(device_extra.NewRequest()))); |
| if (status != ZX_OK) { |
| return status; |
| } |
| |
| // Get the network identity. |
| status = device_extra->WatchIdentity(&identity); |
| if (status != ZX_OK) { |
| FX_LOGS(ERROR) << "Could not get LoWPAN network identity: " << zx_status_get_string(status); |
| return status; |
| } |
| |
| // TODO(fxbug.dev/67254): Restore the following block once the LoWPAN service |
| // correctly returns the net_type. |
| |
| // // Check if the provision is a Thread network. |
| // if (!identity.has_net_type()) { |
| // FX_LOGS(ERROR) << "No net_type provided; cannot confirm Thread network type."; |
| // return ZX_ERR_INTERNAL; |
| // } |
| // if (identity.net_type() != fuchsia::lowpan::NET_TYPE_THREAD_1_X) { |
| // FX_LOGS(ERROR) << "Cannot support LoWPAN network type \"" << identity.net_type() |
| // << "\" in ThreadStackManager."; |
| // return ZX_ERR_INTERNAL; |
| // } |
| |
| // Start copying provision info. |
| netInfo.Reset(); |
| // Copy network name. |
| if (identity.has_raw_name()) { |
| std::memcpy(netInfo.ThreadNetworkName, identity.raw_name().data(), |
| std::min<size_t>(DeviceNetworkInfo::kMaxThreadNetworkNameLength, |
| identity.raw_name().size())); |
| } |
| // Copy extended PAN id. |
| if (identity.has_xpanid()) { |
| std::memcpy( |
| netInfo.ThreadExtendedPANId, identity.xpanid().data(), |
| std::min<size_t>(DeviceNetworkInfo::kThreadExtendedPANIdLength, identity.xpanid().size())); |
| netInfo.FieldPresent.ThreadExtendedPANId = true; |
| } |
| // Copy PAN id. |
| if (!identity.has_panid()) { |
| // Warn that PAN id remains unspecified. |
| FX_LOGS(WARNING) << "PAN id not supplied."; |
| } else { |
| netInfo.ThreadPANId = identity.panid(); |
| } |
| // Copy channel. |
| if (!identity.has_channel() || identity.channel() < kMinThreadChannel || |
| identity.channel() > kMaxThreadChannel) { |
| // Warn that channel remains unspecified. |
| std::string channel_info = |
| identity.has_channel() ? std::to_string(identity.channel()) : "(none)"; |
| FX_LOGS(WARNING) << "Invalid Thread channel: " << channel_info; |
| } else { |
| netInfo.ThreadChannel = identity.channel(); |
| } |
| |
| // TODO(fxbug.dev/55638) - Implement mesh prefix and pre-shared commisioning key. |
| |
| if (!includeCredentials) { |
| // No futher processing needed, credentials won't be included. |
| return WEAVE_NO_ERROR; |
| } |
| |
| // Get credential. |
| std::unique_ptr<Credential> credential; |
| status = device_extra->GetCredential(&credential); |
| if (status != ZX_OK) { |
| FX_LOGS(ERROR) << "Could not retrieve credential: " << zx_status_get_string(status); |
| return status; |
| } |
| |
| // Copy credential info. |
| if (!credential) { |
| // Warn that credential remains unset. |
| FX_LOGS(WARNING) << "Credential requested but no credential provided from LoWPAN device"; |
| } else { |
| std::memcpy(netInfo.ThreadNetworkKey, credential->master_key().data(), |
| std::min<size_t>(DeviceNetworkInfo::kMaxThreadNetworkNameLength, |
| credential->master_key().size())); |
| netInfo.FieldPresent.ThreadNetworkKey = true; |
| } |
| |
| return WEAVE_NO_ERROR; |
| } |
| |
| WEAVE_ERROR ThreadStackManagerDelegateImpl::SetThreadProvision(const DeviceNetworkInfo& netInfo) { |
| DeviceSyncPtr device; |
| std::unique_ptr<Credential> credential; |
| Identity identity; |
| |
| // Set up identity. |
| std::vector<uint8_t> network_name{ |
| netInfo.ThreadNetworkName, |
| netInfo.ThreadNetworkName + std::strlen(netInfo.ThreadNetworkName)}; |
| |
| identity.set_raw_name(std::move(network_name)); |
| identity.set_net_type(fuchsia::lowpan::NET_TYPE_THREAD_1_X); |
| |
| if (netInfo.FieldPresent.ThreadExtendedPANId) { |
| identity.set_xpanid(std::vector<uint8_t>{ |
| netInfo.ThreadExtendedPANId, |
| netInfo.ThreadExtendedPANId + DeviceNetworkInfo::kThreadExtendedPANIdLength}); |
| } |
| |
| if (netInfo.ThreadChannel != Profiles::NetworkProvisioning::kThreadChannel_NotSpecified) { |
| identity.set_channel(netInfo.ThreadChannel); |
| } |
| |
| if (netInfo.ThreadPANId != Profiles::NetworkProvisioning::kThreadPANId_NotSpecified) { |
| identity.set_panid(netInfo.ThreadPANId); |
| } |
| |
| // Set up credential. |
| if (netInfo.FieldPresent.ThreadNetworkKey) { |
| credential = std::make_unique<Credential>(); |
| credential->set_master_key(std::vector<uint8_t>{ |
| netInfo.ThreadNetworkKey, |
| netInfo.ThreadNetworkKey + DeviceNetworkInfo::kThreadNetworkKeyLength}); |
| } |
| |
| // Add identity and credential to provisioning params. |
| ProvisioningParams params{.identity = std::move(identity), .credential = std::move(credential)}; |
| |
| // Provision the thread device. |
| return device_->ProvisionNetwork(std::move(params)); |
| } |
| |
| void ThreadStackManagerDelegateImpl::ClearThreadProvision() { |
| // TODO(fxbug.dev/59029): When thread stack mgr is initialized, this workaround will be removed. |
| if (!device_.is_bound()) { |
| FX_LOGS(INFO) << "Skipping ClearThreadProvision as device is not bound"; |
| return; |
| } |
| zx_status_t status = device_->LeaveNetwork(); |
| if (status != ZX_OK) { |
| FX_LOGS(ERROR) << "Could not clear LoWPAN provision: " << zx_status_get_string(status); |
| } |
| } |
| |
| ThreadDeviceType ThreadStackManagerDelegateImpl::GetThreadDeviceType() { |
| DeviceState device_state; |
| |
| // Get the device state. |
| if (GetDeviceState(&device_state) != ZX_OK) { |
| return ThreadDeviceType::kThreadDeviceType_NotSupported; |
| } |
| |
| // Determine device type by role. |
| switch (device_state.role()) { |
| case Role::END_DEVICE: |
| return ThreadDeviceType::kThreadDeviceType_FullEndDevice; |
| case Role::SLEEPY_END_DEVICE: |
| return ThreadDeviceType::kThreadDeviceType_SleepyEndDevice; |
| case Role::ROUTER: |
| case Role::SLEEPY_ROUTER: |
| case Role::LEADER: |
| case Role::COORDINATOR: |
| return ThreadDeviceType::kThreadDeviceType_Router; |
| default: |
| return ThreadDeviceType::kThreadDeviceType_NotSupported; |
| }; |
| } |
| |
| bool ThreadStackManagerDelegateImpl::HaveMeshConnectivity() { return IsThreadAttached(); } |
| |
| WEAVE_ERROR ThreadStackManagerDelegateImpl::GetAndLogThreadStatsCounters() { |
| return WEAVE_ERROR_NOT_IMPLEMENTED; // TODO(fxbug.dev/55888) |
| } |
| |
| WEAVE_ERROR ThreadStackManagerDelegateImpl::GetAndLogThreadTopologyMinimal() { |
| return WEAVE_ERROR_NOT_IMPLEMENTED; // TODO(fxbug.dev/55888) |
| } |
| |
| WEAVE_ERROR ThreadStackManagerDelegateImpl::GetAndLogThreadTopologyFull() { |
| return WEAVE_ERROR_NOT_IMPLEMENTED; // TODO(fxbug.dev/55888) |
| } |
| |
| zx_status_t ThreadStackManagerDelegateImpl::GetDeviceState(DeviceState* device_state) { |
| DeviceSyncPtr device; |
| zx_status_t status; |
| |
| // Get device pointer. |
| status = GetProtocols(std::move(Protocols().set_device(device.NewRequest()))); |
| if (status != ZX_OK) { |
| return status; |
| } |
| |
| // Grab device state. |
| status = device->WatchDeviceState(device_state); |
| if (status != ZX_OK) { |
| FX_LOGS(ERROR) << "Could not get LoWPAN device state: " << zx_status_get_string(status); |
| return status; |
| } |
| |
| return ZX_OK; |
| } |
| |
| zx_status_t ThreadStackManagerDelegateImpl::GetProtocols(Protocols protocols) { |
| // See note at top to explain these SyncPtrs. |
| LookupSyncPtr lookup; |
| Lookup_LookupDevice_Result result; |
| zx_status_t status; |
| |
| // Access the LoWPAN service. |
| status = PlatformMgrImpl().GetComponentContextForProcess()->svc()->Connect(lookup.NewRequest()); |
| if (status != ZX_OK) { |
| FX_LOGS(ERROR) << "Failed to connect to fuchsia.lowpan.device.Lookup: " |
| << zx_status_get_string(status); |
| return status; |
| } |
| |
| // Look up the device by interface name. |
| status = lookup->LookupDevice(interface_name_, std::move(protocols), &result); |
| if (status != ZX_OK) { |
| FX_LOGS(ERROR) << "Failed to lookup device: " << zx_status_get_string(status); |
| return status; |
| } |
| if (result.is_err()) { |
| FX_LOGS(ERROR) << "LoWPAN service error during lookup: " << static_cast<int32_t>(result.err()); |
| return ZX_ERR_INTERNAL; |
| } |
| |
| return ZX_OK; |
| } |
| |
| const std::string& ThreadStackManagerDelegateImpl::GetInterfaceName() const { |
| return interface_name_; |
| } |
| |
| } // namespace DeviceLayer |
| } // namespace Weave |
| } // namespace nl |