diff --git a/src/cli/README_COMMISSIONER.md b/src/cli/README_COMMISSIONER.md index d199cf65f..0494f4a49 100644 --- a/src/cli/README_COMMISSIONER.md +++ b/src/cli/README_COMMISSIONER.md @@ -81,11 +81,12 @@ Energy: 00050000 0 0 0 0 ### joiner add -Usage: `commissioner joiner add ` +Usage: `commissioner joiner add | ` Add a Joiner entry. - eui64: The IEEE EUI-64 of the Joiner or '\*' to match any Joiner. +- discerner: The Joiner discerner in format `number/length`. - pskd: Pre-Shared Key for the Joiner. ```bash @@ -93,6 +94,11 @@ Add a Joiner entry. Done ``` +```bash +> commissioner joiner add 0xabc/12 J01NME +Done +``` + ### joiner remove Usage: `commissioner joiner remove ` diff --git a/src/cli/README_JOINER.md b/src/cli/README_JOINER.md index 02ff2458b..6d8c2ab5c 100644 --- a/src/cli/README_JOINER.md +++ b/src/cli/README_JOINER.md @@ -7,6 +7,7 @@ See [README_COMMISSIONING.md](README_COMMISSIONING.md). ## Command List - [help](#help) +- [discerner](#discerner) - [id](#id) - [start](#start) - [stop](#stop) @@ -28,6 +29,24 @@ stop Done ``` +### discerner + +Usage: `joiner discerner [discerner]` + +Print or set the Joiner Discerner. Note this value takes the place of the place of EUI-64 during the joiner session of Thread commissioning. + +- discerner: `clear` to clear discerner, `number/length` to set discerner + +```bash +> joiner discerner 0xabc/12 +Done +> joiner discerner +0xabc/12 +Done +> joiner discerner clear +Done +``` + ### id Usage: `joiner id` diff --git a/src/cli/cli.cpp b/src/cli/cli.cpp index dbffc3346..7742da7fc 100644 --- a/src/cli/cli.cpp +++ b/src/cli/cli.cpp @@ -399,6 +399,30 @@ otError Interpreter::ParseUnsignedLong(char *aString, unsigned long &aUnsignedLo return (*endptr == '\0') ? OT_ERROR_NONE : OT_ERROR_INVALID_ARGS; } +otError Interpreter::ParseJoinerDiscerner(char *aString, otJoinerDiscerner &aDiscerner) +{ + otError error = OT_ERROR_NONE; + char * separator = strstr(aString, "/"); + unsigned long length; + + VerifyOrExit(separator != nullptr, error = OT_ERROR_NOT_FOUND); + + SuccessOrExit(error = ParseUnsignedLong(separator + 1, length)); + VerifyOrExit(length > 0 && length <= 64, error = OT_ERROR_INVALID_ARGS); + + { + char * end; + unsigned long long value = strtoull(aString, &end, 0); + aDiscerner.mValue = value; + VerifyOrExit(end == separator, error = OT_ERROR_INVALID_ARGS); + } + + aDiscerner.mLength = static_cast(length); + +exit: + return error; +} + otError Interpreter::ParsePingInterval(const char *aString, uint32_t &aInterval) { otError error = OT_ERROR_NONE; diff --git a/src/cli/cli.hpp b/src/cli/cli.hpp index 59eadd33c..52cbf81f1 100644 --- a/src/cli/cli.hpp +++ b/src/cli/cli.hpp @@ -204,10 +204,11 @@ private: kDefaultPingCount = 1, }; - otError ParsePingInterval(const char *aString, uint32_t &aInterval); - void ProcessHelp(uint8_t aArgsLength, char *aArgs[]); - void ProcessBufferInfo(uint8_t aArgsLength, char *aArgs[]); - void ProcessChannel(uint8_t aArgsLength, char *aArgs[]); + otError ParsePingInterval(const char *aString, uint32_t &aInterval); + static otError ParseJoinerDiscerner(char *aString, otJoinerDiscerner &aJoinerDiscerner); + void ProcessHelp(uint8_t aArgsLength, char *aArgs[]); + void ProcessBufferInfo(uint8_t aArgsLength, char *aArgs[]); + void ProcessChannel(uint8_t aArgsLength, char *aArgs[]); #if (OPENTHREAD_CONFIG_THREAD_VERSION >= OT_THREAD_VERSION_1_2) void ProcessBackboneRouter(uint8_t aArgsLength, char *aArgs[]); diff --git a/src/cli/cli_commissioner.cpp b/src/cli/cli_commissioner.cpp index c6b24b587..606ad9ba6 100644 --- a/src/cli/cli_commissioner.cpp +++ b/src/cli/cli_commissioner.cpp @@ -116,20 +116,27 @@ otError Commissioner::ProcessJoiner(uint8_t aArgsLength, char *aArgs[]) { otError error; otExtAddress addr; - const otExtAddress *addrPtr; + const otExtAddress *addrPtr = nullptr; + otJoinerDiscerner discerner; VerifyOrExit(aArgsLength > 2, error = OT_ERROR_INVALID_ARGS); + memset(&discerner, 0, sizeof(discerner)); + if (strcmp(aArgs[2], "*") == 0) { - addrPtr = nullptr; + // Intentionally empty } - else + else if ((error = Interpreter::ParseJoinerDiscerner(aArgs[2], discerner)) == OT_ERROR_NOT_FOUND) { VerifyOrExit(Interpreter::Hex2Bin(aArgs[2], addr.m8, sizeof(addr)) == sizeof(addr), error = OT_ERROR_INVALID_ARGS); addrPtr = &addr; } + else if (error != OT_ERROR_NONE) + { + ExitNow(); + } if (strcmp(aArgs[1], "add") == 0) { @@ -142,8 +149,16 @@ otError Commissioner::ProcessJoiner(uint8_t aArgsLength, char *aArgs[]) SuccessOrExit(error = Interpreter::ParseUnsignedLong(aArgs[4], timeout)); } - SuccessOrExit( - error = otCommissionerAddJoiner(mInterpreter.mInstance, addrPtr, aArgs[3], static_cast(timeout))); + if (discerner.mLength) + { + SuccessOrExit(error = otCommissionerAddJoinerWithDiscerner(mInterpreter.mInstance, &discerner, aArgs[3], + static_cast(timeout))); + } + else + { + SuccessOrExit(error = otCommissionerAddJoiner(mInterpreter.mInstance, addrPtr, aArgs[3], + static_cast(timeout))); + } } else if (strcmp(aArgs[1], "remove") == 0) { diff --git a/src/cli/cli_joiner.cpp b/src/cli/cli_joiner.cpp index ddc5f12e5..0cf4dc8af 100644 --- a/src/cli/cli_joiner.cpp +++ b/src/cli/cli_joiner.cpp @@ -33,6 +33,8 @@ #include "cli_joiner.hpp" +#include + #include "cli/cli.hpp" #include "cli/cli_server.hpp" @@ -42,12 +44,47 @@ namespace ot { namespace Cli { const struct Joiner::Command Joiner::sCommands[] = { - {"help", &Joiner::ProcessHelp}, - {"id", &Joiner::ProcessId}, - {"start", &Joiner::ProcessStart}, - {"stop", &Joiner::ProcessStop}, + {"discerner", &Joiner::ProcessDiscerner}, {"help", &Joiner::ProcessHelp}, {"id", &Joiner::ProcessId}, + {"start", &Joiner::ProcessStart}, {"stop", &Joiner::ProcessStop}, }; +otError Joiner::ProcessDiscerner(uint8_t aArgsLength, char *aArgs[]) +{ + otError error = OT_ERROR_NONE; + + if (aArgsLength == 2) + { + otJoinerDiscerner discerner; + + memset(&discerner, 0, sizeof(discerner)); + if (strcmp(aArgs[1], "clear") == 0) + { + SuccessOrExit(error = otJoinerSetDiscerner(mInterpreter.mInstance, nullptr)); + } + else + { + VerifyOrExit(OT_ERROR_NONE == Interpreter::ParseJoinerDiscerner(aArgs[1], discerner), + error = OT_ERROR_INVALID_ARGS); + SuccessOrExit(error = otJoinerSetDiscerner(mInterpreter.mInstance, &discerner)); + } + } + else if (aArgsLength == 1) + { + const otJoinerDiscerner *discerner = otJoinerGetDiscerner(mInterpreter.mInstance); + + VerifyOrExit(discerner != nullptr, error = OT_ERROR_NOT_FOUND); + + mInterpreter.mServer->OutputFormat("0x%" PRIx64 "/%u\r\n", discerner->mValue, discerner->mLength); + } + else + { + error = OT_ERROR_INVALID_ARGS; + } + +exit: + return error; +} + otError Joiner::ProcessHelp(uint8_t aArgsLength, char *aArgs[]) { OT_UNUSED_VARIABLE(aArgsLength); diff --git a/src/cli/cli_joiner.hpp b/src/cli/cli_joiner.hpp index 802de29bb..5ff0eea74 100644 --- a/src/cli/cli_joiner.hpp +++ b/src/cli/cli_joiner.hpp @@ -79,6 +79,7 @@ private: otError (Joiner::*mCommand)(uint8_t aArgsLength, char *aArgs[]); }; + otError ProcessDiscerner(uint8_t aArgsLength, char *aArgs[]); otError ProcessHelp(uint8_t aArgsLength, char *aArgs[]); otError ProcessId(uint8_t aArgsLength, char *aArgs[]); otError ProcessStart(uint8_t aArgsLength, char *aArgs[]); diff --git a/tests/scripts/expect/_common.exp b/tests/scripts/expect/_common.exp index f3a7cc7e0..bc44ee841 100644 --- a/tests/scripts/expect/_common.exp +++ b/tests/scripts/expect/_common.exp @@ -27,7 +27,7 @@ # POSSIBILITY OF SUCH DAMAGE. # -proc wait_for {command expected} { +proc wait_for {command success {failure {[\r\n]FAILURE_NOT_EXPECTED[\r\n]}}} { set result 0 set timeout 1 for {set i 0} {$i < 20} {incr i} { @@ -36,9 +36,12 @@ proc wait_for {command expected} { } expect { - -re $expected { + -re $success { set result 1 } + -re $failure { + exit 1 + } timeout { # Do nothing } @@ -64,9 +67,38 @@ proc spawn_node {id} { return $spawn_id } +proc setup_leader {} { + # Sets up a Thread network spawn_1 as the leader. + global spawn_1 + global spawn_id + global env + + set spawn_1 [spawn_node 1] + + set spawn_id $spawn_1 + send "dataset init new\n" + expect "Done" + send "dataset commit active\n" + expect "Done" + send "ifconfig up\n" + expect "Done" + send "thread start\n" + expect "Done" + wait_for "state" "leader" + expect "Done" +} + proc dispose {} { send "\x04" expect eof } +proc dispose_leader {} { + global spawn_1 + global spawn_id + + set spawn_id $spawn_1 + dispose +} + set timeout 10 diff --git a/tests/scripts/expect/_multinode.exp b/tests/scripts/expect/_multinode.exp index 3844607ee..0771305c3 100644 --- a/tests/scripts/expect/_multinode.exp +++ b/tests/scripts/expect/_multinode.exp @@ -38,7 +38,6 @@ proc setup_nodes {} { global spawn_id global env - set spawn_1 [spawn_node 1] set spawn_2 [spawn_node 2] set psk "J01NME" @@ -48,17 +47,9 @@ proc setup_nodes {} { set eui64 $expect_out(1,string) expect "Done" + setup_leader + set spawn_id $spawn_1 - send "dataset init new\n" - expect "Done" - send "dataset commit active\n" - expect "Done" - send "ifconfig up\n" - expect "Done" - send "thread start\n" - expect "Done" - wait_for "state" "leader" - expect "Done" send "commissioner start\n" expect "Done" expect "Commissioner: active" @@ -84,12 +75,10 @@ proc setup_nodes {} { } proc dispose_nodes {} { - global spawn_1 global spawn_2 global spawn_id - set spawn_id $spawn_1 - dispose + dispose_leader set spawn_id $spawn_2 dispose } diff --git a/tests/scripts/expect/cli-discerner.exp b/tests/scripts/expect/cli-discerner.exp new file mode 100755 index 000000000..26b261ffa --- /dev/null +++ b/tests/scripts/expect/cli-discerner.exp @@ -0,0 +1,85 @@ +#!/usr/bin/expect -f +# +# 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. +# + +source "tests/scripts/expect/_common.exp" + +setup_leader + +set pskd "J01NME" +set discerner "0xabc/12" + +send "commissioner start\n" +expect "Done" +expect "Commissioner: active" +send "commissioner joiner add 1/0 $pskd\n" +expect "InvalidArgs" +send "commissioner joiner add $discerner $pskd\n" +expect "Done" + +set spawn_2 [spawn_node 2] + +set spawn_id $spawn_2 +send "mode rs\n" +expect "Done" +send "ifconfig up\n" +expect "Done" +send "joiner discerner\n" +expect "NotFound" +send "joiner discerner 1/1 2\n" +expect "InvalidArgs" +send "joiner discerner 1\n" +expect "InvalidArgs" +send "joiner discerner 1/\n" +expect "InvalidArgs" +send "joiner discerner 1/128\n" +expect "InvalidArgs" +send "joiner discerner 1/-1\n" +expect "InvalidArgs" +send "joiner discerner 1/0\n" +expect "InvalidArgs" +send "joiner discerner invalid/20\n" +expect "InvalidArgs" +send "joiner discerner clear\n" +expect "Done" +send "joiner discerner $discerner\n" +expect "Done" +send "joiner discerner\n" +expect "$discerner" +expect "Done" +send "joiner start $pskd\n" +expect "Done" +wait_for "" "Join success" "Join failed" +send "thread start\n" +expect "Done" +wait_for "state" "child" +expect "Done" + +dispose + +dispose_leader