From 193bb9ac330db707410ea251e90943d2c4dbc97e Mon Sep 17 00:00:00 2001 From: kangping Date: Sun, 18 Apr 2021 12:45:36 +0800 Subject: [PATCH] [example] add minimum commissioner example --- .github/workflows/tests.yml | 27 +++ example/BUILDING.md | 32 +++ example/CMakeLists.txt | 46 ++++ example/README.md | 83 +++++++ example/mini_commissioner.cpp | 218 ++++++++++++++++++ tests/integration/common.sh | 19 ++ .../test_mini_commissioner.sh | 51 ++++ 7 files changed, 476 insertions(+) create mode 100644 example/BUILDING.md create mode 100644 example/CMakeLists.txt create mode 100644 example/README.md create mode 100644 example/mini_commissioner.cpp create mode 100755 tests/integration/mini_commissioner/test_mini_commissioner.sh diff --git a/.github/workflows/tests.yml b/.github/workflows/tests.yml index 1737f6796..82b28a0dc 100644 --- a/.github/workflows/tests.yml +++ b/.github/workflows/tests.yml @@ -70,3 +70,30 @@ jobs: lcov --directory build/ --capture --output-file coverage.info lcov --list coverage.info bash <(curl -s https://codecov.io/bash) -f coverage.info + + mini-commissioner: + runs-on: ubuntu-18.04 + steps: + - uses: actions/checkout@v2 + - name: Bootstrap + run: | + script/bootstrap.sh + - name: Build + run: | + cd example && mkdir build && cd build + cmake -GNinja \ + -DCMAKE_CXX_STANDARD=11 \ + -DCMAKE_CXX_STANDARD_REQUIRED=ON \ + -DCMAKE_BUILD_TYPE=Release \ + -DCMAKE_INSTALL_PREFIX=/usr/local \ + -DOT_COMM_REFERENCE_DEVICE=ON \ + -DOT_COMM_CCM=OFF \ + -DOT_COMM_WARNING_AS_ERROR=ON \ + .. + ninja + sudo ninja install + - name: Run mini-commissioner tests + run: | + cd tests/integration + ./bootstrap.sh + ./mini_commissioner/test_mini_commissioner.sh diff --git a/example/BUILDING.md b/example/BUILDING.md new file mode 100644 index 000000000..16519acbf --- /dev/null +++ b/example/BUILDING.md @@ -0,0 +1,32 @@ +# Build Minimum Thread Commissioner + +## CMake Build + +```shell +cd ~/ot-commissioner/example +mkdir -p build && cd build +cmake -GNinja -DBUILD_SHARED_LIBS=ON .. +ninja +``` + +A `mini_commissioner` binary will be generated in `build/`. + +## Standalone build + +Build and install the OT Commissioner library: + +```c++ +cd ~/ot-commissioner +mkdir -p build && cd build +cmake -GNinja -DBUILD_SHARED_LIBS=ON .. +sudo ninja install +``` + +Build the `mini_commissioner` app: + +```c++ +cd ~/ot-commissioner/example +clang++ -std=c++11 -Wall -g mini_commissioner.cpp -o mini_commissioner -lcommissioner -lcommissioner-common +``` + +The `mini_commissioner` binary will be generated in current directory. diff --git a/example/CMakeLists.txt b/example/CMakeLists.txt new file mode 100644 index 000000000..0a68fdea5 --- /dev/null +++ b/example/CMakeLists.txt @@ -0,0 +1,46 @@ +# +# Copyright (c) 2021, The OpenThread Commissioner 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. +# + +cmake_minimum_required(VERSION 3.10.1) +project(mini-commissioner VERSION 0.1.0) + +# The commissioner library requires C++11 or higher. +set(CMAKE_CXX_STANDARD 11) + +add_subdirectory(${CMAKE_CURRENT_SOURCE_DIR}/.. + ${CMAKE_CURRENT_BINARY_DIR}/commissioner + EXCLUDE_FROM_ALL +) + +add_executable(mini-commissioner mini_commissioner.cpp) + +target_link_libraries(mini-commissioner PRIVATE commissioner) + +install(TARGETS mini-commissioner + RUNTIME DESTINATION bin +) diff --git a/example/README.md b/example/README.md new file mode 100644 index 000000000..e2c3caaa8 --- /dev/null +++ b/example/README.md @@ -0,0 +1,83 @@ +# Minimum Thread Commissioner + +This directory includes an example of building a minimum Thread Commissioner with the OT Commissioner library. The minimum Thread Commissioner petitions to given Border Router, enables MeshCoP for all joiners and commissions joiners. + +## Build + +See [BUILDING.md](./BUILDING.md) for building this mini Commissioner program. You can find details about the Border Router (`ot-daemon`) and simulation devices in this [codelab](https://openthread.io/codelabs/openthread-simulation-posix). + +## Run the Commissioner + +### Start the BR (ot-daemon) + +See the [codelab](https://openthread.io/codelabs/openthread-simulation-posix). + +### Start the Mini Commissioner + +After a successful build, we should get a `mini_commissioner` binary which can be started with four arguments: + +```shell +./mini_commissioner ::1 49191 00112233445566778899aabbccddeeff ABCDEF +``` + +You can get the usage by starting `mini_commissioner` with no arguments: + +```shell +./minim_commissioner +usage: + mini_commissioner +``` + +> Note: the WiFi/ethernet interface address of the BR should be used but not the Thread interface address. + +If everything go smooth, we will get outputs like below: + +```shell +./mini_commissioner ::1 49191 ca117352886a861cce8a91021e65dd1c ABCDEF +=================================================== +[Border Router address] : ::1 +[Border Router port] : 49191 +[PSKc] : ca117352886a861cce8a91021e65dd1c +[PSKd] : ABCDEF +=================================================== + +=================================================== +type CRTL + C to quit! +=================================================== + +petitioning to [::1]:49191 +the commissioner is active: true +enabling MeshCoP for all joiners +waiting for joiners +``` + +> Note: you are free to quit at any time with `CTRL+C`. + +### Start the joiner + +See the [codelab](https://openthread.io/codelabs/openthread-simulation-posix). + +if everything go smooth, we will get output like below for two times of joining: + +```shell +joiner "5ab1f2745b625c90" is requesting join the Thread network +joiner "5ab1f2745b625c90" is connected: OK +joiner "5ab1f2745b625c90" is commissioned +[Vendor Name] : OPENTHREAD +[Vendor Model] : NRF52840 +[Vendor SW Version] : 20191113-01632-g +[Vendor Stack Version] : f4ce36000010 +[Provisioning URL] : +[Vendor Data] : + +joiner "5ab1f2745b625c90" is requesting join the Thread network +joiner "5ab1f2745b625c90" is connected: OK +joiner "5ab1f2745b625c90" is commissioned +[Vendor Name] : OPENTHREAD +[Vendor Model] : NRF52840 +[Vendor SW Version] : 20191113-01632-g +[Vendor Stack Version] : f4ce36000010 +[Provisioning URL] : +[Vendor Data] : + +``` diff --git a/example/mini_commissioner.cpp b/example/mini_commissioner.cpp new file mode 100644 index 000000000..1c7922e7f --- /dev/null +++ b/example/mini_commissioner.cpp @@ -0,0 +1,218 @@ +/* + * Copyright (c) 2021, The OpenThread Commissioner 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 + * The file implements the minimum Thread Commissioner example app. + */ + +#include +#include + +#include +#include +#include + +#include + +#include + +using namespace ot::commissioner; + +static std::string ToHexString(const ByteArray &aBytes) +{ + std::string str; + + for (uint8_t byte : aBytes) + { + char buf[3]; + + sprintf(buf, "%02x", byte); + str.append(buf); + } + + return str; +} + +static ByteArray FromHexString(const std::string &aHex) +{ + assert(aHex.size() % 2 == 0); + + ByteArray bytes; + + for (size_t i = 0; i < aHex.size(); i += 2) + { + assert(isxdigit(aHex[i])); + assert(isxdigit(aHex[i + 1])); + + uint8_t x = isdigit(aHex[i]) ? (aHex[i] - '0') : (tolower(aHex[i]) - 'a' + 10); + uint8_t y = isdigit(aHex[i + 1]) ? (aHex[i + 1] - '0') : (tolower(aHex[i + 1]) - 'a' + 10); + + bytes.push_back((x << 4) | y); + } + return bytes; +} + +class MyCommissionerHandler : public CommissionerHandler +{ +public: + MyCommissionerHandler(const std::string &aPskd) + : mPskd(aPskd) + { + } + + std::string OnJoinerRequest(const ByteArray &aJoinerId) override + { + auto joinerId = ToHexString(aJoinerId); + + printf("\n"); + printf("joiner \"%s\" is requesting join the Thread network\n", joinerId.c_str()); + + return mPskd; + } + + void OnJoinerConnected(const ByteArray &aJoinerId, Error aError) override + { + auto joinerId = ToHexString(aJoinerId); + + printf("joiner \"%s\" is connected: %s\n", joinerId.c_str(), aError.ToString().c_str()); + } + + bool OnJoinerFinalize(const ByteArray & aJoinerId, + const std::string &aVendorName, + const std::string &aVendorModel, + const std::string &aVendorSwVersion, + const ByteArray & aVendorStackVersion, + const std::string &aProvisioningUrl, + const ByteArray & aVendorData) override + { + printf("joiner \"%s\" is commissioned\n", ToHexString(aJoinerId).c_str()); + printf("[Vendor Name] : %s\n", aVendorName.c_str()); + printf("[Vendor Model] : %s\n", aVendorModel.c_str()); + printf("[Vendor SW Version] : %s\n", aVendorSwVersion.c_str()); + printf("[Vendor Stack Version] : %s\n", ToHexString(aVendorStackVersion).c_str()); + printf("[Provisioning URL] : %s\n", aProvisioningUrl.c_str()); + printf("[Vendor Data] : %s\n", ToHexString(aVendorData).c_str()); + + return true; + } + +private: + std::string mPskd; +}; + +std::shared_ptr commissioner; + +void SignalHandler(int signal) +{ + if (commissioner != nullptr) + { + printf("\nResigning the commissioner\n"); + commissioner->Resign().IgnoreError(); + } + + exit(0); +} + +int main(int argc, const char *argv[]) +{ + if (argc != 5) + { + printf("usage:\n"); + printf(" %s \n", argv[0]); + return -1; + } + + std::string brAddr = argv[1]; + uint16_t brPort = std::stoul(argv[2]); + ByteArray pskc = FromHexString(argv[3]); + std::string pskd = argv[4]; + + printf("===================================================\n"); + printf("[Border Router address] : %s\n", brAddr.c_str()); + printf("[Border Router port] : %hu\n", brPort); + printf("[PSKc] : %s\n", ToHexString(pskc).c_str()); + printf("[PSKd] : %s\n", pskd.c_str()); + printf("===================================================\n\n"); + + MyCommissionerHandler myHandler{pskd}; + commissioner = Commissioner::Create(myHandler); + + signal(SIGINT, SignalHandler); + printf("===================================================\n"); + printf("type CRTL + C to quit!\n"); + printf("===================================================\n\n"); + + Config config; + config.mEnableCcm = false; + config.mPSKc = pskc; + + Error error; + + if ((error = commissioner->Init(config)) != ErrorCode::kNone) + { + printf("failed to initialize the commissioner: %s\n", error.ToString().c_str()); + return -1; + } + + std::string existingCommissionerId; + + printf("petitioning to [%s]:%hu\n", brAddr.c_str(), brPort); + error = commissioner->Petition(existingCommissionerId, brAddr, brPort); + if (error != ErrorCode::kNone) + { + printf("failed to petition to BR at [%s]:%hu: %s\n", brAddr.c_str(), brPort, error.ToString().c_str()); + return -1; + } + + // Check if we are active now. + printf("the commissioner is active: %s\n", commissioner->IsActive() ? "true" : "false"); + assert(commissioner->IsActive()); + + CommissionerDataset dataset; + + printf("enabling MeshCoP for all joiners\n"); + dataset.mPresentFlags |= CommissionerDataset::kSteeringDataBit; + dataset.mSteeringData = {0xFF}; // Set the steeering data to all-ones to allow all joiners. + error = commissioner->SetCommissionerDataset(dataset); + if (error != ErrorCode::kNone) + { + printf("failed to enable MeshCop for all joiners: %s\n", error.ToString().c_str()); + return -1; + } + + printf("waiting for joiners\n"); + while (true) + { + sleep(1); + } + + commissioner->Resign().IgnoreError(); + + return 0; +} diff --git a/tests/integration/common.sh b/tests/integration/common.sh index fd9482847..c884724ad 100755 --- a/tests/integration/common.sh +++ b/tests/integration/common.sh @@ -53,6 +53,8 @@ readonly COMMISSIONER_CTL=/usr/local/bin/commissioner_ctl.py readonly COMMISSIONER_DAEMON_LOG=${RUNTIME_DIR}/commissioner-daemon.log readonly COMMISSIONER_LOG=./commissioner.log +readonly MINI_COMMISSIONER=/usr/local/bin/mini-commissioner + readonly CCM_TOKEN=/usr/local/etc/commissioner/credentials/token.hex readonly CCM_CA_CERT=/usr/local/etc/commissioner/credentials/trust-anchor.pem readonly NON_CCM_CONFIG=/usr/local/etc/commissioner/non-ccm-config.json @@ -247,3 +249,20 @@ start_border_agent_mdns_service() { stop_border_agent_mdns_service() { sudo service avahi-daemon stop } + +# Start mini commissioner in background. +start_mini_commissioner() { + if [ -n "$(pgrep -f "${MINI_COMMISSIONER}")" ]; then + sudo kill -9 "$(pgrep -f "${MINI_COMMISSIONER}")" + fi + + echo "starting mini commissioner" + ${MINI_COMMISSIONER} 127.0.0.1 49191 ${PSKC} ${JOINER_CREDENTIAL} 2>&1 & + sleep 1 + + pgrep -f "${MINI_COMMISSIONER}" +} + +stop_mini_commissioner() { + sudo kill -9 "$(pgrep -f "${MINI_COMMISSIONER}")" +} diff --git a/tests/integration/mini_commissioner/test_mini_commissioner.sh b/tests/integration/mini_commissioner/test_mini_commissioner.sh new file mode 100755 index 000000000..af360a177 --- /dev/null +++ b/tests/integration/mini_commissioner/test_mini_commissioner.sh @@ -0,0 +1,51 @@ +#!/bin/bash +# +# Copyright (c) 2021, The OpenThread Commissioner 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. +# + +## This file includes tests for the mini commissioner example. +## +## Test cases in this file is not included in and driven by the +## `run_tests.sh` script. Instead, run this script directly. +## + +set -euox pipefail + +. "$(dirname "$0")"/../common.sh + +test_mini_commissioner() { + start_daemon + form_network "${PSKC}" + + start_mini_commissioner + start_joiner "meshcop" + + stop_mini_commissioner + stop_daemon +} + +test_mini_commissioner