1#!/bin/bash
2#
3#  Copyright (c) 2018, The OpenThread Authors.
4#  All rights reserved.
5#
6#  Redistribution and use in source and binary forms, with or without
7#  modification, are permitted provided that the following conditions are met:
8#  1. Redistributions of source code must retain the above copyright
9#     notice, this list of conditions and the following disclaimer.
10#  2. Redistributions in binary form must reproduce the above copyright
11#     notice, this list of conditions and the following disclaimer in the
12#     documentation and/or other materials provided with the distribution.
13#  3. Neither the name of the copyright holder nor the
14#     names of its contributors may be used to endorse or promote products
15#     derived from this software without specific prior written permission.
16#
17#  THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
18#  AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
19#  IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
20#  ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
21#  LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
22#  CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
23#  SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
24#  INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
25#  CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
26#  ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
27#  POSSIBILITY OF SUCH DAMAGE.
28#
29
30set -e
31set -x
32
33die()
34{
35    echo " *** ERROR: " "$*"
36    exit 1
37}
38
39at_exit()
40{
41    EXIT_CODE=$?
42
43    sudo killall expect || true
44    sudo killall ot-ctl || true
45    sudo killall ot-daemon || true
46    sudo killall ot-cli || true
47    sudo killall ot-rcp || true
48    sudo killall socat || true
49
50    exit $EXIT_CODE
51}
52
53wait_for_socat()
54{
55    if [[ "$(head -n2 "$SOCAT_OUTPUT" | wc -l | tr -d ' ')" == 2 ]]; then
56        RADIO_PTY=$(head -n1 "$SOCAT_OUTPUT" | grep -o '/dev/.\+')
57        CORE_PTY=$(head -n2 "$SOCAT_OUTPUT" | tail -n1 | grep -o '/dev/.\+')
58        return 0
59    else
60        echo 'Still waiting for socat'
61    fi
62    return 1
63}
64
65wait_for_leader()
66{
67    if grep -q leader "$OT_OUTPUT"; then
68        return 0
69    else
70        echo 'Still waiting for leader'
71    fi
72    return 1
73}
74
75timeout_run()
76{
77    local count="$1"
78    local exit_code
79    shift 1
80
81    while [[ $count != 0 && $exit_code != 0 ]]; do
82        count=$((count - 1))
83        "$@" && return 0 || exit_code=$?
84        sleep 1
85    done
86
87    return $exit_code
88}
89
90do_build()
91{
92    ./script/cmake-build simulation
93    ./script/cmake-build posix -DOT_PLATFORM_NETIF=1 -DOT_PLATFORM_UDP=1 -DOT_UDP_FORWARD=0 -DOT_POSIX_MAX_POWER_TABLE=1 -DOT_DAEMON="${OT_DAEMON}" -DOT_READLINE="${OT_READLINE}"
94}
95
96do_check()
97{
98    trap at_exit INT TERM EXIT
99
100    sudo rm -rf tmp
101
102    SOCAT_OUTPUT=/tmp/ot-socat
103    OT_OUTPUT=/tmp/ot-output
104    socat -d -d pty,raw,echo=0 pty,raw,echo=0 >/dev/null 2>$SOCAT_OUTPUT &
105    timeout_run 10 wait_for_socat
106    echo 'RADIO_PTY' "$RADIO_PTY"
107    echo 'CORE_PTY' "$CORE_PTY"
108
109    RADIO_NCP_PATH="$PWD/build/simulation/examples/apps/ncp/ot-rcp"
110
111    # shellcheck disable=SC2094
112    $RADIO_NCP_PATH 1 >"$RADIO_PTY" <"$RADIO_PTY" &
113
114    # Cover setting a valid network interface name.
115    VALID_NETIF_NAME="wan$(date +%H%M%S)"
116    readonly VALID_NETIF_NAME
117
118    RADIO_URL="spinel+hdlc+uart://${CORE_PTY}?region=US&max-power-table=11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26"
119
120    if [[ ${OT_DAEMON} == 'on' ]]; then
121        sudo -E "$PWD/build/posix/src/posix/ot-daemon" -d7 -v -I "${VALID_NETIF_NAME}" "${RADIO_URL}" 2>&1 | tee "${OT_OUTPUT}" &
122        sleep 3
123        # macOS cannot explicitly set network interface name
124        NETIF_NAME=$(grep -o 'Thread interface: .\+' "${OT_OUTPUT}" | cut -d: -f2 | tr -d ' \r\n')
125        OT_CTL_PATH="$PWD/build/posix/src/posix/ot-ctl"
126        if [[ ${OT_DAEMON_ALLOW_ALL} == 1 ]]; then
127            OT_CTL=("${OT_CTL_PATH}")
128        else
129            OT_CTL=(sudo "${OT_CTL_PATH}")
130        fi
131        "${OT_CTL[@]}" -I "${NETIF_NAME}" panid 0xface | grep 'Done' || die 'failed to set panid with ot-ctl'
132
133        # verify supports options in OpenThread commands without separator --
134        "${OT_CTL[@]}" -I "${NETIF_NAME}" pskc -p 123456 | grep 'Done' || die 'unable to set pskc'
135
136        # verify this reset and factoryreset end immediately
137        "${OT_CTL[@]}" -I "${NETIF_NAME}" reset
138        # sleep a while for daemon ready
139        sleep 2
140        "${OT_CTL[@]}" -I "${NETIF_NAME}" factoryreset
141        # sleep a while for daemon ready
142        sleep 2
143
144        OPENTHREAD_CONFIG_CLI_MAX_LINE_LENGTH=640
145        readonly OPENTHREAD_CONFIG_CLI_MAX_LINE_LENGTH
146        local -r kMaxStringLength="$((OPENTHREAD_CONFIG_CLI_MAX_LINE_LENGTH - 1))"
147
148        # verify success if command length doesn't exceed the limit
149        for len in $(seq 1 ${kMaxStringLength}); do
150            "${OT_CTL[@]}" -I "${NETIF_NAME}" "$(printf '1%.0s' $(seq 1 "${len}"))"
151        done
152
153        # verify failure if command length exceeds the limit
154        len=${OPENTHREAD_CONFIG_CLI_MAX_LINE_LENGTH}
155        if "${OT_CTL[@]}" -I "${NETIF_NAME}" "$(printf '1%.0s' $(seq 1 "${len}"))"; then
156            die
157        fi
158        OT_CLI_CMD="${OT_CTL[*]} -I ${NETIF_NAME}"
159    else
160        OT_CLI="$PWD/build/posix/src/posix/ot-cli"
161        sudo "${OT_CLI}" -I "${VALID_NETIF_NAME}" -n "${RADIO_URL}"
162
163        # Cover setting a too long(max is 15 characters) network interface name.
164        # Expect exit code to be 2(OT_EXIT_INVALID_ARGUMENTS).
165        INVALID_NETIF_NAME="wan0123456789123"
166        readonly INVALID_NETIF_NAME
167        sudo "${OT_CLI}" -I "${INVALID_NETIF_NAME}" -n "${RADIO_URL}" || test $? = 2
168
169        OT_CLI_CMD="$PWD/build/posix/src/posix/ot-cli ${RADIO_URL}"
170    fi
171
172    sudo expect <<EOF | tee "${OT_OUTPUT}" &
173spawn ${OT_CLI_CMD}
174expect_after {
175    timeout { error }
176}
177send "region\r\n"
178expect "US"
179expect "Done"
180send "panid 0xface\r\n"
181expect "Done"
182send "routerselectionjitter 1\r\n"
183expect "Done"
184send "ifconfig up\r\n"
185expect "Done"
186send "thread start\r\n"
187expect "Done"
188sleep 10
189send "state\r\n"
190expect "leader"
191expect "Done"
192send "extaddr\r\n"
193expect "Done"
194send "dataset active\r\n"
195expect "Done"
196send "ipaddr\r\n"
197expect "Done"
198send "coex\r\n"
199expect "Done"
200send "coap start\r\n"
201expect "Done"
202send "coap resource TestResource\r\n"
203expect "Done"
204send "coap set TestContent\r\n"
205expect "Done"
206set timeout -1
207expect eof
208EOF
209
210    sleep 5
211
212    # wait until the node becomes leader
213    timeout_run 10 wait_for_leader
214
215    # wait coap service start
216    sleep 5
217
218    netstat -an | grep -q 5683 || die 'Application CoAP port is not available!'
219
220    extaddr=$(grep -ao -A +1 'extaddr' $OT_OUTPUT | tail -n1 | tr -d '\r\n\0')
221    echo "Extended address is: ${extaddr}"
222
223    prefix=$(grep -ao 'Mesh Local Prefix: [0-9a-f:]\+' $OT_OUTPUT | cut -d: -f2- | tr -d ' \r\n')
224    LEADER_ALOC="${prefix}ff:fe00:fc00"
225
226    # skip testing CoAP for https://github.com/openthread/openthread/issues/6363
227    [[ $OSTYPE == "linux-gnu"* ]] || return 0
228
229    if [[ ${OT_DAEMON} == 'on' ]]; then
230        sudo killall -9 expect || true
231        sudo killall -9 ot-ctl || true
232        NETIF_INDEX=$(ip link show "${NETIF_NAME}" | cut -f 1 -d ":" | head -n 1)
233        sudo PATH="$(dirname "${OT_CTL_PATH}"):${PATH}" \
234            python3 "$PWD/tests/scripts/misc/test_multicast_join.py" "${NETIF_INDEX}" "${NETIF_NAME}" \
235            || die 'multicast group join failed'
236    fi
237
238    # Retrievie test resource through application CoAP
239    coap_response=$(coap-client -B 5 -m GET "coap://[${LEADER_ALOC}]:5683/TestResource")
240    echo "CoAP response is: ${coap_response}"
241
242    # Verify CoAP response contains the test content
243    if [[ ${coap_response} == *TestContent* ]]; then
244        echo 'Success'
245    else
246        die 'Failed to access application CoAP'
247    fi
248
249    # Retrievie extended address through network diagnostic get
250    coap_response=$(echo -n '120100' | xxd -r -p | coap-client -B 5 -m POST "coap://[${LEADER_ALOC}]:61631/d/dg" -f-)
251
252    # Verify Tmf CoAP is blocked
253    if [[ -z ${coap_response} ]]; then
254        die 'Tmf is not blocked'
255    fi
256}
257
258main()
259{
260    if [[ $# == 0 ]]; then
261        do_build
262        do_check
263        return 0
264    fi
265
266    while [[ $# != 0 ]]; do
267        case $1 in
268            build)
269                do_build
270                ;;
271            check)
272                do_check
273                ;;
274            *)
275                echo "Unknown action: $1"
276                return 1
277                ;;
278        esac
279        shift
280    done
281}
282
283main "$@"
284