#!/bin/bash # # 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. # # Test thread commissioning along with openthread. # # Usage: # ./meshcop # test with latest openthread. # NO_CLEAN=1 ./meshcop # test with existing binaries in ${TEST_BASE}. # TEST_CASE=mdns_service ./meshcop # test the meshcop mDNS service. set -euxo pipefail # The test case to run. available cases are: # - commissioning # - mdns_service TEST_CASE="${TEST_CASE:-commissioning}" readonly TEST_CASE # Get our starting directory and remember it ORIGIN_PWD="$(pwd)" readonly ORIGIN_PWD SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)" readonly SCRIPT_DIR #--------------------------------------- # Configurations #--------------------------------------- OT_RCP="ot-rcp" readonly OT_RCP OT_CLI="${OT_CLI:-ot-cli-ftd}" readonly OT_CLI ABS_TOP_BUILDDIR="$(cd "${top_builddir:-"${SCRIPT_DIR}"/../../}" && pwd)" readonly ABS_TOP_BUILDDIR ABS_TOP_SRCDIR="$(cd "${top_srcdir:-"${SCRIPT_DIR}"/../../}" && pwd)" readonly ABS_TOP_SRCDIR NO_CLEAN="${NO_CLEAN:-1}" readonly NO_CLEAN IGNORE_INSTALLED="${IGNORE_INSTALLED:-0}" readonly IGNORE_INSTALLED OTBR_USE_WEB_COMMISSIONER="${USE_WEB_COMMISSIONER:-0}" readonly OTBR_USE_WEB_COMMISSIONER #---------------------------------------- # Test constants #---------------------------------------- TEST_BASE=/tmp/test-otbr readonly TEST_BASE OTBR_AGENT=otbr-agent readonly OTBR_AGENT OTBR_WEB=otbr-web readonly OTBR_WEB OT_COMMISSIONER_CLI=commissioner-cli readonly OT_COMMISSIONER_CLI STAGE_DIR="${TEST_BASE}/stage" readonly STAGE_DIR BUILD_DIR="${TEST_BASE}/build" readonly BUILD_DIR OTBR_PSKC_PATH="${ABS_TOP_BUILDDIR}/tools/pskc" readonly OTBR_PSKC_PATH OTBR_AGENT_PATH="${ABS_TOP_BUILDDIR}/src/agent/${OTBR_AGENT}" readonly OTBR_AGENT_PATH OTBR_DBUS_CONF="${ABS_TOP_BUILDDIR}/src/agent/otbr-agent.conf" readonly OTBR_DBUS_CONF OTBR_WEB_PATH="${ABS_TOP_BUILDDIR}/src/web/${OTBR_WEB}" readonly OTBR_WEB_PATH OT_CTL="${ABS_TOP_BUILDDIR}/third_party/openthread/repo/src/posix/ot-ctl" readonly OT_CTL # The node ids LEADER_NODE_ID=1 readonly LEADER_NODE_ID JOINER_NODE_ID=2 readonly JOINER_NODE_ID # Web GUI OTBR_WEB_HOST=127.0.0.1 readonly OTBR_WEB_HOST OTBR_WEB_PORT=8773 readonly OTBR_WEB_PORT OTBR_WEB_URL="http://${OTBR_WEB_HOST}:${OTBR_WEB_PORT}" readonly OTBR_WEB_URL # External commissioner OT_COMMISSIONER_PATH=${BUILD_DIR}/ot-commissioner/build/src/app/cli/commissioner-cli readonly OT_COMMISSIONER_PATH OT_COMMISSIONER_CONFIG=${BUILD_DIR}/ot-commissioner/src/app/etc/commissioner/non-ccm-config.json readonly OT_COMMISSIONER_CONFIG # # NOTE Joiner pass phrase: # Must be at least 6 bytes long # And this example has: J ZERO ONE N E R # We cannot use letter O and I because Q O I Z are not allowed per spec OT_JOINER_PASSPHRASE=J01NER readonly OT_JOINER_PASSPHRASE # 18b430 is the nest EUI prefix. OT_JOINER_EUI64="18b430000000000${JOINER_NODE_ID}" readonly OT_JOINER_EUI64 # The border agent, and ncp needs a pass phrase. OT_AGENT_PASSPHRASE=MYPASSPHRASE readonly OT_AGENT_PASSPHRASE # The network needs a name. OT_NETWORK_NAME=MyTestNetwork readonly OT_NETWORK_NAME # The TUN device for OpenThread border router. TUN_NAME=wpan0 readonly TUN_NAME # The default meshcop service instance name OT_SERVICE_INSTANCE='OpenThread\(\\032\| \)BorderRouter\(\\032\| \)#[0-9A-F][0-9A-F][0-9A-F][0-9A-F]' readonly OT_SERVICE_INSTANCE echo "ORIGIN_PWD: ${ORIGIN_PWD}" echo "TEST_BASE: ${TEST_BASE}" echo "ABS_TOP_SRCDIR=${ABS_TOP_SRCDIR}" echo "ABS_TOP_BUILDDIR=${ABS_TOP_BUILDDIR}" #---------------------------------------- # Helper functions #---------------------------------------- die() { exit_message="$*" echo " *** ERROR: $*" exit 1 } exists_or_die() { [[ -f $1 ]] || die "Missing file: $1" } executable_or_die() { [[ -x $1 ]] || die "Missing executable: $1" } random_channel() { echo $((11 + "${RANDOM}" % 16)) } random_panid() { printf "0x%04x" "${RANDOM}" } random_xpanid() { printf "%04x%04x%04x%04x" "${RANDOM}" "${RANDOM}" "${RANDOM}" "${RANDOM}" } random_networkkey() { printf "%04x%04x%04x%04x%04x%04x%04x%04x" "${RANDOM}" "${RANDOM}" "${RANDOM}" "${RANDOM}" "${RANDOM}" "${RANDOM}" "${RANDOM}" "${RANDOM}" } write_syslog() { logger -s -p syslog.alert "OPENTHREAD_TEST: $*" } output_logs() { write_syslog 'All apps should be dead now' # part 1 # ------ # # On travis (the CI server), we can't see what went into the # syslog. So this is here so we can see the output. # # part 2 # ------ # # If we run locally, it is sometimes helpful for our victim (you # the developer) to have logs split upto various files to help # that victim, we'll GREP the log files according. # # Wait 5 seconds for the "logs to flush" sleep 5 cd "${ORIGIN_PWD}" echo 'START_LOG: SYSLOG ===================' tee complete-syslog.log "' echo '=====================================' } build_dependencies() { # Clean up old stuff if [[ ${NO_CLEAN} != 1 ]]; then [[ ! -d ${STAGE_DIR} ]] || rm -rf "${STAGE_DIR}" [[ ! -d ${BUILD_DIR} ]] || rm -rf "${BUILD_DIR}" fi [[ -d ${STAGE_DIR} ]] || mkdir -p "${STAGE_DIR}" [[ -d ${BUILD_DIR} ]] || mkdir -p "${BUILD_DIR}" # As above, these steps are broken up ot_cli=$(command -v "${OT_CLI}") ot_rcp=$(command -v "${OT_RCP}") if [ "${TEST_CASE}" == "commissioning" ] \ && [[ ${OTBR_USE_WEB_COMMISSIONER} != 1 ]] then ot_commissioner_build fi write_syslog "TEST: BUILD COMPLETE" } test_setup() { # message for general failures exit_message="JOINER FAILED" executable_or_die "${OTBR_AGENT_PATH}" executable_or_die "${OTBR_WEB_PATH}" # Remove flashes sudo rm -vrf "${TEST_BASE}/tmp" # OPENTHREAD_POSIX_DAEMON_SOCKET_LOCK sudo rm -vf "/tmp/openthread.lock" build_dependencies # We will be creating a lot of log information # Rotate logs so we have a clean and empty set of logs uncluttered with other stuff if [[ -f /etc/logrotate.conf ]]; then sudo logrotate -f /etc/logrotate.conf || true fi # From now on - all exits are TRAPPED # When they occur, we call the function: output_logs'. trap test_teardown EXIT } test_teardown() { # Capture the exit code so we can return it below EXIT_CODE=$? readonly EXIT_CODE write_syslog "EXIT ${EXIT_CODE} - output logs" sudo pkill -f "${OTBR_AGENT}" || true sudo pkill -f "${OTBR_WEB}" || true sudo pkill -f "${OT_COMMISSIONER_CLI}" || true sudo pkill -f "${OT_CLI}" || true wait if [[ ${NO_CLEAN} != 1 ]]; then echo 'clearing all' sudo rm /etc/dbus-1/system.d/otbr-agent.conf || true sudo rm -rf "${STAGE_DIR}" || true sudo rm -rf "${BUILD_DIR}" || true output_logs fi echo "EXIT ${EXIT_CODE}: MESSAGE: ${exit_message}" exit ${EXIT_CODE} } ba_start() { exists_or_die "${OTBR_DBUS_CONF}" sudo cp "${OTBR_DBUS_CONF}" /etc/dbus-1/system.d write_syslog "AGENT: kill old" sudo killall "${OTBR_AGENT}" || true sleep 5 write_syslog "AGENT: starting" # we launch this in the background ( set -e set -x cd "${ORIGIN_PWD}" # check version sudo "${OTBR_AGENT_PATH}" -V # check invalid arguments sudo "${OTBR_AGENT_PATH}" -x && exit $? [[ ! -d tmp ]] || sudo rm -rf tmp sudo "${OTBR_AGENT_PATH}" -I "${TUN_NAME}" -v -d 6 "spinel+hdlc+forkpty://${ot_rcp}?forkpty-arg=${LEADER_NODE_ID}" & ) # wait for it to complete sleep 10 pidof ${OTBR_AGENT} || die "AGENT: failed to start" write_syslog "AGENT: start complete" } web_start() { write_syslog "WEB: kill old" sudo killall "${OTBR_WEB}" || true write_syslog "WEB: starting" ( set -e set -x cd "${ORIGIN_PWD}" sudo "${OTBR_WEB_PATH}" -I "${TUN_NAME}" -p "${OTBR_WEB_PORT}" -a "${OTBR_WEB_HOST}" & ) sleep 15 pidof ${OTBR_WEB} || die "WEB: failed to start" write_syslog "WEB: start complete" } network_form() { OT_PANID="$(random_panid)" readonly OT_PANID OT_XPANID="$(random_xpanid)" readonly OT_XPANID OT_NETWORK_KEY="$(random_networkkey)" readonly OT_NETWORK_KEY OT_CHANNEL="$(random_channel)" readonly OT_CHANNEL curl --header "Content-Type: application/json" --request POST --data "{\"networkKey\":\"${OT_NETWORK_KEY}\",\"prefix\":\"fd11:22::\",\"defaultRoute\":true,\"extPanId\":\"${OT_XPANID}\",\"panId\":\"${OT_PANID}\",\"passphrase\":\"${OT_AGENT_PASSPHRASE}\",\"channel\":${OT_CHANNEL},\"networkName\":\"${OT_NETWORK_NAME}\"}" "${OTBR_WEB_URL}"/form_network | grep "success" || die "WEB: form failed" sleep 15 # verify mDNS is working as expected. local mdns_result="${TEST_BASE}"/mdns_result.log avahi-browse -art | tee "${mdns_result}" OT_BORDER_AGENT_PORT=$(grep -GA3 '^=.\+'"${OT_SERVICE_INSTANCE}"'.\+_meshcop._udp' "${mdns_result}" | head -n4 | grep port | grep -ao '[0-9]\{5\}') rm "${mdns_result}" } ot_commissioner_build() { if [[ -x ${OT_COMMISSIONER_PATH} ]]; then return 0 fi (mkdir -p "${BUILD_DIR}/ot-commissioner" \ && cd "${BUILD_DIR}/ot-commissioner" \ && (git --git-dir=.git rev-parse --is-inside-work-tree || git --git-dir=.git init .) \ && git fetch --depth 1 https://github.com/openthread/ot-commissioner.git main \ && git checkout FETCH_HEAD \ && ./script/bootstrap.sh \ && mkdir build && cd build \ && cmake -GNinja -DCMAKE_BUILD_TYPE=Release .. \ && ninja) } ot_commissioner_start() { write_syslog "COMMISSIONER: kill old" sudo killall "${OT_COMMISSIONER_CLI}" || true OT_PSKC="$("${OTBR_PSKC_PATH}" "${OT_AGENT_PASSPHRASE}" "${OT_XPANID}" "${OT_NETWORK_NAME}")" readonly OT_PSKC OT_COMMISSIONER_LOG="${TEST_BASE}"/commissioner.log readonly OT_COMMISSIONER_LOG local commissioner_config_file="${TEST_BASE}"/ot-commissioner.json local commissioner_config_file="${TEST_BASE}"/ot-commissioner.json sed "s/3aa55f91ca47d1e4e71a08cb35e91591/${OT_PSKC}/g" "${OT_COMMISSIONER_CONFIG}" >"${commissioner_config_file}" expect -f- <