1#!/usr/bin/env python3
2#
3#  Copyright (c) 2017, 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
30import re
31import unittest
32
33import config
34import thread_cert
35
36LEADER = 1
37ROUTER1 = 2
38ROUTER2 = 3
39
40SRV_0_ID = 0
41SRV_0_ENT_NUMBER = '123'
42SRV_0_SERVICE_DATA = '123'
43SRV_0_SERVER_DATA = 'abc'
44
45SRV_1_ID = 1
46SRV_1_ENT_NUMBER = '234'
47SRV_1_SERVICE_DATA = '456'
48SRV_1_SERVER_DATA = 'def'
49
50
51class Test_Service(thread_cert.TestCase):
52    SUPPORT_NCP = False
53
54    TOPOLOGY = {
55        LEADER: {
56            'channel': 12,
57            'mode': 'rdn',
58            'network_name': 'OpenThread',
59            'allowlist': [ROUTER1, ROUTER2]
60        },
61        ROUTER1: {
62            'channel': 12,
63            'mode': 'rdn',
64            'network_name': 'OpenThread',
65            'allowlist': [LEADER, ROUTER2]
66        },
67        ROUTER2: {
68            'channel': 12,
69            'mode': 'rdn',
70            'network_name': 'OpenThread',
71            'allowlist': [LEADER, ROUTER1]
72        },
73    }
74
75    def hasAloc(self, node_id, service_id):
76        for addr in self.nodes[node_id].get_ip6_address(config.ADDRESS_TYPE.ALOC):
77            m = re.match('.*:fc(..)$', addr, re.I)
78            if m is not None:
79                if m.group(1) == str(service_id + 10):  # for service_id=3 look for '...:fc13'
80                    return True
81
82        return False
83
84    def pingFromAll(self, addr):
85        for n in list(self.nodes.values()):
86            self.assertTrue(n.ping(addr))
87
88    def failToPingFromAll(self, addr):
89        for n in list(self.nodes.values()):
90            self.assertFalse(n.ping(addr, timeout=3))
91
92    def test(self):
93        self.nodes[LEADER].start()
94        self.simulator.go(config.LEADER_STARTUP_DELAY)
95        self.assertEqual(self.nodes[LEADER].get_state(), 'leader')
96
97        self.nodes[ROUTER1].start()
98        self.nodes[ROUTER2].start()
99        self.simulator.go(config.ROUTER_STARTUP_DELAY)
100        self.assertEqual(self.nodes[ROUTER1].get_state(), 'router')
101        self.assertEqual(self.nodes[ROUTER2].get_state(), 'router')
102
103        self.assertEqual(self.hasAloc(LEADER, SRV_0_ID), False)
104        self.assertEqual(self.hasAloc(LEADER, SRV_1_ID), False)
105        self.assertEqual(self.hasAloc(ROUTER1, SRV_0_ID), False)
106        self.assertEqual(self.hasAloc(ROUTER1, SRV_1_ID), False)
107        self.assertEqual(self.hasAloc(ROUTER2, SRV_0_ID), False)
108        self.assertEqual(self.hasAloc(ROUTER2, SRV_1_ID), False)
109
110        self.nodes[ROUTER1].add_service(SRV_0_ENT_NUMBER, SRV_0_SERVICE_DATA, SRV_0_SERVER_DATA)
111        self.nodes[ROUTER1].register_netdata()
112        self.simulator.go(2)
113
114        self.assertEqual(self.hasAloc(LEADER, SRV_0_ID), False)
115        self.assertEqual(self.hasAloc(LEADER, SRV_1_ID), False)
116        self.assertEqual(self.hasAloc(ROUTER1, SRV_0_ID), True)
117        self.assertEqual(self.hasAloc(ROUTER1, SRV_1_ID), False)
118        self.assertEqual(self.hasAloc(ROUTER2, SRV_0_ID), False)
119        self.assertEqual(self.hasAloc(ROUTER2, SRV_1_ID), False)
120
121        aloc0 = self.nodes[ROUTER1].get_ip6_address(config.ADDRESS_TYPE.ALOC)[0]
122        self.pingFromAll(aloc0)
123
124        self.nodes[LEADER].add_service(SRV_0_ENT_NUMBER, SRV_0_SERVICE_DATA, SRV_0_SERVER_DATA)
125        self.nodes[LEADER].register_netdata()
126        self.simulator.go(2)
127
128        self.assertEqual(self.hasAloc(LEADER, SRV_0_ID), True)
129        self.assertEqual(self.hasAloc(LEADER, SRV_1_ID), False)
130        self.assertEqual(self.hasAloc(ROUTER1, SRV_0_ID), True)
131        self.assertEqual(self.hasAloc(ROUTER1, SRV_1_ID), False)
132        self.assertEqual(self.hasAloc(ROUTER2, SRV_0_ID), False)
133        self.assertEqual(self.hasAloc(ROUTER2, SRV_1_ID), False)
134
135        self.pingFromAll(aloc0)
136
137        self.nodes[ROUTER2].add_service(SRV_1_ENT_NUMBER, SRV_1_SERVICE_DATA, SRV_1_SERVER_DATA)
138        self.nodes[ROUTER2].register_netdata()
139        self.simulator.go(2)
140
141        self.assertEqual(self.hasAloc(LEADER, SRV_0_ID), True)
142        self.assertEqual(self.hasAloc(LEADER, SRV_1_ID), False)
143        self.assertEqual(self.hasAloc(ROUTER1, SRV_0_ID), True)
144        self.assertEqual(self.hasAloc(ROUTER1, SRV_1_ID), False)
145        self.assertEqual(self.hasAloc(ROUTER2, SRV_0_ID), False)
146        self.assertEqual(self.hasAloc(ROUTER2, SRV_1_ID), True)
147
148        aloc1 = self.nodes[ROUTER2].get_ip6_address(config.ADDRESS_TYPE.ALOC)[0]
149        self.pingFromAll(aloc0)
150        self.pingFromAll(aloc1)
151
152        self.nodes[ROUTER1].remove_service(SRV_0_ENT_NUMBER, SRV_0_SERVICE_DATA)
153        self.nodes[ROUTER1].register_netdata()
154        self.simulator.go(2)
155
156        self.assertEqual(self.hasAloc(LEADER, SRV_0_ID), True)
157        self.assertEqual(self.hasAloc(LEADER, SRV_1_ID), False)
158        self.assertEqual(self.hasAloc(ROUTER1, SRV_0_ID), False)
159        self.assertEqual(self.hasAloc(ROUTER1, SRV_1_ID), False)
160        self.assertEqual(self.hasAloc(ROUTER2, SRV_0_ID), False)
161        self.assertEqual(self.hasAloc(ROUTER2, SRV_1_ID), True)
162
163        self.pingFromAll(aloc0)
164        self.pingFromAll(aloc1)
165
166        self.nodes[LEADER].remove_service(SRV_0_ENT_NUMBER, SRV_0_SERVICE_DATA)
167        self.nodes[LEADER].register_netdata()
168        self.simulator.go(2)
169
170        self.assertEqual(self.hasAloc(LEADER, SRV_0_ID), False)
171        self.assertEqual(self.hasAloc(LEADER, SRV_1_ID), False)
172        self.assertEqual(self.hasAloc(ROUTER1, SRV_0_ID), False)
173        self.assertEqual(self.hasAloc(ROUTER1, SRV_1_ID), False)
174        self.assertEqual(self.hasAloc(ROUTER2, SRV_0_ID), False)
175        self.assertEqual(self.hasAloc(ROUTER2, SRV_1_ID), True)
176
177        self.failToPingFromAll(aloc0)
178        self.pingFromAll(aloc1)
179
180        self.nodes[ROUTER2].remove_service(SRV_1_ENT_NUMBER, SRV_1_SERVICE_DATA)
181        self.nodes[ROUTER2].register_netdata()
182        self.simulator.go(2)
183
184        self.assertEqual(self.hasAloc(LEADER, SRV_0_ID), False)
185        self.assertEqual(self.hasAloc(LEADER, SRV_1_ID), False)
186        self.assertEqual(self.hasAloc(ROUTER1, SRV_0_ID), False)
187        self.assertEqual(self.hasAloc(ROUTER1, SRV_1_ID), False)
188        self.assertEqual(self.hasAloc(ROUTER2, SRV_0_ID), False)
189        self.assertEqual(self.hasAloc(ROUTER2, SRV_1_ID), False)
190
191        self.failToPingFromAll(aloc0)
192        self.failToPingFromAll(aloc1)
193
194
195if __name__ == '__main__':
196    unittest.main()
197