/* * Copyright (c) 2017, 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. */ /** * @file * This file implements the wpan controller service */ #define OTBR_LOG_TAG "WEB" #include "web/web-service/wpan_service.hpp" #include #include #include #include #include "common/byteswap.hpp" #include "common/code_utils.hpp" namespace otbr { namespace Web { #define WPAN_RESPONSE_SUCCESS "successful" #define WPAN_RESPONSE_FAILURE "failed" std::string WpanService::HandleJoinNetworkRequest(const std::string &aJoinRequest) { Json::Value root; Json::Reader reader; Json::FastWriter jsonWriter; std::string response; int index; std::string masterKey; std::string prefix; bool defaultRoute; int ret = kWpanStatus_Ok; otbr::Web::OpenThreadClient client(mIfName); VerifyOrExit(client.Connect(), ret = kWpanStatus_SetFailed); VerifyOrExit(reader.parse(aJoinRequest.c_str(), root) == true, ret = kWpanStatus_ParseRequestFailed); index = root["index"].asUInt(); masterKey = root["masterKey"].asString(); prefix = root["prefix"].asString(); defaultRoute = root["defaultRoute"].asBool(); if (prefix.find('/') == std::string::npos) { prefix += "/64"; } VerifyOrExit(client.FactoryReset(), ret = kWpanStatus_LeaveFailed); VerifyOrExit((ret = commitActiveDataset(client, masterKey, mNetworks[index].mNetworkName, mNetworks[index].mChannel, mNetworks[index].mExtPanId, mNetworks[index].mPanId)) == kWpanStatus_Ok); VerifyOrExit(client.Execute("ifconfig up") != nullptr, ret = kWpanStatus_JoinFailed); VerifyOrExit(client.Execute("thread start") != nullptr, ret = kWpanStatus_JoinFailed); VerifyOrExit(client.Execute("prefix add %s paso%s", prefix.c_str(), (defaultRoute ? "r" : "")) != nullptr, ret = kWpanStatus_SetFailed); exit: root.clear(); root["result"] = WPAN_RESPONSE_SUCCESS; root["error"] = ret; if (ret != kWpanStatus_Ok) { otbrLogErr("Wpan service error: %d", ret); root["result"] = WPAN_RESPONSE_FAILURE; } response = jsonWriter.write(root); return response; } std::string WpanService::HandleFormNetworkRequest(const std::string &aFormRequest) { Json::Value root; Json::FastWriter jsonWriter; Json::Reader reader; std::string response; otbr::Psk::Pskc psk; char pskcStr[OT_PSKC_MAX_LENGTH * 2 + 1]; uint8_t extPanIdBytes[OT_EXTENDED_PANID_LENGTH]; std::string masterKey; std::string prefix; uint16_t channel; std::string networkName; std::string passphrase; uint16_t panId; uint64_t extPanId; bool defaultRoute; int ret = kWpanStatus_Ok; otbr::Web::OpenThreadClient client(mIfName); VerifyOrExit(client.Connect(), ret = kWpanStatus_SetFailed); pskcStr[OT_PSKC_MAX_LENGTH * 2] = '\0'; // for manipulating with strlen VerifyOrExit(reader.parse(aFormRequest.c_str(), root) == true, ret = kWpanStatus_ParseRequestFailed); masterKey = root["masterKey"].asString(); prefix = root["prefix"].asString(); channel = root["channel"].asUInt(); networkName = root["networkName"].asString(); passphrase = root["passphrase"].asString(); VerifyOrExit(sscanf(root["panId"].asString().c_str(), "%hx", &panId) == 1, ret = kWpanStatus_ParseRequestFailed); VerifyOrExit(sscanf(root["extPanId"].asString().c_str(), "%" PRIx64, &extPanId) == 1, ret = kWpanStatus_ParseRequestFailed); defaultRoute = root["defaultRoute"].asBool(); otbr::Utils::Hex2Bytes(root["extPanId"].asString().c_str(), extPanIdBytes, OT_EXTENDED_PANID_LENGTH); otbr::Utils::Bytes2Hex(psk.ComputePskc(extPanIdBytes, networkName.c_str(), passphrase.c_str()), OT_PSKC_MAX_LENGTH, pskcStr); if (prefix.find('/') == std::string::npos) { prefix += "/64"; } VerifyOrExit(client.FactoryReset(), ret = kWpanStatus_LeaveFailed); VerifyOrExit((ret = commitActiveDataset(client, masterKey, networkName, channel, extPanId, panId)) == kWpanStatus_Ok); VerifyOrExit(client.Execute("pskc %s", pskcStr) != nullptr, ret = kWpanStatus_SetFailed); VerifyOrExit(client.Execute("ifconfig up") != nullptr, ret = kWpanStatus_FormFailed); VerifyOrExit(client.Execute("thread start") != nullptr, ret = kWpanStatus_FormFailed); VerifyOrExit(client.Execute("prefix add %s paso%s", prefix.c_str(), (defaultRoute ? "r" : "")) != nullptr, ret = kWpanStatus_SetFailed); exit: root.clear(); root["result"] = WPAN_RESPONSE_SUCCESS; root["error"] = ret; if (ret != kWpanStatus_Ok) { otbrLogErr("Wpan service error: %d", ret); root["result"] = WPAN_RESPONSE_FAILURE; } response = jsonWriter.write(root); return response; } std::string WpanService::HandleAddPrefixRequest(const std::string &aAddPrefixRequest) { Json::Value root; Json::FastWriter jsonWriter; Json::Reader reader; std::string response; std::string prefix; bool defaultRoute; int ret = kWpanStatus_Ok; otbr::Web::OpenThreadClient client(mIfName); VerifyOrExit(client.Connect(), ret = kWpanStatus_SetFailed); VerifyOrExit(reader.parse(aAddPrefixRequest.c_str(), root) == true, ret = kWpanStatus_ParseRequestFailed); prefix = root["prefix"].asString(); defaultRoute = root["defaultRoute"].asBool(); if (prefix.find('/') == std::string::npos) { prefix += "/64"; } VerifyOrExit(client.Execute("prefix add %s paso%s", prefix.c_str(), (defaultRoute ? "r" : "")) != nullptr, ret = kWpanStatus_SetGatewayFailed); VerifyOrExit(client.Execute("netdata register") != nullptr, ret = kWpanStatus_SetGatewayFailed); exit: root.clear(); root["result"] = WPAN_RESPONSE_SUCCESS; root["error"] = ret; if (ret != kWpanStatus_Ok) { otbrLogErr("Wpan service error: %d", ret); root["result"] = WPAN_RESPONSE_FAILURE; } response = jsonWriter.write(root); return response; } std::string WpanService::HandleDeletePrefixRequest(const std::string &aDeleteRequest) { Json::Value root; Json::FastWriter jsonWriter; Json::Reader reader; std::string response; std::string prefix; int ret = kWpanStatus_Ok; otbr::Web::OpenThreadClient client(mIfName); VerifyOrExit(client.Connect(), ret = kWpanStatus_SetFailed); VerifyOrExit(reader.parse(aDeleteRequest.c_str(), root) == true, ret = kWpanStatus_ParseRequestFailed); prefix = root["prefix"].asString(); if (prefix.find('/') == std::string::npos) { prefix += "/64"; } VerifyOrExit(client.Execute("prefix remove %s", prefix.c_str()) != nullptr, ret = kWpanStatus_SetGatewayFailed); VerifyOrExit(client.Execute("netdata register") != nullptr, ret = kWpanStatus_SetGatewayFailed); exit: root.clear(); root["result"] = WPAN_RESPONSE_SUCCESS; root["error"] = ret; if (ret != kWpanStatus_Ok) { otbrLogErr("Wpan service error: %d", ret); root["result"] = WPAN_RESPONSE_FAILURE; } response = jsonWriter.write(root); return response; } std::string WpanService::HandleStatusRequest() { Json::Value root, networkInfo; Json::FastWriter jsonWriter; std::string response, networkName, extPanId, propertyValue; int ret = kWpanStatus_Ok; otbr::Web::OpenThreadClient client(mIfName); char * rval; networkInfo["WPAN service"] = "uninitialized"; VerifyOrExit(client.Connect(), ret = kWpanStatus_SetFailed); VerifyOrExit((rval = client.Execute("state")) != nullptr, ret = kWpanStatus_GetPropertyFailed); networkInfo["RCP:State"] = rval; if (!strcmp(rval, "disabled")) { networkInfo["WPAN service"] = "offline"; ExitNow(); } else if (!strcmp(rval, "detached")) { networkInfo["WPAN service"] = "associating"; ExitNow(); } else { networkInfo["WPAN service"] = "associated"; } VerifyOrExit((rval = client.Execute("version")) != nullptr, ret = kWpanStatus_GetPropertyFailed); networkInfo["OpenThread:Version"] = rval; VerifyOrExit((rval = client.Execute("version api")) != nullptr, ret = kWpanStatus_GetPropertyFailed); networkInfo["OpenThread:Version API"] = rval; VerifyOrExit((rval = client.Execute("rcp version")) != nullptr, ret = kWpanStatus_GetPropertyFailed); networkInfo["RCP:Version"] = rval; VerifyOrExit((rval = client.Execute("eui64")) != nullptr, ret = kWpanStatus_GetPropertyFailed); networkInfo["RCP:EUI64"] = rval; VerifyOrExit((rval = client.Execute("channel")) != nullptr, ret = kWpanStatus_GetPropertyFailed); networkInfo["RCP:Channel"] = rval; VerifyOrExit((rval = client.Execute("txpower")) != nullptr, ret = kWpanStatus_GetPropertyFailed); networkInfo["RCP:TxPower"] = rval; VerifyOrExit((rval = client.Execute("networkname")) != nullptr, ret = kWpanStatus_GetPropertyFailed); networkInfo["Network:Name"] = rval; VerifyOrExit((rval = client.Execute("extpanid")) != nullptr, ret = kWpanStatus_GetPropertyFailed); networkInfo["Network:XPANID"] = rval; VerifyOrExit((rval = client.Execute("panid")) != nullptr, ret = kWpanStatus_GetPropertyFailed); networkInfo["Network:PANID"] = rval; VerifyOrExit((rval = client.Execute("partitionid")) != nullptr, ret = kWpanStatus_GetPropertyFailed); networkInfo["Network:PartitionID"] = rval; { static const char kMeshLocalPrefixLocator[] = "Mesh Local Prefix: "; static const char kMeshLocalAddressTokenLocator[] = "0:ff:fe00:"; static const char localAddressToken[] = "fd"; static const char linkLocalAddressToken[] = "fe80"; std::string meshLocalPrefix = ""; VerifyOrExit((rval = client.Execute("dataset active")) != nullptr, ret = kWpanStatus_GetPropertyFailed); rval = strstr(rval, kMeshLocalPrefixLocator); if (rval != nullptr) { rval += sizeof(kMeshLocalPrefixLocator) - 1; *strstr(rval, "\r\n") = '\0'; networkInfo["IPv6:MeshLocalPrefix"] = rval; meshLocalPrefix = rval; meshLocalPrefix.resize(meshLocalPrefix.find(":/")); } VerifyOrExit((rval = client.Execute("ipaddr")) != nullptr, ret = kWpanStatus_GetPropertyFailed); for (rval = strtok(rval, "\r\n"); rval != nullptr; rval = strtok(nullptr, "\r\n")) { char *meshLocalAddressToken = nullptr; if (strstr(rval, "> ") != nullptr) { rval += 2; } if (strstr(rval, linkLocalAddressToken) == rval) { networkInfo["IPv6:LinkLocalAddress"] = rval; continue; } meshLocalAddressToken = strstr(rval, kMeshLocalAddressTokenLocator); if (meshLocalAddressToken == nullptr) { if ((meshLocalPrefix.size() > 0) && (strstr(rval, meshLocalPrefix.c_str()) == rval)) { networkInfo["IPv6:MeshLocalAddress"] = rval; continue; } if (strstr(rval, localAddressToken) != rval) { networkInfo["IPv6:GlobalAddress"] = rval; } else { networkInfo["IPv6:LocalAddress"] = rval; } } else { *meshLocalAddressToken = '\0'; meshLocalPrefix = rval; networkInfo["IPv6:MeshLocalPrefix"] = rval; std::string la = networkInfo.get("IPv6:LocalAddress", "unknown").asString(); if (strstr(rval, la.c_str()) != nullptr) { networkInfo["IPv6:MeshLocalAddress"] = networkInfo.get("IPv6:LocalAddress", "notfound").asString(); networkInfo.removeMember("IPv6:LocalAddress"); } } } } exit: root["result"] = networkInfo; if (ret != kWpanStatus_Ok) { root["result"] = WPAN_RESPONSE_FAILURE; otbrLogErr("Wpan service error: %d", ret); } root["error"] = ret; response = jsonWriter.write(root); return response; } std::string WpanService::HandleAvailableNetworkRequest() { Json::Value root, networks, networkInfo; Json::FastWriter jsonWriter; std::string response; int ret = kWpanStatus_Ok; otbr::Web::OpenThreadClient client(mIfName); VerifyOrExit(client.Connect(), ret = kWpanStatus_ScanFailed); VerifyOrExit((mNetworksCount = client.Scan(mNetworks, sizeof(mNetworks) / sizeof(mNetworks[0]))) > 0, ret = kWpanStatus_NetworkNotFound); for (int i = 0; i < mNetworksCount; i++) { char extPanId[OT_EXTENDED_PANID_LENGTH * 2 + 1], panId[OT_PANID_LENGTH * 2 + 3], hardwareAddress[OT_HARDWARE_ADDRESS_LENGTH * 2 + 1]; otbr::Utils::Long2Hex(bswap_64(mNetworks[i].mExtPanId), extPanId); otbr::Utils::Bytes2Hex(mNetworks[i].mHardwareAddress, OT_HARDWARE_ADDRESS_LENGTH, hardwareAddress); sprintf(panId, "0x%X", mNetworks[i].mPanId); networkInfo[i]["nn"] = mNetworks[i].mNetworkName; networkInfo[i]["xp"] = extPanId; networkInfo[i]["pi"] = panId; networkInfo[i]["ch"] = mNetworks[i].mChannel; networkInfo[i]["ha"] = hardwareAddress; } root["result"] = networkInfo; exit: if (ret != kWpanStatus_Ok) { root["result"] = WPAN_RESPONSE_FAILURE; otbrLogErr("Error is %d", ret); } root["error"] = ret; response = jsonWriter.write(root); return response; } int WpanService::GetWpanServiceStatus(std::string &aNetworkName, std::string &aExtPanId) const { int status = kWpanStatus_Ok; otbr::Web::OpenThreadClient client(mIfName); const char * rval; VerifyOrExit(client.Connect(), status = kWpanStatus_Uninitialized); rval = client.Execute("state"); VerifyOrExit(rval != nullptr, status = kWpanStatus_Down); if (!strcmp(rval, "disabled")) { status = kWpanStatus_Offline; } else if (!strcmp(rval, "detached")) { status = kWpanStatus_Associating; } else { rval = client.Execute("networkname"); VerifyOrExit(rval != nullptr, status = kWpanStatus_Down); aNetworkName = rval; rval = client.Execute("extpanid"); VerifyOrExit(rval != nullptr, status = kWpanStatus_Down); aExtPanId = rval; } exit: return status; } std::string WpanService::HandleCommission(const std::string &aCommissionRequest) { Json::Value root; Json::Reader reader; Json::FastWriter jsonWriter; int ret = kWpanStatus_Ok; std::string pskd; std::string response; const char * rval; VerifyOrExit(reader.parse(aCommissionRequest.c_str(), root) == true, ret = kWpanStatus_ParseRequestFailed); pskd = root["pskd"].asString(); { otbr::Web::OpenThreadClient client(mIfName); VerifyOrExit(client.Connect(), ret = kWpanStatus_Uninitialized); for (int i = 0; i < 5; i++) { VerifyOrExit((rval = client.Execute("commissioner state")) != nullptr, ret = kWpanStatus_Down); if (strcmp(rval, "disabled") == 0) { VerifyOrExit((rval = client.Execute("commissioner start")) != nullptr, ret = kWpanStatus_Down); } else if (strcmp(rval, "active") == 0) { VerifyOrExit(client.Execute("commissioner joiner add * %s", pskd.c_str()) != nullptr, ret = kWpanStatus_Down); root["error"] = ret; ExitNow(); } sleep(1); } client.Execute("commissioner stop"); } ret = kWpanStatus_SetFailed; exit: root.clear(); root["result"] = WPAN_RESPONSE_SUCCESS; root["error"] = ret; if (ret != kWpanStatus_Ok) { root["result"] = WPAN_RESPONSE_FAILURE; otbrLogErr("error: %d", ret); } response = jsonWriter.write(root); return response; } int WpanService::commitActiveDataset(otbr::Web::OpenThreadClient &aClient, const std::string & aMasterKey, const std::string & aNetworkName, uint16_t aChannel, uint64_t aExtPanId, uint16_t aPanId) { int ret = kWpanStatus_Ok; VerifyOrExit(aClient.Execute("dataset init new") != nullptr, ret = kWpanStatus_SetFailed); VerifyOrExit(aClient.Execute("dataset masterkey %s", aMasterKey.c_str()) != nullptr, ret = kWpanStatus_SetFailed); VerifyOrExit(aClient.Execute("dataset networkname %s", escapeOtCliEscapable(aNetworkName).c_str()) != nullptr, ret = kWpanStatus_SetFailed); VerifyOrExit(aClient.Execute("dataset channel %u", aChannel) != nullptr, ret = kWpanStatus_SetFailed); VerifyOrExit(aClient.Execute("dataset extpanid %016" PRIx64, aExtPanId) != nullptr, ret = kWpanStatus_SetFailed); VerifyOrExit(aClient.Execute("dataset panid %u", aPanId) != nullptr, ret = kWpanStatus_SetFailed); VerifyOrExit(aClient.Execute("dataset commit active") != nullptr, ret = kWpanStatus_SetFailed); exit: return ret; } std::string WpanService::escapeOtCliEscapable(const std::string &aArg) { std::stringbuf strbuf; for (char c : aArg) { switch (c) { case ' ': case '\t': case '\r': case '\n': case '\\': strbuf.sputc('\\'); // Fallthrough default: strbuf.sputc(c); } } return strbuf.str(); } } // namespace Web } // namespace otbr