1 /*
2 * Copyright (c) 2024, The OpenThread Authors.
3 * All rights reserved.
4 *
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions are met:
7 * 1. Redistributions of source code must retain the above copyright
8 * notice, this list of conditions and the following disclaimer.
9 * 2. Redistributions in binary form must reproduce the above copyright
10 * notice, this list of conditions and the following disclaimer in the
11 * documentation and/or other materials provided with the distribution.
12 * 3. Neither the name of the copyright holder nor the
13 * names of its contributors may be used to endorse or promote products
14 * derived from this software without specific prior written permission.
15 *
16 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
17 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
18 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
19 * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
20 * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
21 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
22 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
23 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
24 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
25 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
26 * POSSIBILITY OF SUCH DAMAGE.
27 */
28
29 #define OTBR_LOG_TAG "ARCP_HOST"
30
31 #include "android_rcp_host.hpp"
32
33 #include <net/if.h>
34 #include <vector>
35
36 #include <android-base/file.h>
37 #include <android-base/stringprintf.h>
38 #include <openthread/backbone_router_ftd.h>
39 #include <openthread/border_routing.h>
40 #include <openthread/dnssd_server.h>
41 #include <openthread/ip6.h>
42 #include <openthread/nat64.h>
43 #include <openthread/netdiag.h>
44 #include <openthread/openthread-system.h>
45 #include <openthread/srp_server.h>
46 #include <openthread/thread.h>
47 #include <openthread/thread_ftd.h>
48 #include <openthread/trel.h>
49 #include <openthread/platform/infra_if.h>
50 #include <openthread/platform/trel.h>
51
52 #include "android/common_utils.hpp"
53 #include "common/code_utils.hpp"
54
55 namespace otbr {
56 namespace Android {
57
58 AndroidRcpHost *AndroidRcpHost::sAndroidRcpHost = nullptr;
59
AndroidRcpHost(Host::RcpHost & aRcpHost)60 AndroidRcpHost::AndroidRcpHost(Host::RcpHost &aRcpHost)
61 : mRcpHost(aRcpHost)
62 , mConfiguration()
63 , mInfraIcmp6Socket(-1)
64 {
65 mInfraLinkState.interfaceName = "";
66
67 sAndroidRcpHost = this;
68 }
69
SetConfiguration(const OtDaemonConfiguration & aConfiguration,const std::shared_ptr<IOtStatusReceiver> & aReceiver)70 void AndroidRcpHost::SetConfiguration(const OtDaemonConfiguration &aConfiguration,
71 const std::shared_ptr<IOtStatusReceiver> &aReceiver)
72 {
73 otError error = OT_ERROR_NONE;
74 std::string message;
75 otLinkModeConfig linkModeConfig;
76 bool borderRouterEnabled = aConfiguration.borderRouterEnabled;
77
78 otbrLogInfo("Set configuration: %s", aConfiguration.toString().c_str());
79
80 VerifyOrExit(GetOtInstance() != nullptr, error = OT_ERROR_INVALID_STATE, message = "OT is not initialized");
81
82 SuccessOrExit(error = otThreadSetVendorName(GetOtInstance(), aConfiguration.vendorName.c_str()),
83 message = "Invalid vendor name " + aConfiguration.vendorName);
84 SuccessOrExit(error = otThreadSetVendorModel(GetOtInstance(), aConfiguration.modelName.c_str()),
85 message = "Invalid model name " + aConfiguration.modelName);
86
87 // TODO: b/343814054 - Support enabling/disabling DHCPv6-PD.
88 VerifyOrExit(!aConfiguration.dhcpv6PdEnabled, error = OT_ERROR_NOT_IMPLEMENTED,
89 message = "DHCPv6-PD is not supported");
90 otNat64SetEnabled(GetOtInstance(), aConfiguration.nat64Enabled);
91 // DNS upstream query is enabled if and only if NAT64 is enabled.
92 otDnssdUpstreamQuerySetEnabled(GetOtInstance(), aConfiguration.nat64Enabled);
93
94 // Thread has to be a Router before new Android API is added to support making it a SED (Sleepy End Device)
95 linkModeConfig = GetLinkModeConfig(/* aIsRouter= */ true);
96 SuccessOrExit(error = otThreadSetLinkMode(GetOtInstance(), linkModeConfig), message = "Failed to set link mode");
97
98 // - In non-BR mode, this device should try to be a router only when there are no other routers
99 // - 16 is the default ROUTER_UPGRADE_THRESHOLD value defined in OpenThread
100 otThreadSetRouterUpgradeThreshold(GetOtInstance(), (borderRouterEnabled ? 16 : 1));
101
102 // Sets much lower Leader / Partition weight for a non-BR device so that it would
103 // not attempt to be the new leader after merging partitions. Keeps BR using the
104 // default Leader weight value 64.
105 //
106 // TODO: b/404979710 - sets leader weight higher based on the new Thread 1.4 device
107 // properties feature.
108 otThreadSetLocalLeaderWeight(GetOtInstance(), (borderRouterEnabled ? 64 : 32));
109
110 if (borderRouterEnabled && aConfiguration.srpServerWaitForBorderRoutingEnabled)
111 {
112 // This will automatically disable fast-start mode if it was ever enabled
113 otSrpServerSetAutoEnableMode(GetOtInstance(), true);
114 }
115 else
116 {
117 otSrpServerSetAutoEnableMode(GetOtInstance(), false);
118 otSrpServerEnableFastStartMode(GetOtInstance());
119 }
120
121 SetBorderRouterEnabled(borderRouterEnabled);
122
123 mConfiguration = aConfiguration;
124
125 exit:
126 PropagateResult(error, message, aReceiver);
127 }
128
SetInfraLinkInterfaceName(const std::string & aInterfaceName,int aIcmp6Socket,const std::shared_ptr<IOtStatusReceiver> & aReceiver)129 void AndroidRcpHost::SetInfraLinkInterfaceName(const std::string &aInterfaceName,
130 int aIcmp6Socket,
131 const std::shared_ptr<IOtStatusReceiver> &aReceiver)
132 {
133 otError error = OT_ERROR_NONE;
134 std::string message;
135 const std::string infraIfName = aInterfaceName;
136 unsigned int infraIfIndex = if_nametoindex(infraIfName.c_str());
137
138 otbrLogInfo("Setting infra link state: %s", aInterfaceName.c_str());
139
140 VerifyOrExit(GetOtInstance() != nullptr, error = OT_ERROR_INVALID_STATE, message = "OT is not initialized");
141 VerifyOrExit(mConfiguration.borderRouterEnabled, error = OT_ERROR_INVALID_STATE,
142 message = "Set infra link state when border router is disabled");
143 VerifyOrExit(mInfraLinkState.interfaceName != aInterfaceName || aIcmp6Socket != mInfraIcmp6Socket);
144
145 if (infraIfIndex != 0 && aIcmp6Socket > 0)
146 {
147 SuccessOrExit(error = otBorderRoutingSetEnabled(GetOtInstance(), false /* aEnabled */),
148 message = "failed to disable border routing");
149 otSysSetInfraNetif(infraIfName.c_str(), aIcmp6Socket);
150 aIcmp6Socket = -1;
151 SuccessOrExit(error = otBorderRoutingInit(GetOtInstance(), infraIfIndex, otSysInfraIfIsRunning()),
152 message = "failed to initialize border routing");
153 SuccessOrExit(error = otBorderRoutingSetEnabled(GetOtInstance(), true /* aEnabled */),
154 message = "failed to enable border routing");
155 // TODO: b/320836258 - Make BBR independently configurable
156 otBackboneRouterSetEnabled(GetOtInstance(), true /* aEnabled */);
157 }
158 else
159 {
160 SuccessOrExit(error = otBorderRoutingSetEnabled(GetOtInstance(), false /* aEnabled */),
161 message = "failed to disable border routing");
162 otBackboneRouterSetEnabled(GetOtInstance(), false /* aEnabled */);
163 }
164
165 mInfraLinkState.interfaceName = aInterfaceName;
166 mInfraIcmp6Socket = aIcmp6Socket;
167
168 SetTrelEnabled(mTrelEnabled);
169
170 exit:
171 if (error != OT_ERROR_NONE)
172 {
173 close(aIcmp6Socket);
174 }
175 PropagateResult(error, message, aReceiver);
176 }
177
SetTrelEnabled(bool aEnabled)178 void AndroidRcpHost::SetTrelEnabled(bool aEnabled)
179 {
180 mTrelEnabled = aEnabled;
181
182 otbrLogInfo("%s TREL", aEnabled ? "Enabling" : "Disabling");
183
184 // Tear down TREL if it's been initialized/enabled already.
185 otTrelSetEnabled(GetOtInstance(), false);
186 otSysTrelDeinit();
187
188 if (mTrelEnabled && mInfraLinkState.interfaceName != "")
189 {
190 otSysTrelInit(mInfraLinkState.interfaceName.value_or("").c_str());
191 otTrelSetEnabled(GetOtInstance(), true);
192 }
193 }
194
SetInfraLinkNat64Prefix(const std::string & aNat64Prefix,const std::shared_ptr<IOtStatusReceiver> & aReceiver)195 void AndroidRcpHost::SetInfraLinkNat64Prefix(const std::string &aNat64Prefix,
196 const std::shared_ptr<IOtStatusReceiver> &aReceiver)
197 {
198 otError error = OT_ERROR_NONE;
199 std::string message;
200
201 otbrLogInfo("Setting infra link NAT64 prefix: %s", aNat64Prefix.c_str());
202
203 VerifyOrExit(mRcpHost.GetInstance() != nullptr, error = OT_ERROR_INVALID_STATE, message = "OT is not initialized");
204
205 mInfraLinkState.nat64Prefix = aNat64Prefix;
206 NotifyNat64PrefixDiscoveryDone();
207
208 exit:
209 PropagateResult(error, message, aReceiver);
210 }
211
RunOtCtlCommand(const std::string & aCommand,const bool aIsInteractive,const std::shared_ptr<IOtOutputReceiver> & aReceiver)212 void AndroidRcpHost::RunOtCtlCommand(const std::string &aCommand,
213 const bool aIsInteractive,
214 const std::shared_ptr<IOtOutputReceiver> &aReceiver)
215 {
216 otSysCliInitUsingDaemon(GetOtInstance());
217
218 if (!aCommand.empty())
219 {
220 std::string command = aCommand;
221
222 mIsOtCtlInteractiveMode = aIsInteractive;
223 mOtCtlOutputReceiver = aReceiver;
224
225 otCliInit(GetOtInstance(), AndroidRcpHost::OtCtlCommandCallback, this);
226 otCliInputLine(command.data());
227 }
228 }
229
OtCtlCommandCallback(void * aBinderServer,const char * aFormat,va_list aArguments)230 int AndroidRcpHost::OtCtlCommandCallback(void *aBinderServer, const char *aFormat, va_list aArguments)
231 {
232 return static_cast<AndroidRcpHost *>(aBinderServer)->OtCtlCommandCallback(aFormat, aArguments);
233 }
234
OtCtlCommandCallback(const char * aFormat,va_list aArguments)235 int AndroidRcpHost::OtCtlCommandCallback(const char *aFormat, va_list aArguments)
236 {
237 static const std::string kPrompt = "> ";
238 std::string output;
239
240 VerifyOrExit(mOtCtlOutputReceiver != nullptr, otSysCliInitUsingDaemon(GetOtInstance()));
241
242 android::base::StringAppendV(&output, aFormat, aArguments);
243
244 // Ignore CLI prompt
245 VerifyOrExit(output != kPrompt);
246
247 mOtCtlOutputReceiver->onOutput(output);
248
249 // Check if the command has completed (indicated by "Done" or "Error")
250 if (output.starts_with("Done") || output.starts_with("Error"))
251 {
252 mIsOtCtlOutputComplete = true;
253 }
254
255 // The OpenThread CLI consistently outputs "\r\n" as a newline character. Therefore, we use the presence of "\r\n"
256 // following "Done" or "Error" to signal the completion of a command's output.
257 if (mIsOtCtlOutputComplete && output.ends_with("\r\n"))
258 {
259 if (!mIsOtCtlInteractiveMode)
260 {
261 otSysCliInitUsingDaemon(GetOtInstance());
262 }
263 mIsOtCtlOutputComplete = false;
264 mOtCtlOutputReceiver->onComplete();
265 }
266
267 exit:
268 return output.length();
269 }
270
OutputCallback(void * aContext,const char * aFormat,va_list aArguments)271 static int OutputCallback(void *aContext, const char *aFormat, va_list aArguments)
272 {
273 std::string output;
274
275 android::base::StringAppendV(&output, aFormat, aArguments);
276
277 int length = output.length();
278
279 VerifyOrExit(android::base::WriteStringToFd(output, *(static_cast<int *>(aContext))), length = 0);
280
281 exit:
282 return length;
283 }
284
DumpCliCommand(std::string aCommand,int aFd)285 inline void DumpCliCommand(std::string aCommand, int aFd)
286 {
287 android::base::WriteStringToFd(aCommand + '\n', aFd);
288 otCliInputLine(aCommand.data());
289 }
290
Dump(int aFd,const char ** aArgs,uint32_t aNumArgs)291 binder_status_t AndroidRcpHost::Dump(int aFd, const char **aArgs, uint32_t aNumArgs)
292 {
293 OT_UNUSED_VARIABLE(aArgs);
294 OT_UNUSED_VARIABLE(aNumArgs);
295
296 otCliInit(GetOtInstance(), OutputCallback, &aFd);
297
298 DumpCliCommand("state", aFd);
299 DumpCliCommand("srp server state", aFd);
300 DumpCliCommand("srp server service", aFd);
301 DumpCliCommand("srp server host", aFd);
302 DumpCliCommand("dataset activetimestamp", aFd);
303 DumpCliCommand("dataset channel", aFd);
304 DumpCliCommand("dataset channelmask", aFd);
305 DumpCliCommand("dataset extpanid", aFd);
306 DumpCliCommand("dataset meshlocalprefix", aFd);
307 DumpCliCommand("dataset networkname", aFd);
308 DumpCliCommand("dataset panid", aFd);
309 DumpCliCommand("dataset securitypolicy", aFd);
310 DumpCliCommand("leaderdata", aFd);
311 DumpCliCommand("eidcache", aFd);
312 DumpCliCommand("counters mac", aFd);
313 DumpCliCommand("counters mle", aFd);
314 DumpCliCommand("counters ip", aFd);
315 DumpCliCommand("router table", aFd);
316 DumpCliCommand("neighbor table", aFd);
317 DumpCliCommand("ipaddr -v", aFd);
318 DumpCliCommand("netdata show", aFd);
319
320 fsync(aFd);
321
322 otSysCliInitUsingDaemon(GetOtInstance());
323
324 return STATUS_OK;
325 }
326
ToOtUpstreamDnsServerAddresses(const std::vector<std::string> & aAddresses)327 std::vector<otIp6Address> ToOtUpstreamDnsServerAddresses(const std::vector<std::string> &aAddresses)
328 {
329 std::vector<otIp6Address> addresses;
330
331 // TODO: b/363738575 - support IPv6
332 for (const auto &addressString : aAddresses)
333 {
334 otIp6Address ip6Address;
335 otIp4Address ip4Address;
336
337 if (otIp4AddressFromString(addressString.c_str(), &ip4Address) != OT_ERROR_NONE)
338 {
339 continue;
340 }
341 otIp4ToIp4MappedIp6Address(&ip4Address, &ip6Address);
342 addresses.push_back(ip6Address);
343 }
344
345 return addresses;
346 }
347
SetInfraLinkDnsServers(const std::vector<std::string> & aDnsServers,const std::shared_ptr<IOtStatusReceiver> & aReceiver)348 void AndroidRcpHost::SetInfraLinkDnsServers(const std::vector<std::string> &aDnsServers,
349 const std::shared_ptr<IOtStatusReceiver> &aReceiver)
350 {
351 otError error = OT_ERROR_NONE;
352 std::string message;
353 auto dnsServers = ToOtUpstreamDnsServerAddresses(aDnsServers);
354
355 otbrLogInfo("Setting infra link DNS servers: %d servers", aDnsServers.size());
356
357 VerifyOrExit(aDnsServers != mInfraLinkState.dnsServers);
358
359 mInfraLinkState.dnsServers = aDnsServers;
360 otSysUpstreamDnsSetServerList(dnsServers.data(), dnsServers.size());
361
362 exit:
363 PropagateResult(error, message, aReceiver);
364 }
365
NotifyNat64PrefixDiscoveryDone(void)366 void AndroidRcpHost::NotifyNat64PrefixDiscoveryDone(void)
367 {
368 otIp6Prefix nat64Prefix{};
369 uint32_t infraIfIndex = if_nametoindex(mInfraLinkState.interfaceName.value_or("").c_str());
370
371 otIp6PrefixFromString(mInfraLinkState.nat64Prefix.value_or("").c_str(), &nat64Prefix);
372 otPlatInfraIfDiscoverNat64PrefixDone(GetOtInstance(), infraIfIndex, &nat64Prefix);
373 }
374
GetOtInstance(void)375 otInstance *AndroidRcpHost::GetOtInstance(void)
376 {
377 return mRcpHost.GetInstance();
378 }
379
GetLinkModeConfig(bool aIsRouter)380 otLinkModeConfig AndroidRcpHost::GetLinkModeConfig(bool aIsRouter)
381 {
382 otLinkModeConfig linkModeConfig{};
383
384 if (aIsRouter)
385 {
386 linkModeConfig.mRxOnWhenIdle = true;
387 linkModeConfig.mDeviceType = true;
388 linkModeConfig.mNetworkData = true;
389 }
390 else
391 {
392 linkModeConfig.mRxOnWhenIdle = false;
393 linkModeConfig.mDeviceType = false;
394 linkModeConfig.mNetworkData = true;
395 }
396
397 return linkModeConfig;
398 }
399
SetBorderRouterEnabled(bool aEnabled)400 void AndroidRcpHost::SetBorderRouterEnabled(bool aEnabled)
401 {
402 otError error;
403
404 error = otBorderRoutingSetEnabled(GetOtInstance(), aEnabled);
405 if (error != OT_ERROR_NONE)
406 {
407 otbrLogWarning("Failed to %s Border Routing: %s", (aEnabled ? "enable" : "disable"),
408 otThreadErrorToString(error));
409 ExitNow();
410 }
411
412 otBackboneRouterSetEnabled(GetOtInstance(), aEnabled);
413
414 exit:
415 return;
416 }
417
otPlatInfraIfDiscoverNat64Prefix(uint32_t aInfraIfIndex)418 extern "C" otError otPlatInfraIfDiscoverNat64Prefix(uint32_t aInfraIfIndex)
419 {
420 OT_UNUSED_VARIABLE(aInfraIfIndex);
421
422 AndroidRcpHost *androidRcpHost = AndroidRcpHost::Get();
423 otError error = OT_ERROR_NONE;
424
425 VerifyOrExit(androidRcpHost != nullptr, error = OT_ERROR_INVALID_STATE);
426
427 androidRcpHost->NotifyNat64PrefixDiscoveryDone();
428
429 exit:
430 return error;
431 }
432
433 } // namespace Android
434 } // namespace otbr
435