1 /*
2 * Copyright (c) 2020, 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 "REST"
30
31 #include "rest/resource.hpp"
32
33 #include "string.h"
34
35 #define OT_PSKC_MAX_LENGTH 16
36 #define OT_EXTENDED_PANID_LENGTH 8
37
38 #define OT_REST_RESOURCE_PATH_DIAGNOETIC "/diagnostics"
39 #define OT_REST_RESOURCE_PATH_NODE "/node"
40 #define OT_REST_RESOURCE_PATH_NODE_RLOC "/node/rloc"
41 #define OT_REST_RESOURCE_PATH_NODE_RLOC16 "/node/rloc16"
42 #define OT_REST_RESOURCE_PATH_NODE_EXTADDRESS "/node/ext-address"
43 #define OT_REST_RESOURCE_PATH_NODE_STATE "/node/state"
44 #define OT_REST_RESOURCE_PATH_NODE_NETWORKNAME "/node/network-name"
45 #define OT_REST_RESOURCE_PATH_NODE_LEADERDATA "/node/leader-data"
46 #define OT_REST_RESOURCE_PATH_NODE_NUMOFROUTER "/node/num-of-router"
47 #define OT_REST_RESOURCE_PATH_NODE_EXTPANID "/node/ext-panid"
48 #define OT_REST_RESOURCE_PATH_NETWORK "/networks"
49 #define OT_REST_RESOURCE_PATH_NETWORK_CURRENT "/networks/current"
50 #define OT_REST_RESOURCE_PATH_NETWORK_CURRENT_COMMISSION "/networks/commission"
51 #define OT_REST_RESOURCE_PATH_NETWORK_CURRENT_PREFIX "/networks/current/prefix"
52
53 #define OT_REST_HTTP_STATUS_200 "200 OK"
54 #define OT_REST_HTTP_STATUS_404 "404 Not Found"
55 #define OT_REST_HTTP_STATUS_405 "405 Method Not Allowed"
56 #define OT_REST_HTTP_STATUS_408 "408 Request Timeout"
57 #define OT_REST_HTTP_STATUS_500 "500 Internal Server Error"
58
59 using std::chrono::duration_cast;
60 using std::chrono::microseconds;
61 using std::chrono::steady_clock;
62
63 using std::placeholders::_1;
64 using std::placeholders::_2;
65
66 namespace otbr {
67 namespace rest {
68
69 // MulticastAddr
70 static const char *kMulticastAddrAllRouters = "ff03::2";
71
72 // Default TlvTypes for Diagnostic inforamtion
73 static const uint8_t kAllTlvTypes[] = {0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 14, 15, 16, 17, 19};
74
75 // Timeout (in Microseconds) for deleting outdated diagnostics
76 static const uint32_t kDiagResetTimeout = 3000000;
77
78 // Timeout (in Microseconds) for collecting diagnostics
79 static const uint32_t kDiagCollectTimeout = 2000000;
80
GetHttpStatus(HttpStatusCode aErrorCode)81 static std::string GetHttpStatus(HttpStatusCode aErrorCode)
82 {
83 std::string httpStatus;
84
85 switch (aErrorCode)
86 {
87 case HttpStatusCode::kStatusOk:
88 httpStatus = OT_REST_HTTP_STATUS_200;
89 break;
90 case HttpStatusCode::kStatusResourceNotFound:
91 httpStatus = OT_REST_HTTP_STATUS_404;
92 break;
93 case HttpStatusCode::kStatusMethodNotAllowed:
94 httpStatus = OT_REST_HTTP_STATUS_405;
95 break;
96 case HttpStatusCode::kStatusRequestTimeout:
97 httpStatus = OT_REST_HTTP_STATUS_408;
98 break;
99 case HttpStatusCode::kStatusInternalServerError:
100 httpStatus = OT_REST_HTTP_STATUS_500;
101 break;
102 }
103
104 return httpStatus;
105 }
106
Resource(ControllerOpenThread * aNcp)107 Resource::Resource(ControllerOpenThread *aNcp)
108 : mInstance(nullptr)
109 , mNcp(aNcp)
110 {
111 // Resource Handler
112 mResourceMap.emplace(OT_REST_RESOURCE_PATH_DIAGNOETIC, &Resource::Diagnostic);
113 mResourceMap.emplace(OT_REST_RESOURCE_PATH_NODE, &Resource::NodeInfo);
114 mResourceMap.emplace(OT_REST_RESOURCE_PATH_NODE_STATE, &Resource::State);
115 mResourceMap.emplace(OT_REST_RESOURCE_PATH_NODE_EXTADDRESS, &Resource::ExtendedAddr);
116 mResourceMap.emplace(OT_REST_RESOURCE_PATH_NODE_NETWORKNAME, &Resource::NetworkName);
117 mResourceMap.emplace(OT_REST_RESOURCE_PATH_NODE_RLOC16, &Resource::Rloc16);
118 mResourceMap.emplace(OT_REST_RESOURCE_PATH_NODE_LEADERDATA, &Resource::LeaderData);
119 mResourceMap.emplace(OT_REST_RESOURCE_PATH_NODE_NUMOFROUTER, &Resource::NumOfRoute);
120 mResourceMap.emplace(OT_REST_RESOURCE_PATH_NODE_EXTPANID, &Resource::ExtendedPanId);
121 mResourceMap.emplace(OT_REST_RESOURCE_PATH_NODE_RLOC, &Resource::Rloc);
122
123 // Resource callback handler
124 mResourceCallbackMap.emplace(OT_REST_RESOURCE_PATH_DIAGNOETIC, &Resource::HandleDiagnosticCallback);
125 }
126
Init(void)127 void Resource::Init(void)
128 {
129 mInstance = mNcp->GetThreadHelper()->GetInstance();
130 }
131
Handle(Request & aRequest,Response & aResponse) const132 void Resource::Handle(Request &aRequest, Response &aResponse) const
133 {
134 std::string url = aRequest.GetUrl();
135 auto it = mResourceMap.find(url);
136
137 if (it != mResourceMap.end())
138 {
139 ResourceHandler resourceHandler = it->second;
140 (this->*resourceHandler)(aRequest, aResponse);
141 }
142 else
143 {
144 ErrorHandler(aResponse, HttpStatusCode::kStatusResourceNotFound);
145 }
146 }
147
HandleCallback(Request & aRequest,Response & aResponse)148 void Resource::HandleCallback(Request &aRequest, Response &aResponse)
149 {
150 std::string url = aRequest.GetUrl();
151 auto it = mResourceCallbackMap.find(url);
152
153 if (it != mResourceCallbackMap.end())
154 {
155 ResourceCallbackHandler resourceHandler = it->second;
156 (this->*resourceHandler)(aRequest, aResponse);
157 }
158 }
159
HandleDiagnosticCallback(const Request & aRequest,Response & aResponse)160 void Resource::HandleDiagnosticCallback(const Request &aRequest, Response &aResponse)
161 {
162 OT_UNUSED_VARIABLE(aRequest);
163 std::vector<std::vector<otNetworkDiagTlv>> diagContentSet;
164 std::string body;
165 std::string errorCode;
166
167 auto duration = duration_cast<microseconds>(steady_clock::now() - aResponse.GetStartTime()).count();
168 if (duration >= kDiagCollectTimeout)
169 {
170 DeleteOutDatedDiagnostic();
171
172 for (auto it = mDiagSet.begin(); it != mDiagSet.end(); ++it)
173 {
174 diagContentSet.push_back(it->second.mDiagContent);
175 }
176
177 body = Json::Diag2JsonString(diagContentSet);
178 errorCode = GetHttpStatus(HttpStatusCode::kStatusOk);
179 aResponse.SetResponsCode(errorCode);
180 aResponse.SetBody(body);
181 aResponse.SetComplete();
182 }
183 }
184
ErrorHandler(Response & aResponse,HttpStatusCode aErrorCode) const185 void Resource::ErrorHandler(Response &aResponse, HttpStatusCode aErrorCode) const
186 {
187 std::string errorMessage = GetHttpStatus(aErrorCode);
188 std::string body = Json::Error2JsonString(aErrorCode, errorMessage);
189
190 aResponse.SetResponsCode(errorMessage);
191 aResponse.SetBody(body);
192 aResponse.SetComplete();
193 }
194
GetNodeInfo(Response & aResponse) const195 void Resource::GetNodeInfo(Response &aResponse) const
196 {
197 otbrError error = OTBR_ERROR_NONE;
198 struct NodeInfo node;
199 otRouterInfo routerInfo;
200 uint8_t maxRouterId;
201 std::string body;
202 std::string errorCode;
203
204 VerifyOrExit(otThreadGetLeaderData(mInstance, &node.mLeaderData) == OT_ERROR_NONE, error = OTBR_ERROR_REST);
205
206 node.mNumOfRouter = 0;
207 maxRouterId = otThreadGetMaxRouterId(mInstance);
208 for (uint8_t i = 0; i <= maxRouterId; ++i)
209 {
210 if (otThreadGetRouterInfo(mInstance, i, &routerInfo) != OT_ERROR_NONE)
211 {
212 continue;
213 }
214 ++node.mNumOfRouter;
215 }
216
217 node.mRole = otThreadGetDeviceRole(mInstance);
218 node.mExtAddress = reinterpret_cast<const uint8_t *>(otLinkGetExtendedAddress(mInstance));
219 node.mNetworkName = otThreadGetNetworkName(mInstance);
220 node.mRloc16 = otThreadGetRloc16(mInstance);
221 node.mExtPanId = reinterpret_cast<const uint8_t *>(otThreadGetExtendedPanId(mInstance));
222 node.mRlocAddress = *otThreadGetRloc(mInstance);
223
224 body = Json::Node2JsonString(node);
225 aResponse.SetBody(body);
226
227 exit:
228 if (error == OTBR_ERROR_NONE)
229 {
230 errorCode = GetHttpStatus(HttpStatusCode::kStatusOk);
231 aResponse.SetResponsCode(errorCode);
232 }
233 else
234 {
235 ErrorHandler(aResponse, HttpStatusCode::kStatusInternalServerError);
236 }
237 }
238
NodeInfo(const Request & aRequest,Response & aResponse) const239 void Resource::NodeInfo(const Request &aRequest, Response &aResponse) const
240 {
241 std::string errorCode;
242 if (aRequest.GetMethod() == HttpMethod::kGet)
243 {
244 GetNodeInfo(aResponse);
245 }
246 else
247 {
248 ErrorHandler(aResponse, HttpStatusCode::kStatusMethodNotAllowed);
249 }
250 }
251
GetDataExtendedAddr(Response & aResponse) const252 void Resource::GetDataExtendedAddr(Response &aResponse) const
253 {
254 const uint8_t *extAddress = reinterpret_cast<const uint8_t *>(otLinkGetExtendedAddress(mInstance));
255 std::string errorCode;
256 std::string body = Json::Bytes2HexJsonString(extAddress, OT_EXT_ADDRESS_SIZE);
257
258 aResponse.SetBody(body);
259 errorCode = GetHttpStatus(HttpStatusCode::kStatusOk);
260 aResponse.SetResponsCode(errorCode);
261 }
262
ExtendedAddr(const Request & aRequest,Response & aResponse) const263 void Resource::ExtendedAddr(const Request &aRequest, Response &aResponse) const
264 {
265 std::string errorCode;
266
267 if (aRequest.GetMethod() == HttpMethod::kGet)
268 {
269 GetDataExtendedAddr(aResponse);
270 }
271 else
272 {
273 ErrorHandler(aResponse, HttpStatusCode::kStatusMethodNotAllowed);
274 }
275 }
276
GetDataState(Response & aResponse) const277 void Resource::GetDataState(Response &aResponse) const
278 {
279 std::string state;
280 std::string errorCode;
281 uint8_t role;
282 // 0 : disabled
283 // 1 : detached
284 // 2 : child
285 // 3 : router
286 // 4 : leader
287
288 role = otThreadGetDeviceRole(mInstance);
289 state = Json::Number2JsonString(role);
290 aResponse.SetBody(state);
291 errorCode = GetHttpStatus(HttpStatusCode::kStatusOk);
292 aResponse.SetResponsCode(errorCode);
293 }
294
State(const Request & aRequest,Response & aResponse) const295 void Resource::State(const Request &aRequest, Response &aResponse) const
296 {
297 std::string errorCode;
298
299 if (aRequest.GetMethod() == HttpMethod::kGet)
300 {
301 GetDataState(aResponse);
302 }
303 else
304 {
305 ErrorHandler(aResponse, HttpStatusCode::kStatusMethodNotAllowed);
306 }
307 }
308
GetDataNetworkName(Response & aResponse) const309 void Resource::GetDataNetworkName(Response &aResponse) const
310 {
311 std::string networkName;
312 std::string errorCode;
313
314 networkName = otThreadGetNetworkName(mInstance);
315 networkName = Json::String2JsonString(networkName);
316
317 aResponse.SetBody(networkName);
318 errorCode = GetHttpStatus(HttpStatusCode::kStatusOk);
319 aResponse.SetResponsCode(errorCode);
320 }
321
NetworkName(const Request & aRequest,Response & aResponse) const322 void Resource::NetworkName(const Request &aRequest, Response &aResponse) const
323 {
324 std::string errorCode;
325
326 if (aRequest.GetMethod() == HttpMethod::kGet)
327 {
328 GetDataNetworkName(aResponse);
329 }
330 else
331 {
332 ErrorHandler(aResponse, HttpStatusCode::kStatusMethodNotAllowed);
333 }
334 }
335
GetDataLeaderData(Response & aResponse) const336 void Resource::GetDataLeaderData(Response &aResponse) const
337 {
338 otbrError error = OTBR_ERROR_NONE;
339 otLeaderData leaderData;
340 std::string body;
341 std::string errorCode;
342
343 VerifyOrExit(otThreadGetLeaderData(mInstance, &leaderData) == OT_ERROR_NONE, error = OTBR_ERROR_REST);
344
345 body = Json::LeaderData2JsonString(leaderData);
346
347 aResponse.SetBody(body);
348
349 exit:
350 if (error == OTBR_ERROR_NONE)
351 {
352 errorCode = GetHttpStatus(HttpStatusCode::kStatusOk);
353 aResponse.SetResponsCode(errorCode);
354 }
355 else
356 {
357 ErrorHandler(aResponse, HttpStatusCode::kStatusInternalServerError);
358 }
359 }
360
LeaderData(const Request & aRequest,Response & aResponse) const361 void Resource::LeaderData(const Request &aRequest, Response &aResponse) const
362 {
363 std::string errorCode;
364 if (aRequest.GetMethod() == HttpMethod::kGet)
365 {
366 GetDataLeaderData(aResponse);
367 }
368 else
369 {
370 ErrorHandler(aResponse, HttpStatusCode::kStatusMethodNotAllowed);
371 }
372 }
373
GetDataNumOfRoute(Response & aResponse) const374 void Resource::GetDataNumOfRoute(Response &aResponse) const
375 {
376 uint8_t count = 0;
377 uint8_t maxRouterId;
378 otRouterInfo routerInfo;
379 maxRouterId = otThreadGetMaxRouterId(mInstance);
380 std::string body;
381 std::string errorCode;
382
383 for (uint8_t i = 0; i <= maxRouterId; ++i)
384 {
385 if (otThreadGetRouterInfo(mInstance, i, &routerInfo) != OT_ERROR_NONE)
386 {
387 continue;
388 }
389 ++count;
390 }
391
392 body = Json::Number2JsonString(count);
393
394 aResponse.SetBody(body);
395 errorCode = GetHttpStatus(HttpStatusCode::kStatusOk);
396 aResponse.SetResponsCode(errorCode);
397 }
398
NumOfRoute(const Request & aRequest,Response & aResponse) const399 void Resource::NumOfRoute(const Request &aRequest, Response &aResponse) const
400 {
401 std::string errorCode;
402
403 if (aRequest.GetMethod() == HttpMethod::kGet)
404 {
405 GetDataNumOfRoute(aResponse);
406 }
407 else
408 {
409 ErrorHandler(aResponse, HttpStatusCode::kStatusMethodNotAllowed);
410 }
411 }
412
GetDataRloc16(Response & aResponse) const413 void Resource::GetDataRloc16(Response &aResponse) const
414 {
415 uint16_t rloc16 = otThreadGetRloc16(mInstance);
416 std::string body;
417 std::string errorCode;
418
419 body = Json::Number2JsonString(rloc16);
420
421 aResponse.SetBody(body);
422 errorCode = GetHttpStatus(HttpStatusCode::kStatusOk);
423 aResponse.SetResponsCode(errorCode);
424 }
425
Rloc16(const Request & aRequest,Response & aResponse) const426 void Resource::Rloc16(const Request &aRequest, Response &aResponse) const
427 {
428 std::string errorCode;
429
430 if (aRequest.GetMethod() == HttpMethod::kGet)
431 {
432 GetDataRloc16(aResponse);
433 }
434 else
435 {
436 ErrorHandler(aResponse, HttpStatusCode::kStatusMethodNotAllowed);
437 }
438 }
439
GetDataExtendedPanId(Response & aResponse) const440 void Resource::GetDataExtendedPanId(Response &aResponse) const
441 {
442 const uint8_t *extPanId = reinterpret_cast<const uint8_t *>(otThreadGetExtendedPanId(mInstance));
443 std::string body = Json::Bytes2HexJsonString(extPanId, OT_EXT_PAN_ID_SIZE);
444 std::string errorCode;
445
446 aResponse.SetBody(body);
447 errorCode = GetHttpStatus(HttpStatusCode::kStatusOk);
448 aResponse.SetResponsCode(errorCode);
449 }
450
ExtendedPanId(const Request & aRequest,Response & aResponse) const451 void Resource::ExtendedPanId(const Request &aRequest, Response &aResponse) const
452 {
453 std::string errorCode;
454
455 if (aRequest.GetMethod() == HttpMethod::kGet)
456 {
457 GetDataExtendedPanId(aResponse);
458 }
459 else
460 {
461 ErrorHandler(aResponse, HttpStatusCode::kStatusMethodNotAllowed);
462 }
463 }
464
GetDataRloc(Response & aResponse) const465 void Resource::GetDataRloc(Response &aResponse) const
466 {
467 otIp6Address rlocAddress = *otThreadGetRloc(mInstance);
468 std::string body;
469 std::string errorCode;
470
471 body = Json::IpAddr2JsonString(rlocAddress);
472
473 aResponse.SetBody(body);
474 errorCode = GetHttpStatus(HttpStatusCode::kStatusOk);
475 aResponse.SetResponsCode(errorCode);
476 }
477
Rloc(const Request & aRequest,Response & aResponse) const478 void Resource::Rloc(const Request &aRequest, Response &aResponse) const
479 {
480 std::string errorCode;
481
482 if (aRequest.GetMethod() == HttpMethod::kGet)
483 {
484 GetDataRloc(aResponse);
485 }
486 else
487 {
488 ErrorHandler(aResponse, HttpStatusCode::kStatusMethodNotAllowed);
489 }
490 }
491
DeleteOutDatedDiagnostic(void)492 void Resource::DeleteOutDatedDiagnostic(void)
493 {
494 auto eraseIt = mDiagSet.begin();
495 for (eraseIt = mDiagSet.begin(); eraseIt != mDiagSet.end();)
496 {
497 auto diagInfo = eraseIt->second;
498 auto duration = duration_cast<microseconds>(steady_clock::now() - diagInfo.mStartTime).count();
499
500 if (duration >= kDiagResetTimeout)
501 {
502 eraseIt = mDiagSet.erase(eraseIt);
503 }
504 else
505 {
506 eraseIt++;
507 }
508 }
509 }
510
UpdateDiag(std::string aKey,std::vector<otNetworkDiagTlv> & aDiag)511 void Resource::UpdateDiag(std::string aKey, std::vector<otNetworkDiagTlv> &aDiag)
512 {
513 DiagInfo value;
514
515 value.mStartTime = steady_clock::now();
516 value.mDiagContent.assign(aDiag.begin(), aDiag.end());
517 mDiagSet[aKey] = value;
518 }
519
Diagnostic(const Request & aRequest,Response & aResponse) const520 void Resource::Diagnostic(const Request &aRequest, Response &aResponse) const
521 {
522 otbrError error = OTBR_ERROR_NONE;
523 OT_UNUSED_VARIABLE(aRequest);
524 struct otIp6Address rloc16address = *otThreadGetRloc(mInstance);
525 struct otIp6Address multicastAddress;
526
527 VerifyOrExit(otThreadSendDiagnosticGet(mInstance, &rloc16address, kAllTlvTypes, sizeof(kAllTlvTypes),
528 &Resource::DiagnosticResponseHandler,
529 const_cast<Resource *>(this)) == OT_ERROR_NONE,
530 error = OTBR_ERROR_REST);
531 VerifyOrExit(otIp6AddressFromString(kMulticastAddrAllRouters, &multicastAddress) == OT_ERROR_NONE,
532 error = OTBR_ERROR_REST);
533 VerifyOrExit(otThreadSendDiagnosticGet(mInstance, &multicastAddress, kAllTlvTypes, sizeof(kAllTlvTypes),
534 &Resource::DiagnosticResponseHandler,
535 const_cast<Resource *>(this)) == OT_ERROR_NONE,
536 error = OTBR_ERROR_REST);
537
538 exit:
539
540 if (error == OTBR_ERROR_NONE)
541 {
542 aResponse.SetStartTime(steady_clock::now());
543 aResponse.SetCallback();
544 }
545 else
546 {
547 ErrorHandler(aResponse, HttpStatusCode::kStatusInternalServerError);
548 }
549 }
550
DiagnosticResponseHandler(otError aError,otMessage * aMessage,const otMessageInfo * aMessageInfo,void * aContext)551 void Resource::DiagnosticResponseHandler(otError aError,
552 otMessage * aMessage,
553 const otMessageInfo *aMessageInfo,
554 void * aContext)
555 {
556 static_cast<Resource *>(aContext)->DiagnosticResponseHandler(aError, aMessage, aMessageInfo);
557 }
558
DiagnosticResponseHandler(otError aError,const otMessage * aMessage,const otMessageInfo * aMessageInfo)559 void Resource::DiagnosticResponseHandler(otError aError, const otMessage *aMessage, const otMessageInfo *aMessageInfo)
560 {
561 std::vector<otNetworkDiagTlv> diagSet;
562 otNetworkDiagTlv diagTlv;
563 otNetworkDiagIterator iterator = OT_NETWORK_DIAGNOSTIC_ITERATOR_INIT;
564 otError error;
565 char rloc[7];
566 std::string keyRloc = "0xffee";
567
568 SuccessOrExit(aError);
569
570 OTBR_UNUSED_VARIABLE(aMessageInfo);
571
572 while ((error = otThreadGetNextDiagnosticTlv(aMessage, &iterator, &diagTlv)) == OT_ERROR_NONE)
573 {
574 if (diagTlv.mType == OT_NETWORK_DIAGNOSTIC_TLV_SHORT_ADDRESS)
575 {
576 sprintf(rloc, "0x%04x", diagTlv.mData.mAddr16);
577 keyRloc = Json::CString2JsonString(rloc);
578 }
579 diagSet.push_back(diagTlv);
580 }
581 UpdateDiag(keyRloc, diagSet);
582
583 exit:
584 if (aError != OT_ERROR_NONE)
585 {
586 otbrLogWarning("Failed to get diagnostic data: %s", otThreadErrorToString(aError));
587 }
588 }
589
590 } // namespace rest
591 } // namespace otbr
592