/* * Copyright (c) 2024, The OpenThread Authors. * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. Neither the name of the copyright holder nor the * names of its contributors may be used to endorse or promote products * derived from this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE * POSSIBILITY OF SUCH DAMAGE. */ #define OTBR_LOG_TAG "ARCP_HOST" #include "android_rcp_host.hpp" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "android/common_utils.hpp" #include "common/code_utils.hpp" namespace otbr { namespace Android { AndroidRcpHost *AndroidRcpHost::sAndroidRcpHost = nullptr; AndroidRcpHost::AndroidRcpHost(Ncp::RcpHost &aRcpHost) : mRcpHost(aRcpHost) , mConfiguration() , mInfraIcmp6Socket(-1) { mInfraLinkState.interfaceName = ""; sAndroidRcpHost = this; } void AndroidRcpHost::SetConfiguration(const OtDaemonConfiguration &aConfiguration, const std::shared_ptr &aReceiver) { otError error = OT_ERROR_NONE; std::string message; otLinkModeConfig linkModeConfig; otbrLogInfo("Set configuration: %s", aConfiguration.toString().c_str()); VerifyOrExit(GetOtInstance() != nullptr, error = OT_ERROR_INVALID_STATE, message = "OT is not initialized"); VerifyOrExit(aConfiguration != mConfiguration); // TODO: b/343814054 - Support enabling/disabling DHCPv6-PD. VerifyOrExit(!aConfiguration.dhcpv6PdEnabled, error = OT_ERROR_NOT_IMPLEMENTED, message = "DHCPv6-PD is not supported"); otNat64SetEnabled(GetOtInstance(), aConfiguration.nat64Enabled); // DNS upstream query is enabled if and only if NAT64 is enabled. otDnssdUpstreamQuerySetEnabled(GetOtInstance(), aConfiguration.nat64Enabled); linkModeConfig = GetLinkModeConfig(aConfiguration.borderRouterEnabled); SuccessOrExit(error = otThreadSetLinkMode(GetOtInstance(), linkModeConfig), message = "Failed to set link mode"); if (aConfiguration.borderRouterEnabled) { otSrpServerSetAutoEnableMode(GetOtInstance(), true); SetBorderRouterEnabled(true); } else { // This automatically disables the auto-enable mode which is designed for border router otSrpServerSetEnabled(GetOtInstance(), true); SetBorderRouterEnabled(false); } mConfiguration = aConfiguration; exit: PropagateResult(error, message, aReceiver); } void AndroidRcpHost::SetInfraLinkInterfaceName(const std::string &aInterfaceName, int aIcmp6Socket, const std::shared_ptr &aReceiver) { otError error = OT_ERROR_NONE; std::string message; const std::string infraIfName = aInterfaceName; unsigned int infraIfIndex = if_nametoindex(infraIfName.c_str()); otbrLogInfo("Setting infra link state: %s", aInterfaceName.c_str()); VerifyOrExit(GetOtInstance() != nullptr, error = OT_ERROR_INVALID_STATE, message = "OT is not initialized"); VerifyOrExit(mConfiguration.borderRouterEnabled, error = OT_ERROR_INVALID_STATE, message = "Set infra link state when border router is disabled"); VerifyOrExit(mInfraLinkState.interfaceName != aInterfaceName || aIcmp6Socket != mInfraIcmp6Socket); if (infraIfIndex != 0 && aIcmp6Socket > 0) { SuccessOrExit(error = otBorderRoutingSetEnabled(GetOtInstance(), false /* aEnabled */), message = "failed to disable border routing"); otSysSetInfraNetif(infraIfName.c_str(), aIcmp6Socket); aIcmp6Socket = -1; SuccessOrExit(error = otBorderRoutingInit(GetOtInstance(), infraIfIndex, otSysInfraIfIsRunning()), message = "failed to initialize border routing"); SuccessOrExit(error = otBorderRoutingSetEnabled(GetOtInstance(), true /* aEnabled */), message = "failed to enable border routing"); // TODO: b/320836258 - Make BBR independently configurable otBackboneRouterSetEnabled(GetOtInstance(), true /* aEnabled */); } else { SuccessOrExit(error = otBorderRoutingSetEnabled(GetOtInstance(), false /* aEnabled */), message = "failed to disable border routing"); otBackboneRouterSetEnabled(GetOtInstance(), false /* aEnabled */); } mInfraLinkState.interfaceName = aInterfaceName; mInfraIcmp6Socket = aIcmp6Socket; SetTrelEnabled(mTrelEnabled); exit: if (error != OT_ERROR_NONE) { close(aIcmp6Socket); } PropagateResult(error, message, aReceiver); } void AndroidRcpHost::SetTrelEnabled(bool aEnabled) { mTrelEnabled = aEnabled; otbrLogInfo("%s TREL", aEnabled ? "Enabling" : "Disabling"); // Tear down TREL if it's been initialized/enabled already. otTrelSetEnabled(GetOtInstance(), false); otSysTrelDeinit(); if (mTrelEnabled && mInfraLinkState.interfaceName != "") { otSysTrelInit(mInfraLinkState.interfaceName.value_or("").c_str()); otTrelSetEnabled(GetOtInstance(), true); } } void AndroidRcpHost::SetInfraLinkNat64Prefix(const std::string &aNat64Prefix, const std::shared_ptr &aReceiver) { otError error = OT_ERROR_NONE; std::string message; otbrLogInfo("Setting infra link NAT64 prefix: %s", aNat64Prefix.c_str()); VerifyOrExit(mRcpHost.GetInstance() != nullptr, error = OT_ERROR_INVALID_STATE, message = "OT is not initialized"); mInfraLinkState.nat64Prefix = aNat64Prefix; NotifyNat64PrefixDiscoveryDone(); exit: PropagateResult(error, message, aReceiver); } void AndroidRcpHost::RunOtCtlCommand(const std::string &aCommand, const bool aIsInteractive, const std::shared_ptr &aReceiver) { otSysCliInitUsingDaemon(GetOtInstance()); if (!aCommand.empty()) { std::string command = aCommand; mIsOtCtlInteractiveMode = aIsInteractive; mOtCtlOutputReceiver = aReceiver; otCliInit(GetOtInstance(), AndroidRcpHost::OtCtlCommandCallback, this); otCliInputLine(command.data()); } } int AndroidRcpHost::OtCtlCommandCallback(void *aBinderServer, const char *aFormat, va_list aArguments) { return static_cast(aBinderServer)->OtCtlCommandCallback(aFormat, aArguments); } int AndroidRcpHost::OtCtlCommandCallback(const char *aFormat, va_list aArguments) { static const std::string kPrompt = "> "; std::string output; VerifyOrExit(mOtCtlOutputReceiver != nullptr, otSysCliInitUsingDaemon(GetOtInstance())); android::base::StringAppendV(&output, aFormat, aArguments); // Ignore CLI prompt VerifyOrExit(output != kPrompt); mOtCtlOutputReceiver->onOutput(output); // Check if the command has completed (indicated by "Done" or "Error") if (output.starts_with("Done") || output.starts_with("Error")) { mIsOtCtlOutputComplete = true; } // The OpenThread CLI consistently outputs "\r\n" as a newline character. Therefore, we use the presence of "\r\n" // following "Done" or "Error" to signal the completion of a command's output. if (mIsOtCtlOutputComplete && output.ends_with("\r\n")) { if (!mIsOtCtlInteractiveMode) { otSysCliInitUsingDaemon(GetOtInstance()); } mIsOtCtlOutputComplete = false; mOtCtlOutputReceiver->onComplete(); } exit: return output.length(); } static int OutputCallback(void *aContext, const char *aFormat, va_list aArguments) { std::string output; android::base::StringAppendV(&output, aFormat, aArguments); int length = output.length(); VerifyOrExit(android::base::WriteStringToFd(output, *(static_cast(aContext))), length = 0); exit: return length; } inline void DumpCliCommand(std::string aCommand, int aFd) { android::base::WriteStringToFd(aCommand + '\n', aFd); otCliInputLine(aCommand.data()); } binder_status_t AndroidRcpHost::Dump(int aFd, const char **aArgs, uint32_t aNumArgs) { OT_UNUSED_VARIABLE(aArgs); OT_UNUSED_VARIABLE(aNumArgs); otCliInit(GetOtInstance(), OutputCallback, &aFd); DumpCliCommand("state", aFd); DumpCliCommand("srp server state", aFd); DumpCliCommand("srp server service", aFd); DumpCliCommand("srp server host", aFd); DumpCliCommand("dataset activetimestamp", aFd); DumpCliCommand("dataset channel", aFd); DumpCliCommand("dataset channelmask", aFd); DumpCliCommand("dataset extpanid", aFd); DumpCliCommand("dataset meshlocalprefix", aFd); DumpCliCommand("dataset networkname", aFd); DumpCliCommand("dataset panid", aFd); DumpCliCommand("dataset securitypolicy", aFd); DumpCliCommand("leaderdata", aFd); DumpCliCommand("eidcache", aFd); DumpCliCommand("counters mac", aFd); DumpCliCommand("counters mle", aFd); DumpCliCommand("counters ip", aFd); DumpCliCommand("router table", aFd); DumpCliCommand("neighbor table", aFd); DumpCliCommand("ipaddr -v", aFd); DumpCliCommand("netdata show", aFd); fsync(aFd); otSysCliInitUsingDaemon(GetOtInstance()); return STATUS_OK; } std::vector ToOtUpstreamDnsServerAddresses(const std::vector &aAddresses) { std::vector addresses; // TODO: b/363738575 - support IPv6 for (const auto &addressString : aAddresses) { otIp6Address ip6Address; otIp4Address ip4Address; if (otIp4AddressFromString(addressString.c_str(), &ip4Address) != OT_ERROR_NONE) { continue; } otIp4ToIp4MappedIp6Address(&ip4Address, &ip6Address); addresses.push_back(ip6Address); } return addresses; } void AndroidRcpHost::SetInfraLinkDnsServers(const std::vector &aDnsServers, const std::shared_ptr &aReceiver) { otError error = OT_ERROR_NONE; std::string message; auto dnsServers = ToOtUpstreamDnsServerAddresses(aDnsServers); otbrLogInfo("Setting infra link DNS servers: %d servers", aDnsServers.size()); VerifyOrExit(aDnsServers != mInfraLinkState.dnsServers); mInfraLinkState.dnsServers = aDnsServers; otSysUpstreamDnsSetServerList(dnsServers.data(), dnsServers.size()); exit: PropagateResult(error, message, aReceiver); } void AndroidRcpHost::NotifyNat64PrefixDiscoveryDone(void) { otIp6Prefix nat64Prefix{}; uint32_t infraIfIndex = if_nametoindex(mInfraLinkState.interfaceName.value_or("").c_str()); otIp6PrefixFromString(mInfraLinkState.nat64Prefix.value_or("").c_str(), &nat64Prefix); otPlatInfraIfDiscoverNat64PrefixDone(GetOtInstance(), infraIfIndex, &nat64Prefix); } otInstance *AndroidRcpHost::GetOtInstance(void) { return mRcpHost.GetInstance(); } otLinkModeConfig AndroidRcpHost::GetLinkModeConfig(bool aIsRouter) { otLinkModeConfig linkModeConfig{}; if (aIsRouter) { linkModeConfig.mRxOnWhenIdle = true; linkModeConfig.mDeviceType = true; linkModeConfig.mNetworkData = true; } else { linkModeConfig.mRxOnWhenIdle = false; linkModeConfig.mDeviceType = false; linkModeConfig.mNetworkData = true; } return linkModeConfig; } void AndroidRcpHost::SetBorderRouterEnabled(bool aEnabled) { otError error; error = otBorderRoutingSetEnabled(GetOtInstance(), aEnabled); if (error != OT_ERROR_NONE) { otbrLogWarning("Failed to %s Border Routing: %s", (aEnabled ? "enable" : "disable"), otThreadErrorToString(error)); ExitNow(); } otBackboneRouterSetEnabled(GetOtInstance(), aEnabled); exit: return; } extern "C" otError otPlatInfraIfDiscoverNat64Prefix(uint32_t aInfraIfIndex) { OT_UNUSED_VARIABLE(aInfraIfIndex); AndroidRcpHost *androidRcpHost = AndroidRcpHost::Get(); otError error = OT_ERROR_NONE; VerifyOrExit(androidRcpHost != nullptr, error = OT_ERROR_INVALID_STATE); androidRcpHost->NotifyNat64PrefixDiscoveryDone(); exit: return error; } } // namespace Android } // namespace otbr