1 /*
2 * Copyright (c) 2019, 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 "NCP"
30
31 #include "ncp/ncp_openthread.hpp"
32
33 #include <assert.h>
34 #include <stdio.h>
35 #include <string.h>
36
37 #include <openthread/backbone_router_ftd.h>
38 #include <openthread/border_routing.h>
39 #include <openthread/dataset.h>
40 #include <openthread/dnssd_server.h>
41 #include <openthread/logging.h>
42 #include <openthread/nat64.h>
43 #include <openthread/srp_server.h>
44 #include <openthread/tasklet.h>
45 #include <openthread/thread.h>
46 #include <openthread/thread_ftd.h>
47 #include <openthread/trel.h>
48 #include <openthread/platform/logging.h>
49 #include <openthread/platform/misc.h>
50 #include <openthread/platform/radio.h>
51 #include <openthread/platform/settings.h>
52
53 #include "common/code_utils.hpp"
54 #include "common/logging.hpp"
55 #include "common/types.hpp"
56 #if OTBR_ENABLE_FEATURE_FLAGS
57 #include "proto/feature_flag.pb.h"
58 #endif
59
60 namespace otbr {
61 namespace Ncp {
62
63 static const uint16_t kThreadVersion11 = 2; ///< Thread Version 1.1
64 static const uint16_t kThreadVersion12 = 3; ///< Thread Version 1.2
65 static const uint16_t kThreadVersion13 = 4; ///< Thread Version 1.3
66 static const uint16_t kThreadVersion14 = 5; ///< Thread Version 1.4
67
ControllerOpenThread(const char * aInterfaceName,const std::vector<const char * > & aRadioUrls,const char * aBackboneInterfaceName,bool aDryRun,bool aEnableAutoAttach)68 ControllerOpenThread::ControllerOpenThread(const char *aInterfaceName,
69 const std::vector<const char *> &aRadioUrls,
70 const char *aBackboneInterfaceName,
71 bool aDryRun,
72 bool aEnableAutoAttach)
73 : mInstance(nullptr)
74 , mEnableAutoAttach(aEnableAutoAttach)
75 {
76 VerifyOrDie(aRadioUrls.size() <= OT_PLATFORM_CONFIG_MAX_RADIO_URLS, "Too many Radio URLs!");
77
78 memset(&mConfig, 0, sizeof(mConfig));
79
80 mConfig.mInterfaceName = aInterfaceName;
81 mConfig.mBackboneInterfaceName = aBackboneInterfaceName;
82 mConfig.mDryRun = aDryRun;
83
84 for (const char *url : aRadioUrls)
85 {
86 mConfig.mRadioUrls[mConfig.mRadioUrlNum++] = url;
87 }
88 mConfig.mSpeedUpFactor = 1;
89 }
90
~ControllerOpenThread(void)91 ControllerOpenThread::~ControllerOpenThread(void)
92 {
93 // Make sure OpenThread Instance was gracefully de-initialized.
94 assert(mInstance == nullptr);
95 }
96
ConvertToOtbrLogLevel(otLogLevel aLogLevel)97 otbrLogLevel ControllerOpenThread::ConvertToOtbrLogLevel(otLogLevel aLogLevel)
98 {
99 otbrLogLevel otbrLogLevel;
100
101 switch (aLogLevel)
102 {
103 case OT_LOG_LEVEL_NONE:
104 otbrLogLevel = OTBR_LOG_EMERG;
105 break;
106 case OT_LOG_LEVEL_CRIT:
107 otbrLogLevel = OTBR_LOG_CRIT;
108 break;
109 case OT_LOG_LEVEL_WARN:
110 otbrLogLevel = OTBR_LOG_WARNING;
111 break;
112 case OT_LOG_LEVEL_NOTE:
113 otbrLogLevel = OTBR_LOG_NOTICE;
114 break;
115 case OT_LOG_LEVEL_INFO:
116 otbrLogLevel = OTBR_LOG_INFO;
117 break;
118 case OT_LOG_LEVEL_DEBG:
119 default:
120 otbrLogLevel = OTBR_LOG_DEBUG;
121 break;
122 }
123
124 return otbrLogLevel;
125 }
126
127 #if OTBR_ENABLE_FEATURE_FLAGS
128 /* Converts ProtoLogLevel to otbrLogLevel */
ConvertProtoToOtbrLogLevel(ProtoLogLevel aProtoLogLevel)129 otbrLogLevel ConvertProtoToOtbrLogLevel(ProtoLogLevel aProtoLogLevel)
130 {
131 otbrLogLevel otbrLogLevel;
132
133 switch (aProtoLogLevel)
134 {
135 case PROTO_LOG_EMERG:
136 otbrLogLevel = OTBR_LOG_EMERG;
137 break;
138 case PROTO_LOG_ALERT:
139 otbrLogLevel = OTBR_LOG_ALERT;
140 break;
141 case PROTO_LOG_CRIT:
142 otbrLogLevel = OTBR_LOG_CRIT;
143 break;
144 case PROTO_LOG_ERR:
145 otbrLogLevel = OTBR_LOG_ERR;
146 break;
147 case PROTO_LOG_WARNING:
148 otbrLogLevel = OTBR_LOG_WARNING;
149 break;
150 case PROTO_LOG_NOTICE:
151 otbrLogLevel = OTBR_LOG_NOTICE;
152 break;
153 case PROTO_LOG_INFO:
154 otbrLogLevel = OTBR_LOG_INFO;
155 break;
156 case PROTO_LOG_DEBUG:
157 default:
158 otbrLogLevel = OTBR_LOG_DEBUG;
159 break;
160 }
161
162 return otbrLogLevel;
163 }
164 #endif
165
ConvertToOtLogLevel(otbrLogLevel aLevel)166 otLogLevel ControllerOpenThread::ConvertToOtLogLevel(otbrLogLevel aLevel)
167 {
168 otLogLevel level;
169
170 switch (aLevel)
171 {
172 case OTBR_LOG_EMERG:
173 case OTBR_LOG_ALERT:
174 case OTBR_LOG_CRIT:
175 level = OT_LOG_LEVEL_CRIT;
176 break;
177 case OTBR_LOG_ERR:
178 case OTBR_LOG_WARNING:
179 level = OT_LOG_LEVEL_WARN;
180 break;
181 case OTBR_LOG_NOTICE:
182 level = OT_LOG_LEVEL_NOTE;
183 break;
184 case OTBR_LOG_INFO:
185 level = OT_LOG_LEVEL_INFO;
186 break;
187 case OTBR_LOG_DEBUG:
188 default:
189 level = OT_LOG_LEVEL_DEBG;
190 break;
191 }
192
193 return level;
194 }
195
SetOtbrAndOtLogLevel(otbrLogLevel aLevel)196 otError ControllerOpenThread::SetOtbrAndOtLogLevel(otbrLogLevel aLevel)
197 {
198 otError error = OT_ERROR_NONE;
199 otbrLogSetLevel(aLevel);
200 error = otLoggingSetLevel(ConvertToOtLogLevel(aLevel));
201 return error;
202 }
203
Init(void)204 void ControllerOpenThread::Init(void)
205 {
206 otbrError error = OTBR_ERROR_NONE;
207 otLogLevel level = ConvertToOtLogLevel(otbrLogGetLevel());
208
209 #if OTBR_ENABLE_FEATURE_FLAGS && OTBR_ENABLE_TREL
210 FeatureFlagList featureFlagList;
211 #endif
212
213 VerifyOrExit(otLoggingSetLevel(level) == OT_ERROR_NONE, error = OTBR_ERROR_OPENTHREAD);
214
215 mInstance = otSysInit(&mConfig);
216 assert(mInstance != nullptr);
217
218 {
219 otError result = otSetStateChangedCallback(mInstance, &ControllerOpenThread::HandleStateChanged, this);
220
221 agent::ThreadHelper::LogOpenThreadResult("Set state callback", result);
222 VerifyOrExit(result == OT_ERROR_NONE, error = OTBR_ERROR_OPENTHREAD);
223 }
224
225 #if OTBR_ENABLE_FEATURE_FLAGS && OTBR_ENABLE_TREL
226 // Enable/Disable trel according to feature flag default value.
227 otTrelSetEnabled(mInstance, featureFlagList.enable_trel());
228 #endif
229
230 #if OTBR_ENABLE_SRP_ADVERTISING_PROXY
231 #if OTBR_ENABLE_SRP_SERVER_AUTO_ENABLE_MODE
232 // Let SRP server use auto-enable mode. The auto-enable mode delegates the control of SRP server to the Border
233 // Routing Manager. SRP server automatically starts when bi-directional connectivity is ready.
234 otSrpServerSetAutoEnableMode(mInstance, /* aEnabled */ true);
235 #else
236 otSrpServerSetEnabled(mInstance, /* aEnabled */ true);
237 #endif
238 #endif
239
240 #if !OTBR_ENABLE_FEATURE_FLAGS
241 // Bring up all features when feature flags is not supported.
242 #if OTBR_ENABLE_NAT64
243 otNat64SetEnabled(mInstance, /* aEnabled */ true);
244 #endif
245 #if OTBR_ENABLE_DNS_UPSTREAM_QUERY
246 otDnssdUpstreamQuerySetEnabled(mInstance, /* aEnabled */ true);
247 #endif
248 #if OTBR_ENABLE_DHCP6_PD
249 otBorderRoutingDhcp6PdSetEnabled(mInstance, /* aEnabled */ true);
250 #endif
251 #endif // OTBR_ENABLE_FEATURE_FLAGS
252
253 mThreadHelper = std::unique_ptr<otbr::agent::ThreadHelper>(new otbr::agent::ThreadHelper(mInstance, this));
254
255 exit:
256 SuccessOrDie(error, "Failed to initialize NCP!");
257 }
258
259 #if OTBR_ENABLE_FEATURE_FLAGS
ApplyFeatureFlagList(const FeatureFlagList & aFeatureFlagList)260 otError ControllerOpenThread::ApplyFeatureFlagList(const FeatureFlagList &aFeatureFlagList)
261 {
262 otError error = OT_ERROR_NONE;
263 // Save a cached copy of feature flags for debugging purpose.
264 mAppliedFeatureFlagListBytes = aFeatureFlagList.SerializeAsString();
265
266 #if OTBR_ENABLE_NAT64
267 otNat64SetEnabled(mInstance, aFeatureFlagList.enable_nat64());
268 #endif
269
270 if (aFeatureFlagList.enable_detailed_logging())
271 {
272 error = SetOtbrAndOtLogLevel(ConvertProtoToOtbrLogLevel(aFeatureFlagList.detailed_logging_level()));
273 }
274 else
275 {
276 error = SetOtbrAndOtLogLevel(otbrLogGetDefaultLevel());
277 }
278
279 #if OTBR_ENABLE_TREL
280 otTrelSetEnabled(mInstance, aFeatureFlagList.enable_trel());
281 #endif
282 #if OTBR_ENABLE_DNS_UPSTREAM_QUERY
283 otDnssdUpstreamQuerySetEnabled(mInstance, aFeatureFlagList.enable_dns_upstream_query());
284 #endif
285 #if OTBR_ENABLE_DHCP6_PD
286 otBorderRoutingDhcp6PdSetEnabled(mInstance, aFeatureFlagList.enable_dhcp6_pd());
287 #endif
288
289 return error;
290 }
291 #endif
292
Deinit(void)293 void ControllerOpenThread::Deinit(void)
294 {
295 assert(mInstance != nullptr);
296
297 otSysDeinit();
298 mInstance = nullptr;
299
300 mThreadStateChangedCallbacks.clear();
301 mResetHandlers.clear();
302 }
303
HandleStateChanged(otChangedFlags aFlags)304 void ControllerOpenThread::HandleStateChanged(otChangedFlags aFlags)
305 {
306 for (auto &stateCallback : mThreadStateChangedCallbacks)
307 {
308 stateCallback(aFlags);
309 }
310
311 mThreadHelper->StateChangedCallback(aFlags);
312 }
313
Update(MainloopContext & aMainloop)314 void ControllerOpenThread::Update(MainloopContext &aMainloop)
315 {
316 if (otTaskletsArePending(mInstance))
317 {
318 aMainloop.mTimeout = ToTimeval(Microseconds::zero());
319 }
320
321 otSysMainloopUpdate(mInstance, &aMainloop);
322 }
323
Process(const MainloopContext & aMainloop)324 void ControllerOpenThread::Process(const MainloopContext &aMainloop)
325 {
326 otTaskletsProcess(mInstance);
327
328 otSysMainloopProcess(mInstance, &aMainloop);
329
330 if (IsAutoAttachEnabled() && mThreadHelper->TryResumeNetwork() == OT_ERROR_NONE)
331 {
332 DisableAutoAttach();
333 }
334 }
335
IsAutoAttachEnabled(void)336 bool ControllerOpenThread::IsAutoAttachEnabled(void)
337 {
338 return mEnableAutoAttach;
339 }
340
DisableAutoAttach(void)341 void ControllerOpenThread::DisableAutoAttach(void)
342 {
343 mEnableAutoAttach = false;
344 }
345
PostTimerTask(Milliseconds aDelay,TaskRunner::Task<void> aTask)346 void ControllerOpenThread::PostTimerTask(Milliseconds aDelay, TaskRunner::Task<void> aTask)
347 {
348 mTaskRunner.Post(std::move(aDelay), std::move(aTask));
349 }
350
RegisterResetHandler(std::function<void (void)> aHandler)351 void ControllerOpenThread::RegisterResetHandler(std::function<void(void)> aHandler)
352 {
353 mResetHandlers.emplace_back(std::move(aHandler));
354 }
355
AddThreadStateChangedCallback(ThreadStateChangedCallback aCallback)356 void ControllerOpenThread::AddThreadStateChangedCallback(ThreadStateChangedCallback aCallback)
357 {
358 mThreadStateChangedCallbacks.emplace_back(std::move(aCallback));
359 }
360
Reset(void)361 void ControllerOpenThread::Reset(void)
362 {
363 gPlatResetReason = OT_PLAT_RESET_REASON_SOFTWARE;
364
365 otSysDeinit();
366 mInstance = nullptr;
367
368 Init();
369 for (auto &handler : mResetHandlers)
370 {
371 handler();
372 }
373 mEnableAutoAttach = true;
374 }
375
GetThreadVersion(void)376 const char *ControllerOpenThread::GetThreadVersion(void)
377 {
378 const char *version;
379
380 switch (otThreadGetVersion())
381 {
382 case kThreadVersion11:
383 version = "1.1.1";
384 break;
385 case kThreadVersion12:
386 version = "1.2.0";
387 break;
388 case kThreadVersion13:
389 version = "1.3.0";
390 break;
391 case kThreadVersion14:
392 version = "1.4";
393 break;
394 default:
395 otbrLogEmerg("Unexpected thread version %hu", otThreadGetVersion());
396 exit(-1);
397 }
398 return version;
399 }
400
401 /*
402 * Provide, if required an "otPlatLog()" function
403 */
otPlatLog(otLogLevel aLogLevel,otLogRegion aLogRegion,const char * aFormat,...)404 extern "C" void otPlatLog(otLogLevel aLogLevel, otLogRegion aLogRegion, const char *aFormat, ...)
405 {
406 OT_UNUSED_VARIABLE(aLogRegion);
407
408 otbrLogLevel otbrLogLevel = ControllerOpenThread::ConvertToOtbrLogLevel(aLogLevel);
409
410 va_list ap;
411 va_start(ap, aFormat);
412 otbrLogvNoFilter(otbrLogLevel, aFormat, ap);
413 va_end(ap);
414 }
415
otPlatLogHandleLevelChanged(otLogLevel aLogLevel)416 extern "C" void otPlatLogHandleLevelChanged(otLogLevel aLogLevel)
417 {
418 otbrLogSetLevel(ControllerOpenThread::ConvertToOtbrLogLevel(aLogLevel));
419 otbrLogInfo("OpenThread log level changed to %d", aLogLevel);
420 }
421
422 } // namespace Ncp
423 } // namespace otbr
424