| /* |
| * Copyright (c) 2020, 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 "REST" |
| |
| #include "rest/resource.hpp" |
| #include <openthread/commissioner.h> |
| |
| #define OT_PSKC_MAX_LENGTH 16 |
| #define OT_EXTENDED_PANID_LENGTH 8 |
| |
| #define OT_REST_RESOURCE_PATH_DIAGNOSTICS "/diagnostics" |
| #define OT_REST_RESOURCE_PATH_NODE "/node" |
| #define OT_REST_RESOURCE_PATH_NODE_BAID "/node/ba-id" |
| #define OT_REST_RESOURCE_PATH_NODE_RLOC "/node/rloc" |
| #define OT_REST_RESOURCE_PATH_NODE_RLOC16 "/node/rloc16" |
| #define OT_REST_RESOURCE_PATH_NODE_EXTADDRESS "/node/ext-address" |
| #define OT_REST_RESOURCE_PATH_NODE_STATE "/node/state" |
| #define OT_REST_RESOURCE_PATH_NODE_NETWORKNAME "/node/network-name" |
| #define OT_REST_RESOURCE_PATH_NODE_LEADERDATA "/node/leader-data" |
| #define OT_REST_RESOURCE_PATH_NODE_NUMOFROUTER "/node/num-of-router" |
| #define OT_REST_RESOURCE_PATH_NODE_EXTPANID "/node/ext-panid" |
| #define OT_REST_RESOURCE_PATH_NODE_DATASET_ACTIVE "/node/dataset/active" |
| #define OT_REST_RESOURCE_PATH_NODE_DATASET_PENDING "/node/dataset/pending" |
| #define OT_REST_RESOURCE_PATH_NODE_COMMISSIONER_STATE "/node/commissioner/state" |
| #define OT_REST_RESOURCE_PATH_NODE_COMMISSIONER_JOINER "/node/commissioner/joiner" |
| #define OT_REST_RESOURCE_PATH_NODE_COPROCESSOR "/node/coprocessor" |
| #define OT_REST_RESOURCE_PATH_NODE_COPROCESSOR_VERSION "/node/coprocessor/version" |
| #define OT_REST_RESOURCE_PATH_NETWORK "/networks" |
| #define OT_REST_RESOURCE_PATH_NETWORK_CURRENT "/networks/current" |
| #define OT_REST_RESOURCE_PATH_NETWORK_CURRENT_COMMISSION "/networks/commission" |
| #define OT_REST_RESOURCE_PATH_NETWORK_CURRENT_PREFIX "/networks/current/prefix" |
| |
| #define OT_REST_HTTP_STATUS_200 "200 OK" |
| #define OT_REST_HTTP_STATUS_201 "201 Created" |
| #define OT_REST_HTTP_STATUS_204 "204 No Content" |
| #define OT_REST_HTTP_STATUS_400 "400 Bad Request" |
| #define OT_REST_HTTP_STATUS_404 "404 Not Found" |
| #define OT_REST_HTTP_STATUS_405 "405 Method Not Allowed" |
| #define OT_REST_HTTP_STATUS_408 "408 Request Timeout" |
| #define OT_REST_HTTP_STATUS_409 "409 Conflict" |
| #define OT_REST_HTTP_STATUS_500 "500 Internal Server Error" |
| #define OT_REST_HTTP_STATUS_507 "507 Insufficient Storage" |
| |
| using std::chrono::duration_cast; |
| using std::chrono::microseconds; |
| using std::chrono::steady_clock; |
| |
| using std::placeholders::_1; |
| using std::placeholders::_2; |
| |
| namespace otbr { |
| namespace rest { |
| |
| // MulticastAddr |
| static const char *kMulticastAddrAllRouters = "ff03::2"; |
| |
| // Default TlvTypes for Diagnostic inforamtion |
| static const uint8_t kAllTlvTypes[] = {0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 14, 15, 16, 17, 19}; |
| |
| // Timeout (in Microseconds) for deleting outdated diagnostics |
| static const uint32_t kDiagResetTimeout = 3000000; |
| |
| // Timeout (in Microseconds) for collecting diagnostics |
| static const uint32_t kDiagCollectTimeout = 2000000; |
| |
| static std::string GetHttpStatus(HttpStatusCode aErrorCode) |
| { |
| std::string httpStatus; |
| |
| switch (aErrorCode) |
| { |
| case HttpStatusCode::kStatusOk: |
| httpStatus = OT_REST_HTTP_STATUS_200; |
| break; |
| case HttpStatusCode::kStatusCreated: |
| httpStatus = OT_REST_HTTP_STATUS_201; |
| break; |
| case HttpStatusCode::kStatusNoContent: |
| httpStatus = OT_REST_HTTP_STATUS_204; |
| break; |
| case HttpStatusCode::kStatusBadRequest: |
| httpStatus = OT_REST_HTTP_STATUS_400; |
| break; |
| case HttpStatusCode::kStatusResourceNotFound: |
| httpStatus = OT_REST_HTTP_STATUS_404; |
| break; |
| case HttpStatusCode::kStatusMethodNotAllowed: |
| httpStatus = OT_REST_HTTP_STATUS_405; |
| break; |
| case HttpStatusCode::kStatusRequestTimeout: |
| httpStatus = OT_REST_HTTP_STATUS_408; |
| break; |
| case HttpStatusCode::kStatusConflict: |
| httpStatus = OT_REST_HTTP_STATUS_409; |
| break; |
| case HttpStatusCode::kStatusInternalServerError: |
| httpStatus = OT_REST_HTTP_STATUS_500; |
| break; |
| case HttpStatusCode::kStatusInsufficientStorage: |
| httpStatus = OT_REST_HTTP_STATUS_507; |
| break; |
| } |
| |
| return httpStatus; |
| } |
| |
| Resource::Resource(RcpHost *aHost) |
| : mInstance(nullptr) |
| , mHost(aHost) |
| { |
| // Resource Handler |
| mResourceMap.emplace(OT_REST_RESOURCE_PATH_DIAGNOSTICS, &Resource::Diagnostic); |
| mResourceMap.emplace(OT_REST_RESOURCE_PATH_NODE, &Resource::NodeInfo); |
| mResourceMap.emplace(OT_REST_RESOURCE_PATH_NODE_BAID, &Resource::BaId); |
| mResourceMap.emplace(OT_REST_RESOURCE_PATH_NODE_STATE, &Resource::State); |
| mResourceMap.emplace(OT_REST_RESOURCE_PATH_NODE_EXTADDRESS, &Resource::ExtendedAddr); |
| mResourceMap.emplace(OT_REST_RESOURCE_PATH_NODE_NETWORKNAME, &Resource::NetworkName); |
| mResourceMap.emplace(OT_REST_RESOURCE_PATH_NODE_RLOC16, &Resource::Rloc16); |
| mResourceMap.emplace(OT_REST_RESOURCE_PATH_NODE_LEADERDATA, &Resource::LeaderData); |
| mResourceMap.emplace(OT_REST_RESOURCE_PATH_NODE_NUMOFROUTER, &Resource::NumOfRoute); |
| mResourceMap.emplace(OT_REST_RESOURCE_PATH_NODE_EXTPANID, &Resource::ExtendedPanId); |
| mResourceMap.emplace(OT_REST_RESOURCE_PATH_NODE_RLOC, &Resource::Rloc); |
| mResourceMap.emplace(OT_REST_RESOURCE_PATH_NODE_DATASET_ACTIVE, &Resource::DatasetActive); |
| mResourceMap.emplace(OT_REST_RESOURCE_PATH_NODE_DATASET_PENDING, &Resource::DatasetPending); |
| mResourceMap.emplace(OT_REST_RESOURCE_PATH_NODE_COMMISSIONER_STATE, &Resource::CommissionerState); |
| mResourceMap.emplace(OT_REST_RESOURCE_PATH_NODE_COMMISSIONER_JOINER, &Resource::CommissionerJoiner); |
| mResourceMap.emplace(OT_REST_RESOURCE_PATH_NODE_COPROCESSOR_VERSION, &Resource::CoprocessorVersion); |
| |
| // Resource callback handler |
| mResourceCallbackMap.emplace(OT_REST_RESOURCE_PATH_DIAGNOSTICS, &Resource::HandleDiagnosticCallback); |
| } |
| |
| void Resource::Init(void) |
| { |
| mInstance = mHost->GetThreadHelper()->GetInstance(); |
| } |
| |
| void Resource::Handle(Request &aRequest, Response &aResponse) const |
| { |
| std::string url = aRequest.GetUrl(); |
| auto it = mResourceMap.find(url); |
| |
| if (it != mResourceMap.end()) |
| { |
| ResourceHandler resourceHandler = it->second; |
| (this->*resourceHandler)(aRequest, aResponse); |
| } |
| else |
| { |
| ErrorHandler(aResponse, HttpStatusCode::kStatusResourceNotFound); |
| } |
| } |
| |
| void Resource::HandleCallback(Request &aRequest, Response &aResponse) |
| { |
| std::string url = aRequest.GetUrl(); |
| auto it = mResourceCallbackMap.find(url); |
| |
| if (it != mResourceCallbackMap.end()) |
| { |
| ResourceCallbackHandler resourceHandler = it->second; |
| (this->*resourceHandler)(aRequest, aResponse); |
| } |
| } |
| |
| void Resource::HandleDiagnosticCallback(const Request &aRequest, Response &aResponse) |
| { |
| OT_UNUSED_VARIABLE(aRequest); |
| std::vector<std::vector<otNetworkDiagTlv>> diagContentSet; |
| std::string body; |
| std::string errorCode; |
| |
| auto duration = duration_cast<microseconds>(steady_clock::now() - aResponse.GetStartTime()).count(); |
| if (duration >= kDiagCollectTimeout) |
| { |
| DeleteOutDatedDiagnostic(); |
| |
| for (auto it = mDiagSet.begin(); it != mDiagSet.end(); ++it) |
| { |
| diagContentSet.push_back(it->second.mDiagContent); |
| } |
| |
| body = Json::Diag2JsonString(diagContentSet); |
| errorCode = GetHttpStatus(HttpStatusCode::kStatusOk); |
| aResponse.SetResponsCode(errorCode); |
| aResponse.SetBody(body); |
| aResponse.SetComplete(); |
| } |
| } |
| |
| void Resource::ErrorHandler(Response &aResponse, HttpStatusCode aErrorCode) const |
| { |
| std::string errorMessage = GetHttpStatus(aErrorCode); |
| std::string body = Json::Error2JsonString(aErrorCode, errorMessage); |
| |
| aResponse.SetResponsCode(errorMessage); |
| aResponse.SetBody(body); |
| aResponse.SetComplete(); |
| } |
| |
| void Resource::GetNodeInfo(Response &aResponse) const |
| { |
| otbrError error = OTBR_ERROR_NONE; |
| struct NodeInfo node = {}; |
| otRouterInfo routerInfo; |
| uint8_t maxRouterId; |
| std::string body; |
| std::string errorCode; |
| |
| VerifyOrExit(otBorderAgentGetId(mInstance, &node.mBaId) == OT_ERROR_NONE, error = OTBR_ERROR_REST); |
| (void)otThreadGetLeaderData(mInstance, &node.mLeaderData); |
| |
| node.mNumOfRouter = 0; |
| maxRouterId = otThreadGetMaxRouterId(mInstance); |
| for (uint8_t i = 0; i <= maxRouterId; ++i) |
| { |
| if (otThreadGetRouterInfo(mInstance, i, &routerInfo) != OT_ERROR_NONE) |
| { |
| continue; |
| } |
| ++node.mNumOfRouter; |
| } |
| |
| node.mRole = GetDeviceRoleName(otThreadGetDeviceRole(mInstance)); |
| node.mExtAddress = reinterpret_cast<const uint8_t *>(otLinkGetExtendedAddress(mInstance)); |
| node.mNetworkName = otThreadGetNetworkName(mInstance); |
| node.mRloc16 = otThreadGetRloc16(mInstance); |
| node.mExtPanId = reinterpret_cast<const uint8_t *>(otThreadGetExtendedPanId(mInstance)); |
| node.mRlocAddress = *otThreadGetRloc(mInstance); |
| |
| body = Json::Node2JsonString(node); |
| aResponse.SetBody(body); |
| |
| exit: |
| if (error == OTBR_ERROR_NONE) |
| { |
| errorCode = GetHttpStatus(HttpStatusCode::kStatusOk); |
| aResponse.SetResponsCode(errorCode); |
| } |
| else |
| { |
| ErrorHandler(aResponse, HttpStatusCode::kStatusInternalServerError); |
| } |
| } |
| |
| void Resource::DeleteNodeInfo(Response &aResponse) const |
| { |
| otbrError error = OTBR_ERROR_NONE; |
| std::string errorCode; |
| |
| VerifyOrExit(mHost->GetThreadHelper()->Detach() == OT_ERROR_NONE, error = OTBR_ERROR_INVALID_STATE); |
| VerifyOrExit(otInstanceErasePersistentInfo(mInstance) == OT_ERROR_NONE, error = OTBR_ERROR_REST); |
| mHost->Reset(); |
| |
| exit: |
| if (error == OTBR_ERROR_NONE) |
| { |
| errorCode = GetHttpStatus(HttpStatusCode::kStatusOk); |
| aResponse.SetResponsCode(errorCode); |
| } |
| else if (error == OTBR_ERROR_INVALID_STATE) |
| { |
| ErrorHandler(aResponse, HttpStatusCode::kStatusConflict); |
| } |
| else if (error != OTBR_ERROR_NONE) |
| { |
| ErrorHandler(aResponse, HttpStatusCode::kStatusInternalServerError); |
| } |
| } |
| |
| void Resource::NodeInfo(const Request &aRequest, Response &aResponse) const |
| { |
| std::string errorCode; |
| |
| switch (aRequest.GetMethod()) |
| { |
| case HttpMethod::kGet: |
| GetNodeInfo(aResponse); |
| break; |
| case HttpMethod::kDelete: |
| DeleteNodeInfo(aResponse); |
| break; |
| default: |
| ErrorHandler(aResponse, HttpStatusCode::kStatusMethodNotAllowed); |
| break; |
| } |
| } |
| |
| void Resource::GetDataBaId(Response &aResponse) const |
| { |
| otbrError error = OTBR_ERROR_NONE; |
| otBorderAgentId id; |
| std::string body; |
| std::string errorCode; |
| |
| VerifyOrExit(otBorderAgentGetId(mInstance, &id) == OT_ERROR_NONE, error = OTBR_ERROR_REST); |
| |
| body = Json::Bytes2HexJsonString(id.mId, sizeof(id)); |
| aResponse.SetBody(body); |
| |
| exit: |
| if (error == OTBR_ERROR_NONE) |
| { |
| errorCode = GetHttpStatus(HttpStatusCode::kStatusOk); |
| aResponse.SetResponsCode(errorCode); |
| } |
| else |
| { |
| ErrorHandler(aResponse, HttpStatusCode::kStatusInternalServerError); |
| } |
| } |
| |
| void Resource::BaId(const Request &aRequest, Response &aResponse) const |
| { |
| std::string errorCode; |
| |
| if (aRequest.GetMethod() == HttpMethod::kGet) |
| { |
| GetDataBaId(aResponse); |
| } |
| else |
| { |
| ErrorHandler(aResponse, HttpStatusCode::kStatusMethodNotAllowed); |
| } |
| } |
| |
| void Resource::GetDataExtendedAddr(Response &aResponse) const |
| { |
| const uint8_t *extAddress = reinterpret_cast<const uint8_t *>(otLinkGetExtendedAddress(mInstance)); |
| std::string errorCode; |
| std::string body = Json::Bytes2HexJsonString(extAddress, OT_EXT_ADDRESS_SIZE); |
| |
| aResponse.SetBody(body); |
| errorCode = GetHttpStatus(HttpStatusCode::kStatusOk); |
| aResponse.SetResponsCode(errorCode); |
| } |
| |
| void Resource::ExtendedAddr(const Request &aRequest, Response &aResponse) const |
| { |
| std::string errorCode; |
| |
| if (aRequest.GetMethod() == HttpMethod::kGet) |
| { |
| GetDataExtendedAddr(aResponse); |
| } |
| else |
| { |
| ErrorHandler(aResponse, HttpStatusCode::kStatusMethodNotAllowed); |
| } |
| } |
| |
| void Resource::GetDataState(Response &aResponse) const |
| { |
| std::string state; |
| std::string errorCode; |
| otDeviceRole role; |
| |
| role = otThreadGetDeviceRole(mInstance); |
| state = Json::String2JsonString(GetDeviceRoleName(role)); |
| aResponse.SetBody(state); |
| errorCode = GetHttpStatus(HttpStatusCode::kStatusOk); |
| aResponse.SetResponsCode(errorCode); |
| } |
| |
| void Resource::SetDataState(const Request &aRequest, Response &aResponse) const |
| { |
| otbrError error = OTBR_ERROR_NONE; |
| std::string errorCode; |
| std::string body; |
| |
| VerifyOrExit(Json::JsonString2String(aRequest.GetBody(), body), error = OTBR_ERROR_INVALID_ARGS); |
| if (body == "enable") |
| { |
| if (!otIp6IsEnabled(mInstance)) |
| { |
| VerifyOrExit(otIp6SetEnabled(mInstance, true) == OT_ERROR_NONE, error = OTBR_ERROR_INVALID_STATE); |
| } |
| VerifyOrExit(otThreadSetEnabled(mInstance, true) == OT_ERROR_NONE, error = OTBR_ERROR_INVALID_STATE); |
| } |
| else if (body == "disable") |
| { |
| VerifyOrExit(otThreadSetEnabled(mInstance, false) == OT_ERROR_NONE, error = OTBR_ERROR_INVALID_STATE); |
| VerifyOrExit(otIp6SetEnabled(mInstance, false) == OT_ERROR_NONE, error = OTBR_ERROR_INVALID_STATE); |
| } |
| else |
| { |
| ExitNow(error = OTBR_ERROR_INVALID_ARGS); |
| } |
| |
| errorCode = GetHttpStatus(HttpStatusCode::kStatusOk); |
| aResponse.SetResponsCode(errorCode); |
| |
| exit: |
| if (error == OTBR_ERROR_INVALID_STATE) |
| { |
| ErrorHandler(aResponse, HttpStatusCode::kStatusConflict); |
| } |
| if (error == OTBR_ERROR_INVALID_ARGS) |
| { |
| ErrorHandler(aResponse, HttpStatusCode::kStatusBadRequest); |
| } |
| else if (error != OTBR_ERROR_NONE) |
| { |
| ErrorHandler(aResponse, HttpStatusCode::kStatusInternalServerError); |
| } |
| } |
| |
| void Resource::State(const Request &aRequest, Response &aResponse) const |
| { |
| std::string errorCode; |
| |
| switch (aRequest.GetMethod()) |
| { |
| case HttpMethod::kGet: |
| GetDataState(aResponse); |
| break; |
| case HttpMethod::kPut: |
| SetDataState(aRequest, aResponse); |
| break; |
| case HttpMethod::kOptions: |
| errorCode = GetHttpStatus(HttpStatusCode::kStatusOk); |
| aResponse.SetResponsCode(errorCode); |
| aResponse.SetComplete(); |
| break; |
| default: |
| ErrorHandler(aResponse, HttpStatusCode::kStatusMethodNotAllowed); |
| break; |
| } |
| } |
| |
| void Resource::GetDataNetworkName(Response &aResponse) const |
| { |
| std::string networkName; |
| std::string errorCode; |
| |
| networkName = otThreadGetNetworkName(mInstance); |
| networkName = Json::String2JsonString(networkName); |
| |
| aResponse.SetBody(networkName); |
| errorCode = GetHttpStatus(HttpStatusCode::kStatusOk); |
| aResponse.SetResponsCode(errorCode); |
| } |
| |
| void Resource::NetworkName(const Request &aRequest, Response &aResponse) const |
| { |
| std::string errorCode; |
| |
| if (aRequest.GetMethod() == HttpMethod::kGet) |
| { |
| GetDataNetworkName(aResponse); |
| } |
| else |
| { |
| ErrorHandler(aResponse, HttpStatusCode::kStatusMethodNotAllowed); |
| } |
| } |
| |
| void Resource::GetDataLeaderData(Response &aResponse) const |
| { |
| otbrError error = OTBR_ERROR_NONE; |
| otLeaderData leaderData; |
| std::string body; |
| std::string errorCode; |
| |
| VerifyOrExit(otThreadGetLeaderData(mInstance, &leaderData) == OT_ERROR_NONE, error = OTBR_ERROR_REST); |
| |
| body = Json::LeaderData2JsonString(leaderData); |
| |
| aResponse.SetBody(body); |
| |
| exit: |
| if (error == OTBR_ERROR_NONE) |
| { |
| errorCode = GetHttpStatus(HttpStatusCode::kStatusOk); |
| aResponse.SetResponsCode(errorCode); |
| } |
| else |
| { |
| ErrorHandler(aResponse, HttpStatusCode::kStatusInternalServerError); |
| } |
| } |
| |
| void Resource::LeaderData(const Request &aRequest, Response &aResponse) const |
| { |
| std::string errorCode; |
| if (aRequest.GetMethod() == HttpMethod::kGet) |
| { |
| GetDataLeaderData(aResponse); |
| } |
| else |
| { |
| ErrorHandler(aResponse, HttpStatusCode::kStatusMethodNotAllowed); |
| } |
| } |
| |
| void Resource::GetDataNumOfRoute(Response &aResponse) const |
| { |
| uint8_t count = 0; |
| uint8_t maxRouterId; |
| otRouterInfo routerInfo; |
| maxRouterId = otThreadGetMaxRouterId(mInstance); |
| std::string body; |
| std::string errorCode; |
| |
| for (uint8_t i = 0; i <= maxRouterId; ++i) |
| { |
| if (otThreadGetRouterInfo(mInstance, i, &routerInfo) != OT_ERROR_NONE) |
| { |
| continue; |
| } |
| ++count; |
| } |
| |
| body = Json::Number2JsonString(count); |
| |
| aResponse.SetBody(body); |
| errorCode = GetHttpStatus(HttpStatusCode::kStatusOk); |
| aResponse.SetResponsCode(errorCode); |
| } |
| |
| void Resource::NumOfRoute(const Request &aRequest, Response &aResponse) const |
| { |
| std::string errorCode; |
| |
| if (aRequest.GetMethod() == HttpMethod::kGet) |
| { |
| GetDataNumOfRoute(aResponse); |
| } |
| else |
| { |
| ErrorHandler(aResponse, HttpStatusCode::kStatusMethodNotAllowed); |
| } |
| } |
| |
| void Resource::GetDataRloc16(Response &aResponse) const |
| { |
| uint16_t rloc16 = otThreadGetRloc16(mInstance); |
| std::string body; |
| std::string errorCode; |
| |
| body = Json::Number2JsonString(rloc16); |
| |
| aResponse.SetBody(body); |
| errorCode = GetHttpStatus(HttpStatusCode::kStatusOk); |
| aResponse.SetResponsCode(errorCode); |
| } |
| |
| void Resource::Rloc16(const Request &aRequest, Response &aResponse) const |
| { |
| std::string errorCode; |
| |
| if (aRequest.GetMethod() == HttpMethod::kGet) |
| { |
| GetDataRloc16(aResponse); |
| } |
| else |
| { |
| ErrorHandler(aResponse, HttpStatusCode::kStatusMethodNotAllowed); |
| } |
| } |
| |
| void Resource::GetDataExtendedPanId(Response &aResponse) const |
| { |
| const uint8_t *extPanId = reinterpret_cast<const uint8_t *>(otThreadGetExtendedPanId(mInstance)); |
| std::string body = Json::Bytes2HexJsonString(extPanId, OT_EXT_PAN_ID_SIZE); |
| std::string errorCode; |
| |
| aResponse.SetBody(body); |
| errorCode = GetHttpStatus(HttpStatusCode::kStatusOk); |
| aResponse.SetResponsCode(errorCode); |
| } |
| |
| void Resource::ExtendedPanId(const Request &aRequest, Response &aResponse) const |
| { |
| std::string errorCode; |
| |
| if (aRequest.GetMethod() == HttpMethod::kGet) |
| { |
| GetDataExtendedPanId(aResponse); |
| } |
| else |
| { |
| ErrorHandler(aResponse, HttpStatusCode::kStatusMethodNotAllowed); |
| } |
| } |
| |
| void Resource::GetDataRloc(Response &aResponse) const |
| { |
| otIp6Address rlocAddress = *otThreadGetRloc(mInstance); |
| std::string body; |
| std::string errorCode; |
| |
| body = Json::IpAddr2JsonString(rlocAddress); |
| |
| aResponse.SetBody(body); |
| errorCode = GetHttpStatus(HttpStatusCode::kStatusOk); |
| aResponse.SetResponsCode(errorCode); |
| } |
| |
| void Resource::Rloc(const Request &aRequest, Response &aResponse) const |
| { |
| std::string errorCode; |
| |
| if (aRequest.GetMethod() == HttpMethod::kGet) |
| { |
| GetDataRloc(aResponse); |
| } |
| else |
| { |
| ErrorHandler(aResponse, HttpStatusCode::kStatusMethodNotAllowed); |
| } |
| } |
| |
| void Resource::GetDataset(DatasetType aDatasetType, const Request &aRequest, Response &aResponse) const |
| { |
| otbrError error = OTBR_ERROR_NONE; |
| struct NodeInfo node; |
| std::string body; |
| std::string errorCode; |
| otOperationalDataset dataset; |
| otOperationalDatasetTlvs datasetTlvs; |
| |
| if (aRequest.GetHeaderValue(OT_REST_ACCEPT_HEADER) == OT_REST_CONTENT_TYPE_PLAIN) |
| { |
| if (aDatasetType == DatasetType::kActive) |
| { |
| VerifyOrExit(otDatasetGetActiveTlvs(mInstance, &datasetTlvs) == OT_ERROR_NONE, |
| error = OTBR_ERROR_NOT_FOUND); |
| } |
| else if (aDatasetType == DatasetType::kPending) |
| { |
| VerifyOrExit(otDatasetGetPendingTlvs(mInstance, &datasetTlvs) == OT_ERROR_NONE, |
| error = OTBR_ERROR_NOT_FOUND); |
| } |
| |
| aResponse.SetContentType(OT_REST_CONTENT_TYPE_PLAIN); |
| body = Utils::Bytes2Hex(datasetTlvs.mTlvs, datasetTlvs.mLength); |
| } |
| else |
| { |
| if (aDatasetType == DatasetType::kActive) |
| { |
| VerifyOrExit(otDatasetGetActive(mInstance, &dataset) == OT_ERROR_NONE, error = OTBR_ERROR_NOT_FOUND); |
| body = Json::ActiveDataset2JsonString(dataset); |
| } |
| else if (aDatasetType == DatasetType::kPending) |
| { |
| VerifyOrExit(otDatasetGetPending(mInstance, &dataset) == OT_ERROR_NONE, error = OTBR_ERROR_NOT_FOUND); |
| body = Json::PendingDataset2JsonString(dataset); |
| } |
| } |
| |
| aResponse.SetBody(body); |
| |
| exit: |
| if (error == OTBR_ERROR_NONE) |
| { |
| errorCode = GetHttpStatus(HttpStatusCode::kStatusOk); |
| aResponse.SetResponsCode(errorCode); |
| } |
| else if (error == OTBR_ERROR_NOT_FOUND) |
| { |
| errorCode = GetHttpStatus(HttpStatusCode::kStatusNoContent); |
| aResponse.SetResponsCode(errorCode); |
| } |
| else |
| { |
| ErrorHandler(aResponse, HttpStatusCode::kStatusInternalServerError); |
| } |
| } |
| |
| void Resource::SetDataset(DatasetType aDatasetType, const Request &aRequest, Response &aResponse) const |
| { |
| otError errorOt = OT_ERROR_NONE; |
| otbrError error = OTBR_ERROR_NONE; |
| struct NodeInfo node; |
| std::string body; |
| std::string errorCode = GetHttpStatus(HttpStatusCode::kStatusOk); |
| otOperationalDataset dataset = {}; |
| otOperationalDatasetTlvs datasetTlvs; |
| otOperationalDatasetTlvs datasetUpdateTlvs; |
| int ret; |
| bool isTlv; |
| |
| if (aDatasetType == DatasetType::kActive) |
| { |
| VerifyOrExit(otThreadGetDeviceRole(mInstance) == OT_DEVICE_ROLE_DISABLED, error = OTBR_ERROR_INVALID_STATE); |
| errorOt = otDatasetGetActiveTlvs(mInstance, &datasetTlvs); |
| } |
| else if (aDatasetType == DatasetType::kPending) |
| { |
| errorOt = otDatasetGetPendingTlvs(mInstance, &datasetTlvs); |
| } |
| |
| // Create a new operational dataset if it doesn't exist. |
| if (errorOt == OT_ERROR_NOT_FOUND) |
| { |
| VerifyOrExit(otDatasetCreateNewNetwork(mInstance, &dataset) == OT_ERROR_NONE, error = OTBR_ERROR_REST); |
| otDatasetConvertToTlvs(&dataset, &datasetTlvs); |
| errorCode = GetHttpStatus(HttpStatusCode::kStatusCreated); |
| } |
| |
| isTlv = aRequest.GetHeaderValue(OT_REST_CONTENT_TYPE_HEADER) == OT_REST_CONTENT_TYPE_PLAIN; |
| |
| if (isTlv) |
| { |
| ret = Json::Hex2BytesJsonString(aRequest.GetBody(), datasetUpdateTlvs.mTlvs, OT_OPERATIONAL_DATASET_MAX_LENGTH); |
| VerifyOrExit(ret >= 0, error = OTBR_ERROR_INVALID_ARGS); |
| datasetUpdateTlvs.mLength = ret; |
| |
| VerifyOrExit(otDatasetParseTlvs(&datasetUpdateTlvs, &dataset) == OT_ERROR_NONE, error = OTBR_ERROR_REST); |
| VerifyOrExit(otDatasetUpdateTlvs(&dataset, &datasetTlvs) == OT_ERROR_NONE, error = OTBR_ERROR_REST); |
| } |
| else |
| { |
| if (aDatasetType == DatasetType::kActive) |
| { |
| VerifyOrExit(Json::JsonActiveDatasetString2Dataset(aRequest.GetBody(), dataset), |
| error = OTBR_ERROR_INVALID_ARGS); |
| } |
| else if (aDatasetType == DatasetType::kPending) |
| { |
| VerifyOrExit(Json::JsonPendingDatasetString2Dataset(aRequest.GetBody(), dataset), |
| error = OTBR_ERROR_INVALID_ARGS); |
| VerifyOrExit(dataset.mComponents.mIsDelayPresent, error = OTBR_ERROR_INVALID_ARGS); |
| } |
| VerifyOrExit(otDatasetUpdateTlvs(&dataset, &datasetTlvs) == OT_ERROR_NONE, error = OTBR_ERROR_REST); |
| } |
| |
| if (aDatasetType == DatasetType::kActive) |
| { |
| VerifyOrExit(otDatasetSetActiveTlvs(mInstance, &datasetTlvs) == OT_ERROR_NONE, error = OTBR_ERROR_REST); |
| } |
| else if (aDatasetType == DatasetType::kPending) |
| { |
| VerifyOrExit(otDatasetSetPendingTlvs(mInstance, &datasetTlvs) == OT_ERROR_NONE, error = OTBR_ERROR_REST); |
| } |
| |
| aResponse.SetResponsCode(errorCode); |
| |
| exit: |
| if (error == OTBR_ERROR_INVALID_ARGS) |
| { |
| ErrorHandler(aResponse, HttpStatusCode::kStatusBadRequest); |
| } |
| else if (error == OTBR_ERROR_INVALID_STATE) |
| { |
| ErrorHandler(aResponse, HttpStatusCode::kStatusConflict); |
| } |
| else if (error != OTBR_ERROR_NONE) |
| { |
| ErrorHandler(aResponse, HttpStatusCode::kStatusInternalServerError); |
| } |
| } |
| |
| void Resource::Dataset(DatasetType aDatasetType, const Request &aRequest, Response &aResponse) const |
| { |
| std::string errorCode; |
| |
| switch (aRequest.GetMethod()) |
| { |
| case HttpMethod::kGet: |
| GetDataset(aDatasetType, aRequest, aResponse); |
| break; |
| case HttpMethod::kPut: |
| SetDataset(aDatasetType, aRequest, aResponse); |
| break; |
| case HttpMethod::kOptions: |
| errorCode = GetHttpStatus(HttpStatusCode::kStatusOk); |
| aResponse.SetResponsCode(errorCode); |
| aResponse.SetComplete(); |
| break; |
| default: |
| ErrorHandler(aResponse, HttpStatusCode::kStatusMethodNotAllowed); |
| break; |
| } |
| } |
| |
| void Resource::DatasetActive(const Request &aRequest, Response &aResponse) const |
| { |
| Dataset(DatasetType::kActive, aRequest, aResponse); |
| } |
| |
| void Resource::DatasetPending(const Request &aRequest, Response &aResponse) const |
| { |
| Dataset(DatasetType::kPending, aRequest, aResponse); |
| } |
| |
| void Resource::GetCommissionerState(Response &aResponse) const |
| { |
| std::string state; |
| std::string errorCode; |
| otCommissionerState stateCode; |
| |
| stateCode = otCommissionerGetState(mInstance); |
| state = Json::String2JsonString(GetCommissionerStateName(stateCode)); |
| aResponse.SetBody(state); |
| errorCode = GetHttpStatus(HttpStatusCode::kStatusOk); |
| aResponse.SetResponsCode(errorCode); |
| } |
| |
| void Resource::SetCommissionerState(const Request &aRequest, Response &aResponse) const |
| { |
| otbrError error = OTBR_ERROR_NONE; |
| std::string errorCode; |
| std::string body; |
| |
| VerifyOrExit(Json::JsonString2String(aRequest.GetBody(), body), error = OTBR_ERROR_INVALID_ARGS); |
| if (body == "enable") |
| { |
| VerifyOrExit(otCommissionerGetState(mInstance) == OT_COMMISSIONER_STATE_DISABLED, error = OTBR_ERROR_NONE); |
| VerifyOrExit(otCommissionerStart(mInstance, NULL, NULL, NULL) == OT_ERROR_NONE, |
| error = OTBR_ERROR_INVALID_STATE); |
| } |
| else if (body == "disable") |
| { |
| VerifyOrExit(otCommissionerGetState(mInstance) != OT_COMMISSIONER_STATE_DISABLED, error = OTBR_ERROR_NONE); |
| VerifyOrExit(otCommissionerStop(mInstance) == OT_ERROR_NONE, error = OTBR_ERROR_INVALID_STATE); |
| } |
| else |
| { |
| ExitNow(error = OTBR_ERROR_INVALID_ARGS); |
| } |
| |
| exit: |
| switch (error) |
| { |
| case OTBR_ERROR_NONE: |
| errorCode = GetHttpStatus(HttpStatusCode::kStatusOk); |
| aResponse.SetResponsCode(errorCode); |
| break; |
| case OTBR_ERROR_INVALID_STATE: |
| ErrorHandler(aResponse, HttpStatusCode::kStatusConflict); |
| break; |
| case OTBR_ERROR_INVALID_ARGS: |
| ErrorHandler(aResponse, HttpStatusCode::kStatusBadRequest); |
| break; |
| default: |
| ErrorHandler(aResponse, HttpStatusCode::kStatusInternalServerError); |
| break; |
| } |
| } |
| |
| void Resource::CommissionerState(const Request &aRequest, Response &aResponse) const |
| { |
| std::string errorCode; |
| |
| switch (aRequest.GetMethod()) |
| { |
| case HttpMethod::kGet: |
| GetCommissionerState(aResponse); |
| break; |
| case HttpMethod::kPut: |
| SetCommissionerState(aRequest, aResponse); |
| break; |
| case HttpMethod::kOptions: |
| errorCode = GetHttpStatus(HttpStatusCode::kStatusOk); |
| aResponse.SetResponsCode(errorCode); |
| aResponse.SetComplete(); |
| break; |
| default: |
| ErrorHandler(aResponse, HttpStatusCode::kStatusMethodNotAllowed); |
| break; |
| } |
| } |
| |
| void Resource::GetJoiners(Response &aResponse) const |
| { |
| uint16_t iter = 0; |
| otJoinerInfo joinerInfo; |
| std::vector<otJoinerInfo> joinerTable; |
| std::string joinerJson; |
| std::string errorCode; |
| |
| while (otCommissionerGetNextJoinerInfo(mInstance, &iter, &joinerInfo) == OT_ERROR_NONE) |
| { |
| joinerTable.push_back(joinerInfo); |
| } |
| |
| joinerJson = Json::JoinerTable2JsonString(joinerTable); |
| aResponse.SetBody(joinerJson); |
| errorCode = GetHttpStatus(HttpStatusCode::kStatusOk); |
| aResponse.SetResponsCode(errorCode); |
| } |
| |
| void Resource::AddJoiner(const Request &aRequest, Response &aResponse) const |
| { |
| otbrError error = OTBR_ERROR_NONE; |
| otError errorOt = OT_ERROR_NONE; |
| std::string errorCode; |
| otJoinerInfo joiner; |
| const otExtAddress *addrPtr = nullptr; |
| const uint8_t emptyArray[OT_EXT_ADDRESS_SIZE] = {0}; |
| |
| VerifyOrExit(otCommissionerGetState(mInstance) == OT_COMMISSIONER_STATE_ACTIVE, error = OTBR_ERROR_INVALID_STATE); |
| |
| VerifyOrExit(Json::JsonJoinerInfoString2JoinerInfo(aRequest.GetBody(), joiner), error = OTBR_ERROR_INVALID_ARGS); |
| |
| addrPtr = &joiner.mSharedId.mEui64; |
| if (memcmp(&joiner.mSharedId.mEui64, emptyArray, OT_EXT_ADDRESS_SIZE) == 0) |
| { |
| addrPtr = nullptr; |
| } |
| |
| if (joiner.mType == OT_JOINER_INFO_TYPE_DISCERNER) |
| { |
| errorOt = otCommissionerAddJoinerWithDiscerner(mInstance, &joiner.mSharedId.mDiscerner, joiner.mPskd.m8, |
| joiner.mExpirationTime); |
| } |
| else |
| { |
| errorOt = otCommissionerAddJoiner(mInstance, addrPtr, joiner.mPskd.m8, joiner.mExpirationTime); |
| } |
| VerifyOrExit(errorOt == OT_ERROR_NONE, error = OTBR_ERROR_OPENTHREAD); |
| |
| exit: |
| switch (error) |
| { |
| case OTBR_ERROR_NONE: |
| errorCode = GetHttpStatus(HttpStatusCode::kStatusOk); |
| aResponse.SetResponsCode(errorCode); |
| break; |
| case OTBR_ERROR_INVALID_STATE: |
| ErrorHandler(aResponse, HttpStatusCode::kStatusConflict); |
| break; |
| case OTBR_ERROR_INVALID_ARGS: |
| ErrorHandler(aResponse, HttpStatusCode::kStatusBadRequest); |
| break; |
| case OTBR_ERROR_OPENTHREAD: |
| switch (errorOt) |
| { |
| case OT_ERROR_INVALID_ARGS: |
| ErrorHandler(aResponse, HttpStatusCode::kStatusBadRequest); |
| break; |
| case OT_ERROR_NO_BUFS: |
| ErrorHandler(aResponse, HttpStatusCode::kStatusInsufficientStorage); |
| break; |
| default: |
| ErrorHandler(aResponse, HttpStatusCode::kStatusInternalServerError); |
| break; |
| } |
| break; |
| default: |
| ErrorHandler(aResponse, HttpStatusCode::kStatusInternalServerError); |
| break; |
| } |
| } |
| |
| void Resource::RemoveJoiner(const Request &aRequest, Response &aResponse) const |
| { |
| otbrError error = OTBR_ERROR_NONE; |
| std::string errorCode; |
| otExtAddress eui64; |
| otExtAddress *addrPtr = nullptr; |
| otJoinerDiscerner discerner = { |
| .mValue = 0, |
| .mLength = 0, |
| }; |
| std::string body; |
| |
| VerifyOrExit(otCommissionerGetState(mInstance) == OT_COMMISSIONER_STATE_ACTIVE, error = OTBR_ERROR_INVALID_STATE); |
| |
| VerifyOrExit(Json::JsonString2String(aRequest.GetBody(), body), error = OTBR_ERROR_INVALID_ARGS); |
| if (body != "*") |
| { |
| error = Json::StringDiscerner2Discerner(const_cast<char *>(body.c_str()), discerner); |
| if (error == OTBR_ERROR_NOT_FOUND) |
| { |
| error = OTBR_ERROR_NONE; |
| VerifyOrExit(Json::Hex2BytesJsonString(body, eui64.m8, OT_EXT_ADDRESS_SIZE) == OT_EXT_ADDRESS_SIZE, |
| error = OTBR_ERROR_INVALID_ARGS); |
| addrPtr = &eui64; |
| } |
| else if (error != OTBR_ERROR_NONE) |
| { |
| ExitNow(error = OTBR_ERROR_INVALID_ARGS); |
| } |
| } |
| |
| // These functions should only return OT_ERROR_NONE or OT_ERROR_NOT_FOUND both treated as successful |
| if (discerner.mLength == 0) |
| { |
| (void)otCommissionerRemoveJoiner(mInstance, addrPtr); |
| } |
| else |
| { |
| (void)otCommissionerRemoveJoinerWithDiscerner(mInstance, &discerner); |
| } |
| |
| exit: |
| switch (error) |
| { |
| case OTBR_ERROR_NONE: |
| errorCode = GetHttpStatus(HttpStatusCode::kStatusOk); |
| aResponse.SetResponsCode(errorCode); |
| break; |
| case OTBR_ERROR_INVALID_STATE: |
| ErrorHandler(aResponse, HttpStatusCode::kStatusConflict); |
| break; |
| case OTBR_ERROR_INVALID_ARGS: |
| ErrorHandler(aResponse, HttpStatusCode::kStatusBadRequest); |
| break; |
| default: |
| ErrorHandler(aResponse, HttpStatusCode::kStatusInternalServerError); |
| break; |
| } |
| } |
| |
| void Resource::CommissionerJoiner(const Request &aRequest, Response &aResponse) const |
| { |
| std::string errorCode; |
| |
| switch (aRequest.GetMethod()) |
| { |
| case HttpMethod::kGet: |
| GetJoiners(aResponse); |
| break; |
| case HttpMethod::kPost: |
| AddJoiner(aRequest, aResponse); |
| break; |
| case HttpMethod::kDelete: |
| RemoveJoiner(aRequest, aResponse); |
| break; |
| |
| case HttpMethod::kOptions: |
| errorCode = GetHttpStatus(HttpStatusCode::kStatusOk); |
| aResponse.SetResponsCode(errorCode); |
| aResponse.SetComplete(); |
| break; |
| default: |
| ErrorHandler(aResponse, HttpStatusCode::kStatusMethodNotAllowed); |
| break; |
| } |
| } |
| |
| void Resource::GetCoprocessorVersion(Response &aResponse) const |
| { |
| std::string coprocessorVersion; |
| std::string errorCode; |
| |
| coprocessorVersion = mHost->GetCoprocessorVersion(); |
| coprocessorVersion = Json::String2JsonString(coprocessorVersion); |
| |
| aResponse.SetBody(coprocessorVersion); |
| errorCode = GetHttpStatus(HttpStatusCode::kStatusOk); |
| aResponse.SetResponsCode(errorCode); |
| } |
| |
| void Resource::CoprocessorVersion(const Request &aRequest, Response &aResponse) const |
| { |
| std::string errorCode; |
| |
| if (aRequest.GetMethod() == HttpMethod::kGet) |
| { |
| GetCoprocessorVersion(aResponse); |
| } |
| else |
| { |
| ErrorHandler(aResponse, HttpStatusCode::kStatusMethodNotAllowed); |
| } |
| } |
| |
| void Resource::DeleteOutDatedDiagnostic(void) |
| { |
| auto eraseIt = mDiagSet.begin(); |
| for (eraseIt = mDiagSet.begin(); eraseIt != mDiagSet.end();) |
| { |
| auto diagInfo = eraseIt->second; |
| auto duration = duration_cast<microseconds>(steady_clock::now() - diagInfo.mStartTime).count(); |
| |
| if (duration >= kDiagResetTimeout) |
| { |
| eraseIt = mDiagSet.erase(eraseIt); |
| } |
| else |
| { |
| eraseIt++; |
| } |
| } |
| } |
| |
| void Resource::UpdateDiag(std::string aKey, std::vector<otNetworkDiagTlv> &aDiag) |
| { |
| DiagInfo value; |
| |
| value.mStartTime = steady_clock::now(); |
| value.mDiagContent.assign(aDiag.begin(), aDiag.end()); |
| mDiagSet[aKey] = value; |
| } |
| |
| void Resource::Diagnostic(const Request &aRequest, Response &aResponse) const |
| { |
| otbrError error = OTBR_ERROR_NONE; |
| OT_UNUSED_VARIABLE(aRequest); |
| struct otIp6Address rloc16address = *otThreadGetRloc(mInstance); |
| struct otIp6Address multicastAddress; |
| |
| VerifyOrExit(otThreadSendDiagnosticGet(mInstance, &rloc16address, kAllTlvTypes, sizeof(kAllTlvTypes), |
| &Resource::DiagnosticResponseHandler, |
| const_cast<Resource *>(this)) == OT_ERROR_NONE, |
| error = OTBR_ERROR_REST); |
| VerifyOrExit(otIp6AddressFromString(kMulticastAddrAllRouters, &multicastAddress) == OT_ERROR_NONE, |
| error = OTBR_ERROR_REST); |
| VerifyOrExit(otThreadSendDiagnosticGet(mInstance, &multicastAddress, kAllTlvTypes, sizeof(kAllTlvTypes), |
| &Resource::DiagnosticResponseHandler, |
| const_cast<Resource *>(this)) == OT_ERROR_NONE, |
| error = OTBR_ERROR_REST); |
| |
| exit: |
| |
| if (error == OTBR_ERROR_NONE) |
| { |
| aResponse.SetStartTime(steady_clock::now()); |
| aResponse.SetCallback(); |
| } |
| else |
| { |
| ErrorHandler(aResponse, HttpStatusCode::kStatusInternalServerError); |
| } |
| } |
| |
| void Resource::DiagnosticResponseHandler(otError aError, |
| otMessage *aMessage, |
| const otMessageInfo *aMessageInfo, |
| void *aContext) |
| { |
| static_cast<Resource *>(aContext)->DiagnosticResponseHandler(aError, aMessage, aMessageInfo); |
| } |
| |
| void Resource::DiagnosticResponseHandler(otError aError, const otMessage *aMessage, const otMessageInfo *aMessageInfo) |
| { |
| std::vector<otNetworkDiagTlv> diagSet; |
| otNetworkDiagTlv diagTlv; |
| otNetworkDiagIterator iterator = OT_NETWORK_DIAGNOSTIC_ITERATOR_INIT; |
| otError error; |
| char rloc[7]; |
| std::string keyRloc = "0xffee"; |
| |
| SuccessOrExit(aError); |
| |
| OTBR_UNUSED_VARIABLE(aMessageInfo); |
| |
| while ((error = otThreadGetNextDiagnosticTlv(aMessage, &iterator, &diagTlv)) == OT_ERROR_NONE) |
| { |
| if (diagTlv.mType == OT_NETWORK_DIAGNOSTIC_TLV_SHORT_ADDRESS) |
| { |
| snprintf(rloc, sizeof(rloc), "0x%04x", diagTlv.mData.mAddr16); |
| keyRloc = Json::CString2JsonString(rloc); |
| } |
| diagSet.push_back(diagTlv); |
| } |
| UpdateDiag(keyRloc, diagSet); |
| |
| exit: |
| if (aError != OT_ERROR_NONE) |
| { |
| otbrLogWarning("Failed to get diagnostic data: %s", otThreadErrorToString(aError)); |
| } |
| } |
| |
| } // namespace rest |
| } // namespace otbr |