2 * Copyright (c) 2020, The OpenThread Authors.
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.
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.
29 #include "rest/resource.hpp"
33 #define OT_PSKC_MAX_LENGTH 16
34 #define OT_EXTENDED_PANID_LENGTH 8
36 #define OT_REST_RESOURCE_PATH_DIAGNOETIC "/diagnostics"
37 #define OT_REST_RESOURCE_PATH_NODE "/node"
38 #define OT_REST_RESOURCE_PATH_NODE_RLOC "/node/rloc"
39 #define OT_REST_RESOURCE_PATH_NODE_RLOC16 "/node/rloc16"
40 #define OT_REST_RESOURCE_PATH_NODE_EXTADDRESS "/node/ext-address"
41 #define OT_REST_RESOURCE_PATH_NODE_STATE "/node/state"
42 #define OT_REST_RESOURCE_PATH_NODE_NETWORKNAME "/node/network-name"
43 #define OT_REST_RESOURCE_PATH_NODE_LEADERDATA "/node/leader-data"
44 #define OT_REST_RESOURCE_PATH_NODE_NUMOFROUTER "/node/num-of-router"
45 #define OT_REST_RESOURCE_PATH_NODE_EXTPANID "/node/ext-panid"
46 #define OT_REST_RESOURCE_PATH_NETWORK "/networks"
47 #define OT_REST_RESOURCE_PATH_NETWORK_CURRENT "/networks/current"
48 #define OT_REST_RESOURCE_PATH_NETWORK_CURRENT_COMMISSION "/networks/commission"
49 #define OT_REST_RESOURCE_PATH_NETWORK_CURRENT_PREFIX "/networks/current/prefix"
51 #define OT_REST_HTTP_STATUS_200 "200 OK"
52 #define OT_REST_HTTP_STATUS_404 "404 Not Found"
53 #define OT_REST_HTTP_STATUS_405 "405 Method Not Allowed"
54 #define OT_REST_HTTP_STATUS_408 "408 Request Timeout"
55 #define OT_REST_HTTP_STATUS_500 "500 Internal Server Error"
57 using std::chrono::duration_cast;
58 using std::chrono::microseconds;
59 using std::chrono::steady_clock;
61 using std::placeholders::_1;
62 using std::placeholders::_2;
68 static const char *kMulticastAddrAllRouters = "ff02::2";
70 // Default TlvTypes for Diagnostic inforamtion
71 static const uint8_t kAllTlvTypes[] = {0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 14, 15, 16, 17, 18, 19};
73 // Timeout (in Microseconds) for deleting outdated diagnostics
74 static const uint32_t kDiagResetTimeout = 3000000;
76 // Timeout (in Microseconds) for collecting diagnostics
77 static const uint32_t kDiagCollectTimeout = 2000000;
79 static std::string GetHttpStatus(HttpStatusCode aErrorCode)
81 std::string httpStatus;
85 case HttpStatusCode::kStatusOk:
86 httpStatus = OT_REST_HTTP_STATUS_200;
88 case HttpStatusCode::kStatusResourceNotFound:
89 httpStatus = OT_REST_HTTP_STATUS_404;
91 case HttpStatusCode::kStatusMethodNotAllowed:
92 httpStatus = OT_REST_HTTP_STATUS_405;
94 case HttpStatusCode::kStatusRequestTimeout:
95 httpStatus = OT_REST_HTTP_STATUS_408;
97 case HttpStatusCode::kStatusInternalServerError:
98 httpStatus = OT_REST_HTTP_STATUS_500;
105 Resource::Resource(ControllerOpenThread *aNcp)
108 mInstance = mNcp->GetThreadHelper()->GetInstance();
111 mResourceMap.emplace(OT_REST_RESOURCE_PATH_DIAGNOETIC, &Resource::Diagnostic);
112 mResourceMap.emplace(OT_REST_RESOURCE_PATH_NODE, &Resource::NodeInfo);
113 mResourceMap.emplace(OT_REST_RESOURCE_PATH_NODE_STATE, &Resource::State);
114 mResourceMap.emplace(OT_REST_RESOURCE_PATH_NODE_EXTADDRESS, &Resource::ExtendedAddr);
115 mResourceMap.emplace(OT_REST_RESOURCE_PATH_NODE_NETWORKNAME, &Resource::NetworkName);
116 mResourceMap.emplace(OT_REST_RESOURCE_PATH_NODE_RLOC16, &Resource::Rloc16);
117 mResourceMap.emplace(OT_REST_RESOURCE_PATH_NODE_LEADERDATA, &Resource::LeaderData);
118 mResourceMap.emplace(OT_REST_RESOURCE_PATH_NODE_NUMOFROUTER, &Resource::NumOfRoute);
119 mResourceMap.emplace(OT_REST_RESOURCE_PATH_NODE_EXTPANID, &Resource::ExtendedPanId);
120 mResourceMap.emplace(OT_REST_RESOURCE_PATH_NODE_RLOC, &Resource::Rloc);
122 // Resource callback handler
123 mResourceCallbackMap.emplace(OT_REST_RESOURCE_PATH_DIAGNOETIC, &Resource::HandleDiagnosticCallback);
126 void Resource::Init(void)
130 void Resource::Handle(Request &aRequest, Response &aResponse) const
132 std::string url = aRequest.GetUrl();
133 auto it = mResourceMap.find(url);
135 if (it != mResourceMap.end())
137 ResourceHandler resourceHandler = it->second;
138 (this->*resourceHandler)(aRequest, aResponse);
142 ErrorHandler(aResponse, HttpStatusCode::kStatusResourceNotFound);
146 void Resource::HandleCallback(Request &aRequest, Response &aResponse)
148 std::string url = aRequest.GetUrl();
149 auto it = mResourceCallbackMap.find(url);
151 if (it != mResourceCallbackMap.end())
153 ResourceCallbackHandler resourceHandler = it->second;
154 (this->*resourceHandler)(aRequest, aResponse);
158 void Resource::HandleDiagnosticCallback(const Request &aRequest, Response &aResponse)
160 OT_UNUSED_VARIABLE(aRequest);
161 std::vector<std::vector<otNetworkDiagTlv>> diagContentSet;
163 std::string errorCode;
165 auto duration = duration_cast<microseconds>(steady_clock::now() - aResponse.GetStartTime()).count();
166 if (duration >= kDiagCollectTimeout)
168 DeleteOutDatedDiagnostic();
170 for (auto it = mDiagSet.begin(); it != mDiagSet.end(); ++it)
172 diagContentSet.push_back(it->second.mDiagContent);
175 body = Json::Diag2JsonString(diagContentSet);
176 errorCode = GetHttpStatus(HttpStatusCode::kStatusOk);
177 aResponse.SetResponsCode(errorCode);
178 aResponse.SetBody(body);
179 aResponse.SetComplete();
183 void Resource::ErrorHandler(Response &aResponse, HttpStatusCode aErrorCode) const
185 std::string errorMessage = GetHttpStatus(aErrorCode);
186 std::string body = Json::Error2JsonString(aErrorCode, errorMessage);
188 aResponse.SetResponsCode(errorMessage);
189 aResponse.SetBody(body);
190 aResponse.SetComplete();
193 void Resource::GetNodeInfo(Response &aResponse) const
195 otbrError error = OTBR_ERROR_NONE;
196 struct NodeInfo node;
197 otRouterInfo routerInfo;
200 std::string errorCode;
202 VerifyOrExit(otThreadGetLeaderData(mInstance, &node.mLeaderData) == OT_ERROR_NONE, error = OTBR_ERROR_REST);
204 node.mNumOfRouter = 0;
205 maxRouterId = otThreadGetMaxRouterId(mInstance);
206 for (uint8_t i = 0; i <= maxRouterId; ++i)
208 if (otThreadGetRouterInfo(mInstance, i, &routerInfo) != OT_ERROR_NONE)
215 node.mRole = otThreadGetDeviceRole(mInstance);
216 node.mExtAddress = reinterpret_cast<const uint8_t *>(otLinkGetExtendedAddress(mInstance));
217 node.mNetworkName = otThreadGetNetworkName(mInstance);
218 node.mRloc16 = otThreadGetRloc16(mInstance);
219 node.mExtPanId = reinterpret_cast<const uint8_t *>(otThreadGetExtendedPanId(mInstance));
220 node.mRlocAddress = *otThreadGetRloc(mInstance);
222 body = Json::Node2JsonString(node);
223 aResponse.SetBody(body);
226 if (error == OTBR_ERROR_NONE)
228 errorCode = GetHttpStatus(HttpStatusCode::kStatusOk);
229 aResponse.SetResponsCode(errorCode);
233 ErrorHandler(aResponse, HttpStatusCode::kStatusInternalServerError);
237 void Resource::NodeInfo(const Request &aRequest, Response &aResponse) const
239 std::string errorCode;
240 if (aRequest.GetMethod() == HttpMethod::kGet)
242 GetNodeInfo(aResponse);
246 ErrorHandler(aResponse, HttpStatusCode::kStatusMethodNotAllowed);
250 void Resource::GetDataExtendedAddr(Response &aResponse) const
252 const uint8_t *extAddress = reinterpret_cast<const uint8_t *>(otLinkGetExtendedAddress(mInstance));
253 std::string errorCode;
254 std::string body = Json::Bytes2HexJsonString(extAddress, OT_EXT_ADDRESS_SIZE);
256 aResponse.SetBody(body);
257 errorCode = GetHttpStatus(HttpStatusCode::kStatusOk);
258 aResponse.SetResponsCode(errorCode);
261 void Resource::ExtendedAddr(const Request &aRequest, Response &aResponse) const
263 std::string errorCode;
265 if (aRequest.GetMethod() == HttpMethod::kGet)
267 GetDataExtendedAddr(aResponse);
271 ErrorHandler(aResponse, HttpStatusCode::kStatusMethodNotAllowed);
275 void Resource::GetDataState(Response &aResponse) const
278 std::string errorCode;
286 role = otThreadGetDeviceRole(mInstance);
287 state = Json::Number2JsonString(role);
288 aResponse.SetBody(state);
289 errorCode = GetHttpStatus(HttpStatusCode::kStatusOk);
290 aResponse.SetResponsCode(errorCode);
293 void Resource::State(const Request &aRequest, Response &aResponse) const
295 std::string errorCode;
297 if (aRequest.GetMethod() == HttpMethod::kGet)
299 GetDataState(aResponse);
303 ErrorHandler(aResponse, HttpStatusCode::kStatusMethodNotAllowed);
307 void Resource::GetDataNetworkName(Response &aResponse) const
309 std::string networkName;
310 std::string errorCode;
312 networkName = otThreadGetNetworkName(mInstance);
313 networkName = Json::String2JsonString(networkName);
315 aResponse.SetBody(networkName);
316 errorCode = GetHttpStatus(HttpStatusCode::kStatusOk);
317 aResponse.SetResponsCode(errorCode);
320 void Resource::NetworkName(const Request &aRequest, Response &aResponse) const
322 std::string errorCode;
324 if (aRequest.GetMethod() == HttpMethod::kGet)
326 GetDataNetworkName(aResponse);
330 ErrorHandler(aResponse, HttpStatusCode::kStatusMethodNotAllowed);
334 void Resource::GetDataLeaderData(Response &aResponse) const
336 otbrError error = OTBR_ERROR_NONE;
337 otLeaderData leaderData;
339 std::string errorCode;
341 VerifyOrExit(otThreadGetLeaderData(mInstance, &leaderData) == OT_ERROR_NONE, error = OTBR_ERROR_REST);
343 body = Json::LeaderData2JsonString(leaderData);
345 aResponse.SetBody(body);
348 if (error == OTBR_ERROR_NONE)
350 errorCode = GetHttpStatus(HttpStatusCode::kStatusOk);
351 aResponse.SetResponsCode(errorCode);
355 ErrorHandler(aResponse, HttpStatusCode::kStatusInternalServerError);
359 void Resource::LeaderData(const Request &aRequest, Response &aResponse) const
361 std::string errorCode;
362 if (aRequest.GetMethod() == HttpMethod::kGet)
364 GetDataLeaderData(aResponse);
368 ErrorHandler(aResponse, HttpStatusCode::kStatusMethodNotAllowed);
372 void Resource::GetDataNumOfRoute(Response &aResponse) const
376 otRouterInfo routerInfo;
377 maxRouterId = otThreadGetMaxRouterId(mInstance);
379 std::string errorCode;
381 for (uint8_t i = 0; i <= maxRouterId; ++i)
383 if (otThreadGetRouterInfo(mInstance, i, &routerInfo) != OT_ERROR_NONE)
390 body = Json::Number2JsonString(count);
392 aResponse.SetBody(body);
393 errorCode = GetHttpStatus(HttpStatusCode::kStatusOk);
394 aResponse.SetResponsCode(errorCode);
397 void Resource::NumOfRoute(const Request &aRequest, Response &aResponse) const
399 std::string errorCode;
401 if (aRequest.GetMethod() == HttpMethod::kGet)
403 GetDataNumOfRoute(aResponse);
407 ErrorHandler(aResponse, HttpStatusCode::kStatusMethodNotAllowed);
411 void Resource::GetDataRloc16(Response &aResponse) const
413 uint16_t rloc16 = otThreadGetRloc16(mInstance);
415 std::string errorCode;
417 body = Json::Number2JsonString(rloc16);
419 aResponse.SetBody(body);
420 errorCode = GetHttpStatus(HttpStatusCode::kStatusOk);
421 aResponse.SetResponsCode(errorCode);
424 void Resource::Rloc16(const Request &aRequest, Response &aResponse) const
426 std::string errorCode;
428 if (aRequest.GetMethod() == HttpMethod::kGet)
430 GetDataRloc16(aResponse);
434 ErrorHandler(aResponse, HttpStatusCode::kStatusMethodNotAllowed);
438 void Resource::GetDataExtendedPanId(Response &aResponse) const
440 const uint8_t *extPanId = reinterpret_cast<const uint8_t *>(otThreadGetExtendedPanId(mInstance));
441 std::string body = Json::Bytes2HexJsonString(extPanId, OT_EXT_PAN_ID_SIZE);
442 std::string errorCode;
444 aResponse.SetBody(body);
445 errorCode = GetHttpStatus(HttpStatusCode::kStatusOk);
446 aResponse.SetResponsCode(errorCode);
449 void Resource::ExtendedPanId(const Request &aRequest, Response &aResponse) const
451 std::string errorCode;
453 if (aRequest.GetMethod() == HttpMethod::kGet)
455 GetDataExtendedPanId(aResponse);
459 ErrorHandler(aResponse, HttpStatusCode::kStatusMethodNotAllowed);
463 void Resource::GetDataRloc(Response &aResponse) const
465 otIp6Address rlocAddress = *otThreadGetRloc(mInstance);
467 std::string errorCode;
469 body = Json::IpAddr2JsonString(rlocAddress);
471 aResponse.SetBody(body);
472 errorCode = GetHttpStatus(HttpStatusCode::kStatusOk);
473 aResponse.SetResponsCode(errorCode);
476 void Resource::Rloc(const Request &aRequest, Response &aResponse) const
478 std::string errorCode;
480 if (aRequest.GetMethod() == HttpMethod::kGet)
482 GetDataRloc(aResponse);
486 ErrorHandler(aResponse, HttpStatusCode::kStatusMethodNotAllowed);
490 void Resource::DeleteOutDatedDiagnostic(void)
492 auto eraseIt = mDiagSet.begin();
493 for (eraseIt = mDiagSet.begin(); eraseIt != mDiagSet.end();)
495 auto diagInfo = eraseIt->second;
496 auto duration = duration_cast<microseconds>(steady_clock::now() - diagInfo.mStartTime).count();
498 if (duration >= kDiagResetTimeout)
500 eraseIt = mDiagSet.erase(eraseIt);
509 void Resource::UpdateDiag(std::string aKey, std::vector<otNetworkDiagTlv> &aDiag)
513 value.mStartTime = steady_clock::now();
514 value.mDiagContent.assign(aDiag.begin(), aDiag.end());
515 mDiagSet[aKey] = value;
518 void Resource::Diagnostic(const Request &aRequest, Response &aResponse) const
520 otbrError error = OTBR_ERROR_NONE;
521 OT_UNUSED_VARIABLE(aRequest);
522 struct otIp6Address rloc16address = *otThreadGetRloc(mInstance);
523 struct otIp6Address multicastAddress;
525 VerifyOrExit(otThreadSendDiagnosticGet(mInstance, &rloc16address, kAllTlvTypes, sizeof(kAllTlvTypes),
526 &Resource::DiagnosticResponseHandler,
527 const_cast<Resource *>(this)) == OT_ERROR_NONE,
528 error = OTBR_ERROR_REST);
529 VerifyOrExit(otIp6AddressFromString(kMulticastAddrAllRouters, &multicastAddress) == OT_ERROR_NONE,
530 error = OTBR_ERROR_REST);
531 VerifyOrExit(otThreadSendDiagnosticGet(mInstance, &multicastAddress, kAllTlvTypes, sizeof(kAllTlvTypes),
532 &Resource::DiagnosticResponseHandler,
533 const_cast<Resource *>(this)) == OT_ERROR_NONE,
534 error = OTBR_ERROR_REST);
538 if (error == OTBR_ERROR_NONE)
540 aResponse.SetStartTime(steady_clock::now());
541 aResponse.SetCallback();
545 ErrorHandler(aResponse, HttpStatusCode::kStatusInternalServerError);
549 void Resource::DiagnosticResponseHandler(otError aError,
550 otMessage * aMessage,
551 const otMessageInfo *aMessageInfo,
554 static_cast<Resource *>(aContext)->DiagnosticResponseHandler(aError, aMessage, aMessageInfo);
557 void Resource::DiagnosticResponseHandler(otError aError, const otMessage *aMessage, const otMessageInfo *aMessageInfo)
559 std::vector<otNetworkDiagTlv> diagSet;
560 otNetworkDiagTlv diagTlv;
561 otNetworkDiagIterator iterator = OT_NETWORK_DIAGNOSTIC_ITERATOR_INIT;
564 std::string keyRloc = "0xffee";
566 SuccessOrExit(aError);
568 OTBR_UNUSED_VARIABLE(aMessageInfo);
570 while ((error = otThreadGetNextDiagnosticTlv(aMessage, &iterator, &diagTlv)) == OT_ERROR_NONE)
572 if (diagTlv.mType == OT_NETWORK_DIAGNOSTIC_TLV_SHORT_ADDRESS)
574 sprintf(rloc, "0x%04x", diagTlv.mData.mAddr16);
575 keyRloc = Json::CString2JsonString(rloc);
577 diagSet.push_back(diagTlv);
579 UpdateDiag(keyRloc, diagSet);
582 if (aError != OT_ERROR_NONE)
584 otbrLog(OTBR_LOG_WARNING, "failed to get diagnostic data: %s", otThreadErrorToString(aError));