1#!/usr/bin/env python3
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
29import wpan
30from wpan import verify
31
32# -----------------------------------------------------------------------------------------------------------------------
33# Test description: Router table
34#
35# Verify router table entries on a network with 4 routers:
36# {r1, r2, r3} forming a loop with r4 connecting to r3.
37#
38
39test_name = __file__[:-3] if __file__.endswith('.py') else __file__
40print('-' * 120)
41print('Starting \'{}\''.format(test_name))
42
43# -----------------------------------------------------------------------------------------------------------------------
44# Creating `wpan.Nodes` instances
45
46speedup = 4
47wpan.Node.set_time_speedup_factor(speedup)
48
49r1 = wpan.Node()
50r2 = wpan.Node()
51r3 = wpan.Node()
52r4 = wpan.Node()
53c4 = wpan.Node()
54
55# -----------------------------------------------------------------------------------------------------------------------
56# Init all nodes
57
58wpan.Node.init_all_nodes()
59
60# -----------------------------------------------------------------------------------------------------------------------
61# Build network topology
62#
63#
64#     r1 ------ r2
65#      \        /
66#       \      /
67#        \    /
68#          r3 _____ r4
69#
70#
71
72r1.form("route-table")
73
74r1.allowlist_node(r2)
75r2.allowlist_node(r1)
76r2.join_node(r1, wpan.JOIN_TYPE_ROUTER)
77
78r2.allowlist_node(r3)
79r3.allowlist_node(r2)
80r3.join_node(r2, wpan.JOIN_TYPE_ROUTER)
81
82r3.allowlist_node(r1)
83r1.allowlist_node(r3)
84
85r3.allowlist_node(r4)
86r4.allowlist_node(r3)
87r4.join_node(r3, wpan.JOIN_TYPE_ROUTER)
88
89# c4 is attached to r4 so that it quickly gets promoted to a router role.
90c4.allowlist_node(r4)
91r4.allowlist_node(c4)
92c4.join_node(r4, wpan.JOIN_TYPE_SLEEPY_END_DEVICE)
93c4.set(wpan.WPAN_POLL_INTERVAL, '2000')
94
95# -----------------------------------------------------------------------------------------------------------------------
96# Test implementation
97#
98
99verify(r1.get(wpan.WPAN_NODE_TYPE) == wpan.NODE_TYPE_LEADER)
100verify(r2.get(wpan.WPAN_NODE_TYPE) == wpan.NODE_TYPE_ROUTER)
101verify(r3.get(wpan.WPAN_NODE_TYPE) == wpan.NODE_TYPE_ROUTER)
102verify(r4.get(wpan.WPAN_NODE_TYPE) == wpan.NODE_TYPE_ROUTER)
103
104r1_id = int(r1.get(wpan.WPAN_THREAD_ROUTER_ID), 0)
105r2_id = int(r2.get(wpan.WPAN_THREAD_ROUTER_ID), 0)
106r3_id = int(r3.get(wpan.WPAN_THREAD_ROUTER_ID), 0)
107r4_id = int(r4.get(wpan.WPAN_THREAD_ROUTER_ID), 0)
108
109r1_ext_addr = r1.get(wpan.WPAN_EXT_ADDRESS)[1:-1]
110r2_ext_addr = r2.get(wpan.WPAN_EXT_ADDRESS)[1:-1]
111r3_ext_addr = r3.get(wpan.WPAN_EXT_ADDRESS)[1:-1]
112r4_ext_addr = r4.get(wpan.WPAN_EXT_ADDRESS)[1:-1]
113
114r1_rloc = int(r1.get(wpan.WPAN_THREAD_RLOC16), 16)
115r2_rloc = int(r2.get(wpan.WPAN_THREAD_RLOC16), 16)
116r3_rloc = int(r3.get(wpan.WPAN_THREAD_RLOC16), 16)
117r4_rloc = int(r4.get(wpan.WPAN_THREAD_RLOC16), 16)
118
119WAIT_TIME = 30 / speedup + 5
120
121INVALID_ROUTER_ID = 63
122
123
124def check_r1_router_table():
125    router_table = wpan.parse_router_table_result(r1.get(wpan.WPAN_THREAD_ROUTER_TABLE))
126    verify(len(router_table) == 4)
127    for entry in router_table:
128        if entry.rloc16 == r1_rloc:
129            pass
130        elif entry.rloc16 == r2_rloc:
131            # r1 should be directly connected to r2.
132            verify(entry.is_link_established())
133            verify(entry.ext_address == r2_ext_addr)
134        elif entry.rloc16 == r3_rloc:
135            # r1 should be directly connected to r3.
136            verify(entry.is_link_established())
137            verify(entry.ext_address == r3_ext_addr)
138        elif entry.rloc16 == r4_rloc:
139            # r1's next hop towards r4 should be through r3.
140            verify(not entry.is_link_established())
141            verify(entry.next_hop == r3_id)
142        else:
143            raise (wpan.VerifyError("unknown entry in the router table of r1"))
144
145
146wpan.verify_within(check_r1_router_table, WAIT_TIME)
147
148
149def check_r3_router_table():
150    router_table = wpan.parse_router_table_result(r3.get(wpan.WPAN_THREAD_ROUTER_TABLE))
151    verify(len(router_table) == 4)
152    for entry in router_table:
153        if entry.rloc16 == r1_rloc:
154            # r3 should be directly connected to r1.
155            verify(entry.is_link_established())
156            verify(entry.ext_address == r1_ext_addr)
157        elif entry.rloc16 == r2_rloc:
158            # r3 should be directly connected to r2.
159            verify(entry.is_link_established())
160            verify(entry.ext_address == r2_ext_addr)
161        elif entry.rloc16 == r3_rloc:
162            pass
163        elif entry.rloc16 == r4_rloc:
164            # r3 should be directly connected to r4.
165            verify(entry.is_link_established())
166            verify(entry.ext_address == r4_ext_addr)
167        else:
168            raise (wpan.VerifyError("unknown entry in the router table of r3"))
169
170
171wpan.verify_within(check_r3_router_table, WAIT_TIME)
172
173
174def check_r4_router_table():
175    router_table = wpan.parse_router_table_result(r4.get(wpan.WPAN_THREAD_ROUTER_TABLE))
176    verify(len(router_table) == 4)
177    for entry in router_table:
178        if entry.rloc16 == r1_rloc:
179            # r4's next hop towards r1 should be through r3.
180            verify(not entry.is_link_established())
181            verify(entry.next_hop == r3_id)
182        elif entry.rloc16 == r2_rloc:
183            # r4's next hop towards r2 should be through r3.
184            verify(not entry.is_link_established())
185            verify(entry.next_hop == r3_id)
186        elif entry.rloc16 == r3_rloc:
187            # r4 should be directly connected to r3.
188            verify(entry.is_link_established())
189            verify(entry.ext_address == r3_ext_addr)
190        elif entry.rloc16 == r4_rloc:
191            pass
192        else:
193            raise (wpan.VerifyError("unknown entry in the router table of r4"))
194
195
196wpan.verify_within(check_r4_router_table, WAIT_TIME)
197
198# -----------------------------------------------------------------------------------------------------------------------
199# Test finished
200
201wpan.Node.finalize_all_nodes()
202
203print('\'{}\' passed.'.format(test_name))
204