mirror of
https://github.com/espressif/openthread.git
synced 2026-08-19 08:59:52 +00:00
Add sniffer. Extend some tests according to Thread Certification Test Plan V1.1 Preview 1612. (#957)
This commit is contained in:
@@ -86,6 +86,19 @@ cd /tmp || die
|
||||
[ $BUILD_TARGET != posix-32-bit ] || {
|
||||
sudo apt-get install g++-multilib || die
|
||||
}
|
||||
|
||||
[ $BUILD_TARGET != posix-distcheck ] || {
|
||||
sudo apt-get install clang || die
|
||||
}
|
||||
|
||||
[ $BUILD_TARGET != posix -o $CC != clang ] || {
|
||||
sudo apt-get install clang || die
|
||||
}
|
||||
|
||||
# Packages used by sniffer
|
||||
sudo -H pip install pycryptodome==3.4.3 || die
|
||||
pip install pycryptodome==3.4.3 || die
|
||||
|
||||
}
|
||||
|
||||
[ $TRAVIS_OS_NAME != osx ] || {
|
||||
|
||||
@@ -121,7 +121,17 @@ EXTRA_DIST = \
|
||||
thread-cert/Cert_9_2_16_ActivePendingPartition.py \
|
||||
thread-cert/Cert_9_2_17_Orphan.py \
|
||||
thread-cert/Cert_9_2_18_RollBackActiveTimestamp.py \
|
||||
thread-cert/common.py \
|
||||
thread-cert/config.py \
|
||||
thread-cert/ipv6.py \
|
||||
thread-cert/lowpan.py \
|
||||
thread-cert/mac802154.py \
|
||||
thread-cert/message.py \
|
||||
thread-cert/mle.py \
|
||||
thread-cert/net_crypto.py \
|
||||
thread-cert/network_data.py \
|
||||
thread-cert/node.py \
|
||||
thread-cert/sniffer.py \
|
||||
$(NULL)
|
||||
|
||||
if OPENTHREAD_BUILD_TESTS
|
||||
|
||||
@@ -30,15 +30,20 @@
|
||||
import time
|
||||
import unittest
|
||||
|
||||
import config
|
||||
import mle
|
||||
import node
|
||||
|
||||
LEADER = 1
|
||||
ROUTER = 2
|
||||
SNIFFER = 3
|
||||
|
||||
|
||||
class Cert_5_1_01_RouterAttach(unittest.TestCase):
|
||||
|
||||
def setUp(self):
|
||||
self.nodes = {}
|
||||
for i in range(1,3):
|
||||
for i in range(1, 3):
|
||||
self.nodes[i] = node.Node(i)
|
||||
|
||||
self.nodes[LEADER].set_panid(0xface)
|
||||
@@ -52,11 +57,16 @@ class Cert_5_1_01_RouterAttach(unittest.TestCase):
|
||||
self.nodes[ROUTER].enable_whitelist()
|
||||
self.nodes[ROUTER].set_router_selection_jitter(1)
|
||||
|
||||
self.sniffer = config.create_default_thread_sniffer(SNIFFER)
|
||||
self.sniffer.start()
|
||||
|
||||
def tearDown(self):
|
||||
for node in list(self.nodes.values()):
|
||||
node.stop()
|
||||
del self.nodes
|
||||
|
||||
self.sniffer.stop()
|
||||
|
||||
def test(self):
|
||||
self.nodes[LEADER].start()
|
||||
self.nodes[LEADER].set_state('leader')
|
||||
@@ -66,5 +76,100 @@ class Cert_5_1_01_RouterAttach(unittest.TestCase):
|
||||
time.sleep(7)
|
||||
self.assertEqual(self.nodes[ROUTER].get_state(), 'router')
|
||||
|
||||
leader_messages = self.sniffer.get_messages_sent_by(LEADER)
|
||||
router_messages = self.sniffer.get_messages_sent_by(ROUTER)
|
||||
|
||||
# 1 - Leader
|
||||
msg = leader_messages.next_mle_message(mle.CommandType.ADVERTISEMENT)
|
||||
msg.assertSentWithHopLimit(255)
|
||||
msg.assertSentToDestinationAddress("ff02::1")
|
||||
msg.assertMleMessageContainsTlv(mle.SourceAddress)
|
||||
msg.assertMleMessageContainsTlv(mle.LeaderData)
|
||||
msg.assertMleMessageContainsTlv(mle.Route64)
|
||||
|
||||
# 2 - Router
|
||||
msg = router_messages.next_mle_message(mle.CommandType.PARENT_REQUEST)
|
||||
msg.assertSentWithHopLimit(255)
|
||||
msg.assertSentToDestinationAddress("ff02::2")
|
||||
msg.assertMleMessageContainsTlv(mle.Mode)
|
||||
msg.assertMleMessageContainsTlv(mle.Challenge)
|
||||
msg.assertMleMessageContainsTlv(mle.ScanMask)
|
||||
msg.assertMleMessageContainsTlv(mle.Version)
|
||||
|
||||
scan_mask_tlv = msg.get_mle_message_tlv(mle.ScanMask)
|
||||
self.assertEqual(1, scan_mask_tlv.router)
|
||||
self.assertEqual(0, scan_mask_tlv.end_device)
|
||||
|
||||
# 3 - Leader
|
||||
msg = leader_messages.next_mle_message(mle.CommandType.PARENT_RESPONSE)
|
||||
msg.assertSentToNode(self.nodes[ROUTER])
|
||||
msg.assertMleMessageContainsTlv(mle.SourceAddress)
|
||||
msg.assertMleMessageContainsTlv(mle.LeaderData)
|
||||
msg.assertMleMessageContainsTlv(mle.LinkLayerFrameCounter)
|
||||
msg.assertMleMessageContainsOptionalTlv(mle.MleFrameCounter)
|
||||
msg.assertMleMessageContainsTlv(mle.Response)
|
||||
msg.assertMleMessageContainsTlv(mle.Challenge)
|
||||
msg.assertMleMessageContainsTlv(mle.LinkMargin)
|
||||
msg.assertMleMessageContainsTlv(mle.Connectivity)
|
||||
msg.assertMleMessageContainsTlv(mle.Version)
|
||||
|
||||
# 4 - Router
|
||||
msg = router_messages.next_mle_message(mle.CommandType.CHILD_ID_REQUEST)
|
||||
msg.assertSentToNode(self.nodes[LEADER])
|
||||
msg.assertMleMessageContainsTlv(mle.Response)
|
||||
msg.assertMleMessageContainsTlv(mle.LinkLayerFrameCounter)
|
||||
msg.assertMleMessageContainsOptionalTlv(mle.MleFrameCounter)
|
||||
msg.assertMleMessageContainsTlv(mle.Mode)
|
||||
msg.assertMleMessageContainsTlv(mle.Timeout)
|
||||
msg.assertMleMessageContainsTlv(mle.Version)
|
||||
msg.assertMleMessageContainsTlv(mle.TlvRequest)
|
||||
msg.assertMleMessageDoesNotContainTlv(mle.AddressRegistration)
|
||||
|
||||
# 5 - Leader
|
||||
msg = leader_messages.next_mle_message(mle.CommandType.CHILD_ID_RESPONSE)
|
||||
msg.assertSentToNode(self.nodes[ROUTER])
|
||||
msg.assertMleMessageContainsTlv(mle.SourceAddress)
|
||||
msg.assertMleMessageContainsTlv(mle.LeaderData)
|
||||
msg.assertMleMessageContainsTlv(mle.Address16)
|
||||
msg.assertMleMessageContainsOptionalTlv(mle.NetworkData)
|
||||
msg.assertMleMessageContainsOptionalTlv(mle.Route64)
|
||||
msg.assertMleMessageContainsOptionalTlv(mle.AddressRegistration)
|
||||
|
||||
# 8 - Router
|
||||
msg = router_messages.next_mle_message(mle.CommandType.LINK_REQUEST)
|
||||
msg.assertMleMessageContainsTlv(mle.SourceAddress)
|
||||
msg.assertMleMessageContainsTlv(mle.LeaderData)
|
||||
msg.assertMleMessageContainsTlv(mle.Challenge)
|
||||
msg.assertMleMessageContainsTlv(mle.Version)
|
||||
msg.assertMleMessageContainsTlv(mle.TlvRequest)
|
||||
|
||||
tlv_request = msg.get_mle_message_tlv(mle.TlvRequest)
|
||||
self.assertIn(mle.TlvType.LINK_MARGIN, tlv_request.tlvs)
|
||||
|
||||
# 9 - Leader
|
||||
msg = leader_messages.next_mle_message(mle.CommandType.LINK_ACCEPT_AND_REQUEST)
|
||||
msg.assertMleMessageContainsTlv(mle.SourceAddress)
|
||||
msg.assertMleMessageContainsTlv(mle.LeaderData)
|
||||
msg.assertMleMessageContainsTlv(mle.Response)
|
||||
msg.assertMleMessageContainsTlv(mle.LinkLayerFrameCounter)
|
||||
msg.assertMleMessageContainsTlv(mle.Version)
|
||||
msg.assertMleMessageContainsTlv(mle.LinkMargin)
|
||||
msg.assertMleMessageContainsOptionalTlv(mle.MleFrameCounter)
|
||||
msg.assertMleMessageContainsOptionalTlv(mle.Challenge)
|
||||
|
||||
# 10 - Router
|
||||
msg = router_messages.next_mle_message(mle.CommandType.ADVERTISEMENT)
|
||||
msg.assertSentWithHopLimit(255)
|
||||
msg.assertSentToDestinationAddress("ff02::1")
|
||||
msg.assertMleMessageContainsTlv(mle.SourceAddress)
|
||||
msg.assertMleMessageContainsTlv(mle.LeaderData)
|
||||
msg.assertMleMessageContainsTlv(mle.Route64)
|
||||
|
||||
# 11 - Leader, Router1
|
||||
for addr in self.nodes[LEADER].get_addrs():
|
||||
self.assertTrue(self.nodes[ROUTER].ping(addr))
|
||||
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
unittest.main()
|
||||
|
||||
@@ -30,17 +30,22 @@
|
||||
import time
|
||||
import unittest
|
||||
|
||||
import config
|
||||
import mle
|
||||
import node
|
||||
|
||||
LEADER = 1
|
||||
ROUTER = 2
|
||||
ED = 3
|
||||
SED = 4
|
||||
SNIFFER = 5
|
||||
|
||||
|
||||
class Cert_5_1_02_ChildAddressTimeout(unittest.TestCase):
|
||||
|
||||
def setUp(self):
|
||||
self.nodes = {}
|
||||
for i in range(1,5):
|
||||
for i in range(1, 5):
|
||||
self.nodes[i] = node.Node(i)
|
||||
|
||||
self.nodes[LEADER].set_panid(0xface)
|
||||
@@ -68,7 +73,13 @@ class Cert_5_1_02_ChildAddressTimeout(unittest.TestCase):
|
||||
self.nodes[SED].add_whitelist(self.nodes[ROUTER].get_addr64())
|
||||
self.nodes[SED].enable_whitelist()
|
||||
|
||||
self.sniffer = config.create_default_thread_sniffer(SNIFFER)
|
||||
self.sniffer.start()
|
||||
|
||||
def tearDown(self):
|
||||
self.sniffer.stop()
|
||||
del self.sniffer
|
||||
|
||||
for node in list(self.nodes.values()):
|
||||
node.stop()
|
||||
del self.nodes
|
||||
@@ -105,5 +116,29 @@ class Cert_5_1_02_ChildAddressTimeout(unittest.TestCase):
|
||||
if addr[0:4] != 'fe80':
|
||||
self.assertFalse(self.nodes[LEADER].ping(addr))
|
||||
|
||||
leader_messages = self.sniffer.get_messages_sent_by(LEADER)
|
||||
router1_messages = self.sniffer.get_messages_sent_by(ROUTER)
|
||||
ed_messages = self.sniffer.get_messages_sent_by(ED)
|
||||
sed_messages = self.sniffer.get_messages_sent_by(SED)
|
||||
|
||||
# 1 - All
|
||||
leader_messages.next_mle_message(mle.CommandType.ADVERTISEMENT)
|
||||
router1_messages.next_mle_message(mle.CommandType.PARENT_REQUEST)
|
||||
leader_messages.next_mle_message(mle.CommandType.PARENT_RESPONSE)
|
||||
router1_messages.next_mle_message(mle.CommandType.CHILD_ID_REQUEST)
|
||||
leader_messages.next_mle_message(mle.CommandType.CHILD_ID_RESPONSE)
|
||||
|
||||
router1_messages.next_mle_message(mle.CommandType.ADVERTISEMENT)
|
||||
ed_messages.next_mle_message(mle.CommandType.PARENT_REQUEST)
|
||||
router1_messages.next_mle_message(mle.CommandType.PARENT_RESPONSE)
|
||||
ed_messages.next_mle_message(mle.CommandType.CHILD_ID_REQUEST)
|
||||
router1_messages.next_mle_message(mle.CommandType.CHILD_ID_RESPONSE)
|
||||
|
||||
sed_messages.next_mle_message(mle.CommandType.PARENT_REQUEST)
|
||||
router1_messages.next_mle_message(mle.CommandType.PARENT_RESPONSE)
|
||||
sed_messages.next_mle_message(mle.CommandType.CHILD_ID_REQUEST)
|
||||
router1_messages.next_mle_message(mle.CommandType.CHILD_ID_RESPONSE)
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
unittest.main()
|
||||
|
||||
@@ -30,16 +30,21 @@
|
||||
import time
|
||||
import unittest
|
||||
|
||||
import config
|
||||
import mle
|
||||
import node
|
||||
|
||||
LEADER = 1
|
||||
ROUTER1 = 2
|
||||
ROUTER2 = 3
|
||||
SNIFFER = 4
|
||||
|
||||
|
||||
class Cert_5_1_03_RouterAddressReallocation(unittest.TestCase):
|
||||
|
||||
def setUp(self):
|
||||
self.nodes = {}
|
||||
for i in range(1,4):
|
||||
for i in range(1, 4):
|
||||
self.nodes[i] = node.Node(i)
|
||||
|
||||
self.nodes[LEADER].set_panid(0xface)
|
||||
@@ -62,7 +67,13 @@ class Cert_5_1_03_RouterAddressReallocation(unittest.TestCase):
|
||||
self.nodes[ROUTER2].enable_whitelist()
|
||||
self.nodes[ROUTER2].set_router_selection_jitter(1)
|
||||
|
||||
self.sniffer = config.create_default_thread_sniffer(SNIFFER)
|
||||
self.sniffer.start()
|
||||
|
||||
def tearDown(self):
|
||||
self.sniffer.stop()
|
||||
del self.sniffer
|
||||
|
||||
for node in list(self.nodes.values()):
|
||||
node.stop()
|
||||
del self.nodes
|
||||
@@ -85,9 +96,68 @@ class Cert_5_1_03_RouterAddressReallocation(unittest.TestCase):
|
||||
self.nodes[ROUTER2].set_network_id_timeout(110)
|
||||
self.nodes[LEADER].stop()
|
||||
time.sleep(130)
|
||||
|
||||
self.assertEqual(self.nodes[ROUTER2].get_state(), 'leader')
|
||||
self.assertEqual(self.nodes[ROUTER1].get_state(), 'router')
|
||||
self.assertEqual(self.nodes[ROUTER1].get_addr16(), rloc16)
|
||||
|
||||
leader_messages = self.sniffer.get_messages_sent_by(LEADER)
|
||||
router1_messages = self.sniffer.get_messages_sent_by(ROUTER1)
|
||||
router2_messages = self.sniffer.get_messages_sent_by(ROUTER2)
|
||||
|
||||
# 2 - All
|
||||
leader_messages.next_mle_message(mle.CommandType.ADVERTISEMENT)
|
||||
|
||||
router1_messages.next_mle_message(mle.CommandType.PARENT_REQUEST)
|
||||
leader_messages.next_mle_message(mle.CommandType.PARENT_RESPONSE)
|
||||
router1_messages.next_mle_message(mle.CommandType.CHILD_ID_REQUEST)
|
||||
leader_messages.next_mle_message(mle.CommandType.CHILD_ID_RESPONSE)
|
||||
|
||||
router2_messages.next_mle_message(mle.CommandType.PARENT_REQUEST)
|
||||
leader_messages.next_mle_message(mle.CommandType.PARENT_RESPONSE)
|
||||
router1_messages.next_mle_message(mle.CommandType.PARENT_RESPONSE)
|
||||
router2_messages.next_mle_message(mle.CommandType.CHILD_ID_REQUEST)
|
||||
|
||||
# 5 - Router1
|
||||
# Router1 make two attempts to reconnect to its current Partition.
|
||||
for _ in xrange(2):
|
||||
msg = router1_messages.next_mle_message(mle.CommandType.PARENT_REQUEST)
|
||||
msg.assertSentWithHopLimit(255)
|
||||
msg.assertSentToDestinationAddress("ff02::2")
|
||||
msg.assertMleMessageContainsTlv(mle.Mode)
|
||||
msg.assertMleMessageContainsTlv(mle.Challenge)
|
||||
msg.assertMleMessageContainsTlv(mle.ScanMask)
|
||||
msg.assertMleMessageContainsTlv(mle.Version)
|
||||
|
||||
scan_mask_tlv = msg.get_mle_message_tlv(mle.ScanMask)
|
||||
self.assertEqual(1, scan_mask_tlv.router)
|
||||
self.assertEqual(1, scan_mask_tlv.end_device)
|
||||
|
||||
# 6 - Router1
|
||||
msg = router1_messages.next_mle_message(mle.CommandType.PARENT_REQUEST)
|
||||
msg.assertSentWithHopLimit(255)
|
||||
msg.assertSentToDestinationAddress("ff02::2")
|
||||
msg.assertMleMessageContainsTlv(mle.Mode)
|
||||
msg.assertMleMessageContainsTlv(mle.Challenge)
|
||||
msg.assertMleMessageContainsTlv(mle.ScanMask)
|
||||
msg.assertMleMessageContainsTlv(mle.Version)
|
||||
|
||||
scan_mask_tlv = msg.get_mle_message_tlv(mle.ScanMask)
|
||||
self.assertEqual(1, scan_mask_tlv.router)
|
||||
self.assertEqual(0, scan_mask_tlv.end_device)
|
||||
|
||||
# 7 - Router1
|
||||
msg = router1_messages.next_mle_message(mle.CommandType.CHILD_ID_REQUEST)
|
||||
msg.assertSentToNode(self.nodes[ROUTER2])
|
||||
msg.assertMleMessageContainsTlv(mle.Response)
|
||||
msg.assertMleMessageContainsTlv(mle.LinkLayerFrameCounter)
|
||||
msg.assertMleMessageContainsOptionalTlv(mle.MleFrameCounter)
|
||||
msg.assertMleMessageContainsTlv(mle.Mode)
|
||||
msg.assertMleMessageContainsTlv(mle.Timeout)
|
||||
msg.assertMleMessageContainsTlv(mle.Version)
|
||||
msg.assertMleMessageContainsTlv(mle.TlvRequest)
|
||||
msg.assertMleMessageDoesNotContainTlv(mle.AddressRegistration)
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
unittest.main()
|
||||
|
||||
@@ -30,16 +30,21 @@
|
||||
import time
|
||||
import unittest
|
||||
|
||||
import config
|
||||
import mle
|
||||
import node
|
||||
|
||||
LEADER = 1
|
||||
ROUTER1 = 2
|
||||
ROUTER2 = 3
|
||||
SNIFFER = 4
|
||||
|
||||
|
||||
class Cert_5_1_04_RouterAddressReallocation(unittest.TestCase):
|
||||
|
||||
def setUp(self):
|
||||
self.nodes = {}
|
||||
for i in range(1,4):
|
||||
for i in range(1, 4):
|
||||
self.nodes[i] = node.Node(i)
|
||||
|
||||
self.nodes[LEADER].set_panid(0xface)
|
||||
@@ -62,7 +67,13 @@ class Cert_5_1_04_RouterAddressReallocation(unittest.TestCase):
|
||||
self.nodes[ROUTER2].enable_whitelist()
|
||||
self.nodes[ROUTER2].set_router_selection_jitter(1)
|
||||
|
||||
self.sniffer = config.create_default_thread_sniffer(SNIFFER)
|
||||
self.sniffer.start()
|
||||
|
||||
def tearDown(self):
|
||||
self.sniffer.stop()
|
||||
del self.sniffer
|
||||
|
||||
for node in list(self.nodes.values()):
|
||||
node.stop()
|
||||
del self.nodes
|
||||
@@ -89,5 +100,77 @@ class Cert_5_1_04_RouterAddressReallocation(unittest.TestCase):
|
||||
self.assertEqual(self.nodes[ROUTER2].get_state(), 'router')
|
||||
self.assertEqual(self.nodes[ROUTER1].get_addr16(), rloc16)
|
||||
|
||||
leader_messages = self.sniffer.get_messages_sent_by(LEADER)
|
||||
router1_messages = self.sniffer.get_messages_sent_by(ROUTER1)
|
||||
router2_messages = self.sniffer.get_messages_sent_by(ROUTER2)
|
||||
|
||||
# 2 - All
|
||||
leader_messages.next_mle_message(mle.CommandType.ADVERTISEMENT)
|
||||
|
||||
router1_messages.next_mle_message(mle.CommandType.PARENT_REQUEST)
|
||||
leader_messages.next_mle_message(mle.CommandType.PARENT_RESPONSE)
|
||||
router1_messages.next_mle_message(mle.CommandType.CHILD_ID_REQUEST)
|
||||
leader_messages.next_mle_message(mle.CommandType.CHILD_ID_RESPONSE)
|
||||
|
||||
router2_messages.next_mle_message(mle.CommandType.PARENT_REQUEST)
|
||||
leader_messages.next_mle_message(mle.CommandType.PARENT_RESPONSE)
|
||||
router1_messages.next_mle_message(mle.CommandType.PARENT_RESPONSE)
|
||||
router2_messages.next_mle_message(mle.CommandType.CHILD_ID_REQUEST)
|
||||
|
||||
# 5 - Router1
|
||||
# Router1 make two attempts to reconnect to its current Partition.
|
||||
for _ in xrange(2):
|
||||
msg = router1_messages.next_mle_message(mle.CommandType.PARENT_REQUEST)
|
||||
msg.assertSentWithHopLimit(255)
|
||||
msg.assertSentToDestinationAddress("ff02::2")
|
||||
msg.assertMleMessageContainsTlv(mle.Mode)
|
||||
msg.assertMleMessageContainsTlv(mle.Challenge)
|
||||
msg.assertMleMessageContainsTlv(mle.ScanMask)
|
||||
msg.assertMleMessageContainsTlv(mle.Version)
|
||||
|
||||
scan_mask_tlv = msg.get_mle_message_tlv(mle.ScanMask)
|
||||
self.assertEqual(1, scan_mask_tlv.router)
|
||||
self.assertEqual(1, scan_mask_tlv.end_device)
|
||||
|
||||
# 6 - Router1
|
||||
msg = router1_messages.next_mle_message(mle.CommandType.PARENT_REQUEST)
|
||||
msg.assertSentWithHopLimit(255)
|
||||
msg.assertSentToDestinationAddress("ff02::2")
|
||||
msg.assertMleMessageContainsTlv(mle.Mode)
|
||||
msg.assertMleMessageContainsTlv(mle.Challenge)
|
||||
msg.assertMleMessageContainsTlv(mle.ScanMask)
|
||||
msg.assertMleMessageContainsTlv(mle.Version)
|
||||
|
||||
scan_mask_tlv = msg.get_mle_message_tlv(mle.ScanMask)
|
||||
self.assertEqual(1, scan_mask_tlv.router)
|
||||
self.assertEqual(0, scan_mask_tlv.end_device)
|
||||
|
||||
# 8 - Router2
|
||||
router2_messages.next_mle_message(mle.CommandType.PARENT_REQUEST)
|
||||
router2_messages.next_mle_message(mle.CommandType.CHILD_ID_REQUEST)
|
||||
|
||||
# 9 - Router1
|
||||
msg = router1_messages.next_mle_message(mle.CommandType.PARENT_RESPONSE)
|
||||
msg.assertSentToNode(self.nodes[ROUTER2])
|
||||
msg.assertMleMessageContainsTlv(mle.SourceAddress)
|
||||
msg.assertMleMessageContainsTlv(mle.LeaderData)
|
||||
msg.assertMleMessageContainsTlv(mle.LinkLayerFrameCounter)
|
||||
msg.assertMleMessageContainsOptionalTlv(mle.MleFrameCounter)
|
||||
msg.assertMleMessageContainsTlv(mle.Response)
|
||||
msg.assertMleMessageContainsTlv(mle.Challenge)
|
||||
msg.assertMleMessageContainsTlv(mle.LinkMargin)
|
||||
msg.assertMleMessageContainsTlv(mle.Connectivity)
|
||||
msg.assertMleMessageContainsTlv(mle.Version)
|
||||
|
||||
msg = router1_messages.next_mle_message(mle.CommandType.CHILD_ID_RESPONSE)
|
||||
msg.assertSentToNode(self.nodes[ROUTER2])
|
||||
msg.assertMleMessageContainsTlv(mle.SourceAddress)
|
||||
msg.assertMleMessageContainsTlv(mle.LeaderData)
|
||||
msg.assertMleMessageContainsTlv(mle.Address16)
|
||||
msg.assertMleMessageContainsOptionalTlv(mle.NetworkData)
|
||||
msg.assertMleMessageContainsOptionalTlv(mle.Route64)
|
||||
msg.assertMleMessageContainsOptionalTlv(mle.AddressRegistration)
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
unittest.main()
|
||||
|
||||
@@ -30,15 +30,20 @@
|
||||
import time
|
||||
import unittest
|
||||
|
||||
import config
|
||||
import mle
|
||||
import node
|
||||
|
||||
LEADER = 1
|
||||
ROUTER1 = 2
|
||||
SNIFFER = 3
|
||||
|
||||
|
||||
class Cert_5_1_05_RouterAddressTimeout(unittest.TestCase):
|
||||
|
||||
def setUp(self):
|
||||
self.nodes = {}
|
||||
for i in range(1,3):
|
||||
for i in range(1, 3):
|
||||
self.nodes[i] = node.Node(i)
|
||||
|
||||
self.nodes[LEADER].set_panid(0xface)
|
||||
@@ -52,7 +57,13 @@ class Cert_5_1_05_RouterAddressTimeout(unittest.TestCase):
|
||||
self.nodes[ROUTER1].enable_whitelist()
|
||||
self.nodes[ROUTER1].set_router_selection_jitter(1)
|
||||
|
||||
self.sniffer = config.create_default_thread_sniffer(SNIFFER)
|
||||
self.sniffer.start()
|
||||
|
||||
def tearDown(self):
|
||||
self.sniffer.stop()
|
||||
del self.sniffer
|
||||
|
||||
for node in list(self.nodes.values()):
|
||||
node.stop()
|
||||
del self.nodes
|
||||
@@ -84,5 +95,35 @@ class Cert_5_1_05_RouterAddressTimeout(unittest.TestCase):
|
||||
self.assertEqual(self.nodes[ROUTER1].get_state(), 'router')
|
||||
self.assertEqual(self.nodes[ROUTER1].get_addr16(), rloc16)
|
||||
|
||||
leader_messages = self.sniffer.get_messages_sent_by(LEADER)
|
||||
router1_messages = self.sniffer.get_messages_sent_by(ROUTER1)
|
||||
|
||||
# 2 - All
|
||||
leader_messages.next_mle_message(mle.CommandType.ADVERTISEMENT)
|
||||
|
||||
router1_messages.next_mle_message(mle.CommandType.PARENT_REQUEST)
|
||||
leader_messages.next_mle_message(mle.CommandType.PARENT_RESPONSE)
|
||||
|
||||
router1_messages.next_mle_message(mle.CommandType.CHILD_ID_REQUEST)
|
||||
leader_messages.next_mle_message(mle.CommandType.CHILD_ID_RESPONSE)
|
||||
|
||||
# 3 - Router1
|
||||
router1_messages.next_mle_message(mle.CommandType.LINK_REQUEST)
|
||||
router1_messages.next_mle_message(mle.CommandType.PARENT_REQUEST)
|
||||
router1_messages.next_mle_message(mle.CommandType.CHILD_ID_REQUEST)
|
||||
|
||||
# 4 - Leader
|
||||
leader_messages.next_mle_message(mle.CommandType.PARENT_RESPONSE)
|
||||
leader_messages.next_mle_message(mle.CommandType.CHILD_ID_RESPONSE)
|
||||
|
||||
# 6 - Router1
|
||||
router1_messages.next_mle_message(mle.CommandType.LINK_REQUEST)
|
||||
router1_messages.next_mle_message(mle.CommandType.PARENT_REQUEST)
|
||||
router1_messages.next_mle_message(mle.CommandType.CHILD_ID_REQUEST)
|
||||
|
||||
# 7 - Leader
|
||||
leader_messages.next_mle_message(mle.CommandType.PARENT_RESPONSE)
|
||||
leader_messages.next_mle_message(mle.CommandType.CHILD_ID_RESPONSE)
|
||||
|
||||
if __name__ == '__main__':
|
||||
unittest.main()
|
||||
|
||||
@@ -30,15 +30,20 @@
|
||||
import time
|
||||
import unittest
|
||||
|
||||
import config
|
||||
import mle
|
||||
import node
|
||||
|
||||
LEADER = 1
|
||||
ROUTER1 = 2
|
||||
SNIFFER = 3
|
||||
|
||||
|
||||
class Cert_5_1_06_RemoveRouterId(unittest.TestCase):
|
||||
|
||||
def setUp(self):
|
||||
self.nodes = {}
|
||||
for i in range(1,3):
|
||||
for i in range(1, 3):
|
||||
self.nodes[i] = node.Node(i)
|
||||
|
||||
self.nodes[LEADER].set_panid(0xface)
|
||||
@@ -52,7 +57,13 @@ class Cert_5_1_06_RemoveRouterId(unittest.TestCase):
|
||||
self.nodes[ROUTER1].enable_whitelist()
|
||||
self.nodes[ROUTER1].set_router_selection_jitter(1)
|
||||
|
||||
self.sniffer = config.create_default_thread_sniffer(SNIFFER)
|
||||
self.sniffer.start()
|
||||
|
||||
def tearDown(self):
|
||||
self.sniffer.stop()
|
||||
del self.sniffer
|
||||
|
||||
for node in list(self.nodes.values()):
|
||||
node.stop()
|
||||
del self.nodes
|
||||
@@ -77,5 +88,26 @@ class Cert_5_1_06_RemoveRouterId(unittest.TestCase):
|
||||
for addr in self.nodes[ROUTER1].get_addrs():
|
||||
self.assertTrue(self.nodes[LEADER].ping(addr))
|
||||
|
||||
leader_messages = self.sniffer.get_messages_sent_by(LEADER)
|
||||
router1_messages = self.sniffer.get_messages_sent_by(ROUTER1)
|
||||
|
||||
# 1 - All
|
||||
leader_messages.next_mle_message(mle.CommandType.ADVERTISEMENT)
|
||||
|
||||
router1_messages.next_mle_message(mle.CommandType.PARENT_REQUEST)
|
||||
leader_messages.next_mle_message(mle.CommandType.PARENT_RESPONSE)
|
||||
|
||||
router1_messages.next_mle_message(mle.CommandType.CHILD_ID_REQUEST)
|
||||
leader_messages.next_mle_message(mle.CommandType.CHILD_ID_RESPONSE)
|
||||
|
||||
# 3 - Router1
|
||||
router1_messages.next_mle_message(mle.CommandType.PARENT_REQUEST)
|
||||
leader_messages.next_mle_message(mle.CommandType.PARENT_RESPONSE)
|
||||
|
||||
router1_messages.next_mle_message(mle.CommandType.CHILD_ID_REQUEST)
|
||||
msg = leader_messages.next_mle_message(mle.CommandType.CHILD_ID_RESPONSE)
|
||||
msg.assertSentToNode(self.nodes[ROUTER1])
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
unittest.main()
|
||||
|
||||
@@ -30,6 +30,8 @@
|
||||
import time
|
||||
import unittest
|
||||
|
||||
import config
|
||||
import mle
|
||||
import node
|
||||
|
||||
LEADER = 1
|
||||
@@ -37,11 +39,14 @@ ROUTER1 = 2
|
||||
ROUTER2 = 3
|
||||
ROUTER3 = 4
|
||||
ROUTER4 = 5
|
||||
SNIFFER = 6
|
||||
|
||||
|
||||
class Cert_5_1_08_RouterAttachConnectivity(unittest.TestCase):
|
||||
|
||||
def setUp(self):
|
||||
self.nodes = {}
|
||||
for i in range(1,6):
|
||||
for i in range(1, 6):
|
||||
self.nodes[i] = node.Node(i)
|
||||
|
||||
self.nodes[LEADER].set_panid(0xface)
|
||||
@@ -80,7 +85,13 @@ class Cert_5_1_08_RouterAttachConnectivity(unittest.TestCase):
|
||||
self.nodes[ROUTER4].enable_whitelist()
|
||||
self.nodes[ROUTER4].set_router_selection_jitter(1)
|
||||
|
||||
self.sniffer = config.create_default_thread_sniffer(SNIFFER)
|
||||
self.sniffer.start()
|
||||
|
||||
def tearDown(self):
|
||||
self.sniffer.stop()
|
||||
del self.sniffer
|
||||
|
||||
for node in list(self.nodes.values()):
|
||||
node.stop()
|
||||
del self.nodes
|
||||
@@ -95,5 +106,50 @@ class Cert_5_1_08_RouterAttachConnectivity(unittest.TestCase):
|
||||
time.sleep(5)
|
||||
self.assertEqual(self.nodes[i].get_state(), 'router')
|
||||
|
||||
leader_messages = self.sniffer.get_messages_sent_by(LEADER)
|
||||
router1_messages = self.sniffer.get_messages_sent_by(ROUTER1)
|
||||
router2_messages = self.sniffer.get_messages_sent_by(ROUTER2)
|
||||
router3_messages = self.sniffer.get_messages_sent_by(ROUTER3)
|
||||
router4_messages = self.sniffer.get_messages_sent_by(ROUTER4)
|
||||
|
||||
# 1 - Leader, Router1, Router2, Router3
|
||||
leader_messages.next_mle_message(mle.CommandType.ADVERTISEMENT)
|
||||
router1_messages.next_mle_message(mle.CommandType.ADVERTISEMENT)
|
||||
router2_messages.next_mle_message(mle.CommandType.ADVERTISEMENT)
|
||||
router3_messages.next_mle_message(mle.CommandType.ADVERTISEMENT)
|
||||
|
||||
# 2 - Router4
|
||||
msg = router4_messages.next_mle_message(mle.CommandType.PARENT_REQUEST)
|
||||
msg.assertSentWithHopLimit(255)
|
||||
msg.assertSentToDestinationAddress("ff02::2")
|
||||
msg.assertMleMessageContainsTlv(mle.Mode)
|
||||
msg.assertMleMessageContainsTlv(mle.Challenge)
|
||||
msg.assertMleMessageContainsTlv(mle.ScanMask)
|
||||
msg.assertMleMessageContainsTlv(mle.Version)
|
||||
|
||||
scan_mask_tlv = msg.get_mle_message_tlv(mle.ScanMask)
|
||||
self.assertEqual(1, scan_mask_tlv.router)
|
||||
self.assertEqual(0, scan_mask_tlv.end_device)
|
||||
|
||||
# 3 - Router2, Router3
|
||||
msg = router2_messages.next_mle_message(mle.CommandType.PARENT_RESPONSE)
|
||||
msg.assertSentToNode(self.nodes[ROUTER4])
|
||||
|
||||
msg = router3_messages.next_mle_message(mle.CommandType.PARENT_RESPONSE)
|
||||
msg.assertSentToNode(self.nodes[ROUTER4])
|
||||
|
||||
# 4 - Router4
|
||||
msg = router4_messages.next_mle_message(mle.CommandType.CHILD_ID_REQUEST)
|
||||
msg.assertSentToNode(self.nodes[ROUTER3])
|
||||
msg.assertMleMessageContainsTlv(mle.Response)
|
||||
msg.assertMleMessageContainsTlv(mle.LinkLayerFrameCounter)
|
||||
msg.assertMleMessageContainsOptionalTlv(mle.MleFrameCounter)
|
||||
msg.assertMleMessageContainsTlv(mle.Mode)
|
||||
msg.assertMleMessageContainsTlv(mle.Timeout)
|
||||
msg.assertMleMessageContainsTlv(mle.Version)
|
||||
msg.assertMleMessageContainsTlv(mle.TlvRequest)
|
||||
msg.assertMleMessageDoesNotContainTlv(mle.AddressRegistration)
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
unittest.main()
|
||||
|
||||
@@ -30,6 +30,8 @@
|
||||
import time
|
||||
import unittest
|
||||
|
||||
import config
|
||||
import mle
|
||||
import node
|
||||
|
||||
LEADER = 1
|
||||
@@ -37,11 +39,14 @@ ROUTER1 = 2
|
||||
REED0 = 3
|
||||
REED1 = 4
|
||||
ROUTER2 = 5
|
||||
SNIFFER = 6
|
||||
|
||||
|
||||
class Cert_5_1_09_REEDAttachConnectivity(unittest.TestCase):
|
||||
|
||||
def setUp(self):
|
||||
self.nodes = {}
|
||||
for i in range(1,6):
|
||||
for i in range(1, 6):
|
||||
self.nodes[i] = node.Node(i)
|
||||
|
||||
self.nodes[LEADER].set_panid(0xface)
|
||||
@@ -80,7 +85,13 @@ class Cert_5_1_09_REEDAttachConnectivity(unittest.TestCase):
|
||||
self.nodes[ROUTER2].enable_whitelist()
|
||||
self.nodes[ROUTER2].set_router_selection_jitter(1)
|
||||
|
||||
self.sniffer = config.create_default_thread_sniffer(SNIFFER)
|
||||
self.sniffer.start()
|
||||
|
||||
def tearDown(self):
|
||||
self.sniffer.stop()
|
||||
del self.sniffer
|
||||
|
||||
for node in list(self.nodes.values()):
|
||||
node.stop()
|
||||
del self.nodes
|
||||
@@ -109,5 +120,63 @@ class Cert_5_1_09_REEDAttachConnectivity(unittest.TestCase):
|
||||
self.assertEqual(self.nodes[ROUTER2].get_state(), 'router')
|
||||
self.assertEqual(self.nodes[REED1].get_state(), 'router')
|
||||
|
||||
leader_messages = self.sniffer.get_messages_sent_by(LEADER)
|
||||
router1_messages = self.sniffer.get_messages_sent_by(ROUTER1)
|
||||
reed0_messages = self.sniffer.get_messages_sent_by(REED0)
|
||||
reed1_messages = self.sniffer.get_messages_sent_by(REED1)
|
||||
router2_messages = self.sniffer.get_messages_sent_by(ROUTER2)
|
||||
|
||||
# 1 - Leader, Router1, REED1, REED2
|
||||
leader_messages.next_mle_message(mle.CommandType.ADVERTISEMENT)
|
||||
router1_messages.next_mle_message(mle.CommandType.ADVERTISEMENT)
|
||||
|
||||
# 2 - Router2
|
||||
msg = router2_messages.next_mle_message(mle.CommandType.PARENT_REQUEST)
|
||||
msg.assertSentWithHopLimit(255)
|
||||
msg.assertSentToDestinationAddress("ff02::2")
|
||||
msg.assertMleMessageContainsTlv(mle.Mode)
|
||||
msg.assertMleMessageContainsTlv(mle.Challenge)
|
||||
msg.assertMleMessageContainsTlv(mle.ScanMask)
|
||||
msg.assertMleMessageContainsTlv(mle.Version)
|
||||
|
||||
scan_mask_tlv = msg.get_mle_message_tlv(mle.ScanMask)
|
||||
self.assertEqual(1, scan_mask_tlv.router)
|
||||
self.assertEqual(0, scan_mask_tlv.end_device)
|
||||
|
||||
# 4 - Router2
|
||||
msg = router2_messages.next_mle_message(mle.CommandType.PARENT_REQUEST)
|
||||
msg.assertSentWithHopLimit(255)
|
||||
msg.assertSentToDestinationAddress("ff02::2")
|
||||
msg.assertMleMessageContainsTlv(mle.Mode)
|
||||
msg.assertMleMessageContainsTlv(mle.Challenge)
|
||||
msg.assertMleMessageContainsTlv(mle.ScanMask)
|
||||
msg.assertMleMessageContainsTlv(mle.Version)
|
||||
|
||||
scan_mask_tlv = msg.get_mle_message_tlv(mle.ScanMask)
|
||||
self.assertEqual(1, scan_mask_tlv.router)
|
||||
self.assertEqual(1, scan_mask_tlv.end_device)
|
||||
|
||||
# 5 - REED1, REED2
|
||||
msg = reed0_messages.next_mle_message(mle.CommandType.PARENT_RESPONSE)
|
||||
connectivity_tlv_reed0 = msg.get_mle_message_tlv(mle.Connectivity)
|
||||
|
||||
msg = reed1_messages.next_mle_message(mle.CommandType.PARENT_RESPONSE)
|
||||
connectivity_tlv_reed1 = msg.get_mle_message_tlv(mle.Connectivity)
|
||||
|
||||
self.assertGreater(connectivity_tlv_reed1.link_quality_3, connectivity_tlv_reed0.link_quality_3)
|
||||
|
||||
# 6 - Router2
|
||||
msg = router2_messages.next_mle_message(mle.CommandType.CHILD_ID_REQUEST)
|
||||
msg.assertSentToNode(self.nodes[REED1])
|
||||
msg.assertMleMessageContainsTlv(mle.Response)
|
||||
msg.assertMleMessageContainsTlv(mle.LinkLayerFrameCounter)
|
||||
msg.assertMleMessageContainsOptionalTlv(mle.MleFrameCounter)
|
||||
msg.assertMleMessageContainsTlv(mle.Mode)
|
||||
msg.assertMleMessageContainsTlv(mle.Timeout)
|
||||
msg.assertMleMessageContainsTlv(mle.Version)
|
||||
msg.assertMleMessageContainsTlv(mle.TlvRequest)
|
||||
msg.assertMleMessageDoesNotContainTlv(mle.AddressRegistration)
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
unittest.main()
|
||||
|
||||
@@ -30,17 +30,22 @@
|
||||
import time
|
||||
import unittest
|
||||
|
||||
import config
|
||||
import mle
|
||||
import node
|
||||
|
||||
LEADER = 1
|
||||
ROUTER1 = 2
|
||||
ROUTER2 = 3
|
||||
ROUTER3 = 4
|
||||
SNIFFER = 5
|
||||
|
||||
|
||||
class Cert_5_1_10_RouterAttachLinkQuality(unittest.TestCase):
|
||||
|
||||
def setUp(self):
|
||||
self.nodes = {}
|
||||
for i in range(1,5):
|
||||
for i in range(1, 5):
|
||||
self.nodes[i] = node.Node(i)
|
||||
|
||||
self.nodes[LEADER].set_panid(0xface)
|
||||
@@ -70,7 +75,13 @@ class Cert_5_1_10_RouterAttachLinkQuality(unittest.TestCase):
|
||||
self.nodes[ROUTER3].enable_whitelist()
|
||||
self.nodes[ROUTER3].set_router_selection_jitter(1)
|
||||
|
||||
self.sniffer = config.create_default_thread_sniffer(SNIFFER)
|
||||
self.sniffer.start()
|
||||
|
||||
def tearDown(self):
|
||||
self.sniffer.stop()
|
||||
del self.sniffer
|
||||
|
||||
for node in list(self.nodes.values()):
|
||||
node.stop()
|
||||
del self.nodes
|
||||
@@ -92,5 +103,44 @@ class Cert_5_1_10_RouterAttachLinkQuality(unittest.TestCase):
|
||||
time.sleep(5)
|
||||
self.assertEqual(self.nodes[ROUTER3].get_state(), 'router')
|
||||
|
||||
leader_messages = self.sniffer.get_messages_sent_by(LEADER)
|
||||
router1_messages = self.sniffer.get_messages_sent_by(ROUTER1)
|
||||
router2_messages = self.sniffer.get_messages_sent_by(ROUTER2)
|
||||
router3_messages = self.sniffer.get_messages_sent_by(ROUTER3)
|
||||
|
||||
# 1 - Leader, Router1, Router2
|
||||
leader_messages.next_mle_message(mle.CommandType.ADVERTISEMENT)
|
||||
router1_messages.next_mle_message(mle.CommandType.ADVERTISEMENT)
|
||||
router2_messages.next_mle_message(mle.CommandType.ADVERTISEMENT)
|
||||
|
||||
# 3 - Router3
|
||||
msg = router3_messages.next_mle_message(mle.CommandType.PARENT_REQUEST)
|
||||
msg.assertSentWithHopLimit(255)
|
||||
msg.assertSentToDestinationAddress("ff02::2")
|
||||
msg.assertMleMessageContainsTlv(mle.Mode)
|
||||
msg.assertMleMessageContainsTlv(mle.Challenge)
|
||||
msg.assertMleMessageContainsTlv(mle.ScanMask)
|
||||
msg.assertMleMessageContainsTlv(mle.Version)
|
||||
|
||||
# 4 - Router1, Router2
|
||||
msg = router1_messages.next_mle_message(mle.CommandType.PARENT_RESPONSE)
|
||||
msg.assertSentToNode(self.nodes[ROUTER3])
|
||||
|
||||
msg = router2_messages.next_mle_message(mle.CommandType.PARENT_RESPONSE)
|
||||
msg.assertSentToNode(self.nodes[ROUTER3])
|
||||
|
||||
# 5 - Router3
|
||||
msg = router3_messages.next_mle_message(mle.CommandType.CHILD_ID_REQUEST)
|
||||
msg.assertSentToNode(self.nodes[ROUTER1])
|
||||
msg.assertMleMessageContainsTlv(mle.Response)
|
||||
msg.assertMleMessageContainsTlv(mle.LinkLayerFrameCounter)
|
||||
msg.assertMleMessageContainsOptionalTlv(mle.MleFrameCounter)
|
||||
msg.assertMleMessageContainsTlv(mle.Mode)
|
||||
msg.assertMleMessageContainsTlv(mle.Timeout)
|
||||
msg.assertMleMessageContainsTlv(mle.Version)
|
||||
msg.assertMleMessageContainsTlv(mle.TlvRequest)
|
||||
msg.assertMleMessageDoesNotContainTlv(mle.AddressRegistration)
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
unittest.main()
|
||||
|
||||
@@ -30,17 +30,22 @@
|
||||
import time
|
||||
import unittest
|
||||
|
||||
import config
|
||||
import mle
|
||||
import node
|
||||
|
||||
LEADER = 1
|
||||
REED = 2
|
||||
ROUTER2 = 3
|
||||
ROUTER1 = 4
|
||||
SNIFFER = 5
|
||||
|
||||
|
||||
class Cert_5_1_11_REEDAttachLinkQuality(unittest.TestCase):
|
||||
|
||||
def setUp(self):
|
||||
self.nodes = {}
|
||||
for i in range(1,5):
|
||||
for i in range(1, 5):
|
||||
self.nodes[i] = node.Node(i)
|
||||
|
||||
self.nodes[LEADER].set_panid(0xface)
|
||||
@@ -70,7 +75,13 @@ class Cert_5_1_11_REEDAttachLinkQuality(unittest.TestCase):
|
||||
self.nodes[ROUTER1].enable_whitelist()
|
||||
self.nodes[ROUTER1].set_router_selection_jitter(1)
|
||||
|
||||
self.sniffer = config.create_default_thread_sniffer(SNIFFER)
|
||||
self.sniffer.start()
|
||||
|
||||
def tearDown(self):
|
||||
self.sniffer.stop()
|
||||
del self.sniffer
|
||||
|
||||
for node in list(self.nodes.values()):
|
||||
node.stop()
|
||||
del self.nodes
|
||||
@@ -93,5 +104,46 @@ class Cert_5_1_11_REEDAttachLinkQuality(unittest.TestCase):
|
||||
self.assertEqual(self.nodes[ROUTER1].get_state(), 'router')
|
||||
self.assertEqual(self.nodes[REED].get_state(), 'router')
|
||||
|
||||
leader_messages = self.sniffer.get_messages_sent_by(LEADER)
|
||||
router1_messages = self.sniffer.get_messages_sent_by(ROUTER1)
|
||||
reed_messages = self.sniffer.get_messages_sent_by(REED)
|
||||
router2_messages = self.sniffer.get_messages_sent_by(ROUTER2)
|
||||
|
||||
# 1 - Leader. REED1, Router2
|
||||
leader_messages.next_mle_message(mle.CommandType.ADVERTISEMENT)
|
||||
reed_messages.next_mle_message(mle.CommandType.ADVERTISEMENT)
|
||||
router2_messages.next_mle_message(mle.CommandType.ADVERTISEMENT)
|
||||
|
||||
# 3 - Router1
|
||||
msg = router1_messages.next_mle_message(mle.CommandType.PARENT_REQUEST)
|
||||
msg.assertSentWithHopLimit(255)
|
||||
msg.assertSentToDestinationAddress("ff02::2")
|
||||
msg.assertMleMessageContainsTlv(mle.Mode)
|
||||
msg.assertMleMessageContainsTlv(mle.Challenge)
|
||||
msg.assertMleMessageContainsTlv(mle.ScanMask)
|
||||
msg.assertMleMessageContainsTlv(mle.Version)
|
||||
|
||||
scan_mask_tlv = msg.get_mle_message_tlv(mle.ScanMask)
|
||||
self.assertEqual(1, scan_mask_tlv.router)
|
||||
self.assertEqual(0, scan_mask_tlv.end_device)
|
||||
|
||||
# 4 - Router2
|
||||
msg = router2_messages.next_mle_message(mle.CommandType.PARENT_RESPONSE)
|
||||
msg.assertSentToNode(self.nodes[ROUTER1])
|
||||
|
||||
# 5 - Router1
|
||||
msg = router1_messages.next_mle_message(mle.CommandType.PARENT_REQUEST)
|
||||
msg.assertSentWithHopLimit(255)
|
||||
msg.assertSentToDestinationAddress("ff02::2")
|
||||
msg.assertMleMessageContainsTlv(mle.Mode)
|
||||
msg.assertMleMessageContainsTlv(mle.Challenge)
|
||||
msg.assertMleMessageContainsTlv(mle.ScanMask)
|
||||
msg.assertMleMessageContainsTlv(mle.Version)
|
||||
|
||||
scan_mask_tlv = msg.get_mle_message_tlv(mle.ScanMask)
|
||||
self.assertEqual(1, scan_mask_tlv.router)
|
||||
self.assertEqual(1, scan_mask_tlv.end_device)
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
unittest.main()
|
||||
|
||||
@@ -30,16 +30,21 @@
|
||||
import time
|
||||
import unittest
|
||||
|
||||
import config
|
||||
import mle
|
||||
import node
|
||||
|
||||
LEADER = 1
|
||||
ROUTER1 = 2
|
||||
ROUTER2 = 3
|
||||
SNIFFER = 4
|
||||
|
||||
|
||||
class Cert_5_1_12_NewRouterSync(unittest.TestCase):
|
||||
|
||||
def setUp(self):
|
||||
self.nodes = {}
|
||||
for i in range(1,4):
|
||||
for i in range(1, 4):
|
||||
self.nodes[i] = node.Node(i)
|
||||
|
||||
self.nodes[LEADER].set_panid(0xface)
|
||||
@@ -60,11 +65,52 @@ class Cert_5_1_12_NewRouterSync(unittest.TestCase):
|
||||
self.nodes[ROUTER2].enable_whitelist()
|
||||
self.nodes[ROUTER2].set_router_selection_jitter(1)
|
||||
|
||||
self.sniffer = config.create_default_thread_sniffer(SNIFFER)
|
||||
self.sniffer.start()
|
||||
|
||||
def tearDown(self):
|
||||
self.sniffer.stop()
|
||||
del self.sniffer
|
||||
|
||||
for node in list(self.nodes.values()):
|
||||
node.stop()
|
||||
del self.nodes
|
||||
|
||||
def verify_step_4(self, router1_messages, router2_messages, req_receiver, accept_receiver):
|
||||
if router2_messages.contains_mle_message(mle.CommandType.LINK_REQUEST) and \
|
||||
(router1_messages.contains_mle_message(mle.CommandType.LINK_ACCEPT) or
|
||||
router1_messages.contains_mle_message(mle.CommandType.LINK_ACCEPT_AND_REQUEST)):
|
||||
|
||||
msg = router2_messages.next_mle_message(mle.CommandType.LINK_REQUEST)
|
||||
|
||||
msg.assertSentToNode(self.nodes[req_receiver])
|
||||
msg.assertMleMessageContainsTlv(mle.SourceAddress)
|
||||
msg.assertMleMessageContainsTlv(mle.LeaderData)
|
||||
msg.assertMleMessageContainsTlv(mle.Challenge)
|
||||
msg.assertMleMessageContainsTlv(mle.Version)
|
||||
msg.assertMleMessageContainsTlv(mle.TlvRequest)
|
||||
|
||||
msg = router1_messages.next_mle_message_of_one_of_command_types(mle.CommandType.LINK_ACCEPT_AND_REQUEST,
|
||||
mle.CommandType.LINK_ACCEPT)
|
||||
self.assertIsNotNone(msg)
|
||||
|
||||
msg.assertSentToNode(self.nodes[accept_receiver])
|
||||
msg.assertMleMessageContainsTlv(mle.SourceAddress)
|
||||
msg.assertMleMessageContainsTlv(mle.LeaderData)
|
||||
msg.assertMleMessageContainsTlv(mle.LinkLayerFrameCounter)
|
||||
msg.assertMleMessageContainsOptionalTlv(mle.MleFrameCounter)
|
||||
msg.assertMleMessageContainsTlv(mle.Version)
|
||||
msg.assertMleMessageContainsTlv(mle.LinkMargin)
|
||||
|
||||
if msg.mle.command.type == mle.CommandType.LINK_ACCEPT_AND_REQUEST:
|
||||
msg.assertMleMessageContainsTlv(mle.TlvRequest)
|
||||
msg.assertMleMessageContainsTlv(mle.Challenge)
|
||||
|
||||
return True
|
||||
|
||||
else:
|
||||
return False
|
||||
|
||||
def test(self):
|
||||
self.nodes[LEADER].start()
|
||||
self.nodes[LEADER].set_state('leader')
|
||||
@@ -80,10 +126,30 @@ class Cert_5_1_12_NewRouterSync(unittest.TestCase):
|
||||
|
||||
time.sleep(10)
|
||||
|
||||
leader_messages = self.sniffer.get_messages_sent_by(LEADER)
|
||||
router1_messages = self.sniffer.get_messages_sent_by(ROUTER1)
|
||||
router2_messages = self.sniffer.get_messages_sent_by(ROUTER2)
|
||||
|
||||
# 2 - Router1
|
||||
msg = router1_messages.next_mle_message(mle.CommandType.ADVERTISEMENT)
|
||||
msg.assertSentWithHopLimit(255)
|
||||
msg.assertSentToDestinationAddress("ff02::1")
|
||||
msg.assertMleMessageContainsTlv(mle.SourceAddress)
|
||||
msg.assertMleMessageContainsTlv(mle.LeaderData)
|
||||
msg.assertMleMessageContainsTlv(mle.Route64)
|
||||
|
||||
self.nodes[ROUTER1].add_whitelist(self.nodes[ROUTER2].get_addr64())
|
||||
self.nodes[ROUTER2].add_whitelist(self.nodes[ROUTER1].get_addr64())
|
||||
|
||||
time.sleep(10)
|
||||
time.sleep(35)
|
||||
|
||||
leader_messages = self.sniffer.get_messages_sent_by(LEADER)
|
||||
router1_messages = self.sniffer.get_messages_sent_by(ROUTER1)
|
||||
router2_messages = self.sniffer.get_messages_sent_by(ROUTER2)
|
||||
|
||||
# 4 - Router1, Router2
|
||||
self.assertTrue(self.verify_step_4(router1_messages, router2_messages, ROUTER1, ROUTER2) or
|
||||
self.verify_step_4(router2_messages, router1_messages, ROUTER2, ROUTER1))
|
||||
|
||||
if __name__ == '__main__':
|
||||
unittest.main()
|
||||
|
||||
@@ -30,15 +30,20 @@
|
||||
import time
|
||||
import unittest
|
||||
|
||||
import config
|
||||
import mle
|
||||
import node
|
||||
|
||||
LEADER = 1
|
||||
ROUTER = 2
|
||||
SNIFFER = 3
|
||||
|
||||
|
||||
class Cert_5_1_13_RouterReset(unittest.TestCase):
|
||||
|
||||
def setUp(self):
|
||||
self.nodes = {}
|
||||
for i in range(1,3):
|
||||
for i in range(1, 3):
|
||||
self.nodes[i] = node.Node(i)
|
||||
|
||||
self.nodes[LEADER].set_panid(0xface)
|
||||
@@ -52,7 +57,13 @@ class Cert_5_1_13_RouterReset(unittest.TestCase):
|
||||
self.nodes[ROUTER].enable_whitelist()
|
||||
self.nodes[ROUTER].set_router_selection_jitter(1)
|
||||
|
||||
self.sniffer = config.create_default_thread_sniffer(SNIFFER)
|
||||
self.sniffer.start()
|
||||
|
||||
def tearDown(self):
|
||||
self.sniffer.stop()
|
||||
del self.sniffer
|
||||
|
||||
for node in list(self.nodes.values()):
|
||||
node.stop()
|
||||
del self.nodes
|
||||
@@ -68,7 +79,7 @@ class Cert_5_1_13_RouterReset(unittest.TestCase):
|
||||
|
||||
rloc16 = self.nodes[ROUTER].get_addr16()
|
||||
|
||||
self.nodes[ROUTER].stop();
|
||||
self.nodes[ROUTER].stop()
|
||||
time.sleep(5)
|
||||
|
||||
self.nodes[ROUTER].start()
|
||||
@@ -76,5 +87,62 @@ class Cert_5_1_13_RouterReset(unittest.TestCase):
|
||||
self.assertEqual(self.nodes[ROUTER].get_state(), 'router')
|
||||
self.assertEqual(self.nodes[ROUTER].get_addr16(), rloc16)
|
||||
|
||||
leader_messages = self.sniffer.get_messages_sent_by(LEADER)
|
||||
router1_messages = self.sniffer.get_messages_sent_by(ROUTER)
|
||||
|
||||
# 1 - All
|
||||
leader_messages.next_mle_message(mle.CommandType.ADVERTISEMENT)
|
||||
|
||||
router1_messages.next_mle_message(mle.CommandType.PARENT_REQUEST)
|
||||
leader_messages.next_mle_message(mle.CommandType.PARENT_RESPONSE)
|
||||
|
||||
router1_messages.next_mle_message(mle.CommandType.CHILD_ID_REQUEST)
|
||||
leader_messages.next_mle_message(mle.CommandType.CHILD_ID_RESPONSE)
|
||||
|
||||
router1_messages.next_mle_message(mle.CommandType.LINK_REQUEST)
|
||||
msg = leader_messages.next_mle_message_of_one_of_command_types(mle.CommandType.LINK_ACCEPT_AND_REQUEST,
|
||||
mle.CommandType.LINK_ACCEPT)
|
||||
self.assertIsNotNone(msg)
|
||||
|
||||
# 2 - Router1 / Leader
|
||||
msg = router1_messages.next_mle_message(mle.CommandType.ADVERTISEMENT)
|
||||
msg.assertSentWithHopLimit(255)
|
||||
msg.assertSentToDestinationAddress("ff02::1")
|
||||
msg.assertMleMessageContainsTlv(mle.SourceAddress)
|
||||
msg.assertMleMessageContainsTlv(mle.LeaderData)
|
||||
msg.assertMleMessageContainsTlv(mle.Route64)
|
||||
|
||||
msg = leader_messages.next_mle_message(mle.CommandType.ADVERTISEMENT)
|
||||
msg.assertSentWithHopLimit(255)
|
||||
msg.assertSentToDestinationAddress("ff02::1")
|
||||
msg.assertMleMessageContainsTlv(mle.SourceAddress)
|
||||
msg.assertMleMessageContainsTlv(mle.LeaderData)
|
||||
msg.assertMleMessageContainsTlv(mle.Route64)
|
||||
|
||||
# 4 - Router1
|
||||
msg = router1_messages.next_mle_message(mle.CommandType.LINK_REQUEST)
|
||||
msg.assertSentToDestinationAddress("ff02::2")
|
||||
msg.assertMleMessageContainsTlv(mle.Challenge)
|
||||
msg.assertMleMessageContainsTlv(mle.Version)
|
||||
msg.assertMleMessageContainsTlv(mle.TlvRequest)
|
||||
|
||||
tlv_request = msg.get_mle_message_tlv(mle.TlvRequest)
|
||||
self.assertIn(mle.TlvType.ROUTE64, tlv_request.tlvs)
|
||||
self.assertIn(mle.TlvType.ADDRESS16, tlv_request.tlvs)
|
||||
|
||||
# 5 - Leader
|
||||
msg = leader_messages.next_mle_message(mle.CommandType.LINK_ACCEPT)
|
||||
msg.assertSentToNode(self.nodes[ROUTER])
|
||||
msg.assertMleMessageContainsTlv(mle.SourceAddress)
|
||||
msg.assertMleMessageContainsTlv(mle.LeaderData)
|
||||
msg.assertMleMessageContainsTlv(mle.Response)
|
||||
msg.assertMleMessageContainsTlv(mle.LinkLayerFrameCounter)
|
||||
msg.assertMleMessageContainsOptionalTlv(mle.MleFrameCounter)
|
||||
msg.assertMleMessageContainsTlv(mle.Address16)
|
||||
msg.assertMleMessageContainsTlv(mle.Version)
|
||||
msg.assertMleMessageContainsTlv(mle.Route64)
|
||||
msg.assertMleMessageContainsOptionalTlv(mle.Challenge)
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
unittest.main()
|
||||
|
||||
@@ -0,0 +1,151 @@
|
||||
#!/usr/bin/python
|
||||
#
|
||||
# Copyright (c) 2016, 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.
|
||||
#
|
||||
|
||||
import struct
|
||||
|
||||
import ipaddress
|
||||
|
||||
from binascii import hexlify
|
||||
|
||||
|
||||
def enum(*sequential, **named):
|
||||
enums = dict(zip(sequential, range(len(sequential))), **named)
|
||||
names = dict((value, key) for key, value in enums.iteritems())
|
||||
enums['name'] = names
|
||||
return type('Enum', (), enums)
|
||||
|
||||
|
||||
class MessageInfo(object):
|
||||
|
||||
def __init__(self):
|
||||
self.aux_sec_hdr = None
|
||||
self.aux_sec_hdr_bytes = None
|
||||
|
||||
self.mhr_bytes = None
|
||||
self.nonpayload_fields = None
|
||||
|
||||
self.source_mac_address = None
|
||||
self.destination_mac_address = None
|
||||
|
||||
self._source_ipv6 = None
|
||||
self._destination_ipv6 = None
|
||||
|
||||
self.stable = None
|
||||
self.payload_length = 0
|
||||
|
||||
def _convert_value_to_ip_address(self, value):
|
||||
if isinstance(value, str):
|
||||
value = unicode(value)
|
||||
|
||||
elif isinstance(value, bytearray):
|
||||
value = bytes(value)
|
||||
|
||||
return ipaddress.ip_address(value)
|
||||
|
||||
@property
|
||||
def source_ipv6(self):
|
||||
return self._source_ipv6
|
||||
|
||||
@source_ipv6.setter
|
||||
def source_ipv6(self, value):
|
||||
self._source_ipv6 = self._convert_value_to_ip_address(value)
|
||||
|
||||
@property
|
||||
def destination_ipv6(self):
|
||||
return self._destination_ipv6
|
||||
|
||||
@destination_ipv6.setter
|
||||
def destination_ipv6(self, value):
|
||||
self._destination_ipv6 = self._convert_value_to_ip_address(value)
|
||||
|
||||
|
||||
class MacAddress(object):
|
||||
|
||||
SHORT = 0
|
||||
LONG = 1
|
||||
|
||||
def __init__(self, mac_address, _type, big_endian=True):
|
||||
if _type == self.SHORT:
|
||||
length = 2
|
||||
elif _type == self.LONG:
|
||||
length = 8
|
||||
|
||||
if not big_endian:
|
||||
mac_address = mac_address[::-1]
|
||||
|
||||
self._mac_address = bytearray(mac_address[:length])
|
||||
self._type = _type
|
||||
|
||||
@property
|
||||
def type(self):
|
||||
return self._type
|
||||
|
||||
@property
|
||||
def type_str(self):
|
||||
return "SHORT" if self.type == self.SHORT else "LONG"
|
||||
|
||||
@property
|
||||
def mac_address(self):
|
||||
return self._mac_address
|
||||
|
||||
@property
|
||||
def rloc(self):
|
||||
return struct.unpack(">H", self._mac_address)[0]
|
||||
|
||||
def convert_to_iid(self):
|
||||
if self._type == self.SHORT:
|
||||
return bytearray([0x00, 0x00, 0x00, 0xff, 0xfe, 0x00]) + self._mac_address[:2]
|
||||
elif self._type == self.LONG:
|
||||
return bytearray([self._mac_address[0] ^ 0x02]) + self._mac_address[1:]
|
||||
else:
|
||||
raise RuntimeError("Could not convert to IID. Invalid MAC address type: {}".format(self._type))
|
||||
|
||||
@classmethod
|
||||
def from_eui64(cls, eui64, big_endian=True):
|
||||
if not isinstance(eui64, bytearray):
|
||||
raise RuntimeError("Could not create MAC address from EUI64. Invalid data type: {}".format(type(eui64)))
|
||||
|
||||
return cls(eui64, MacAddress.LONG)
|
||||
|
||||
@classmethod
|
||||
def from_rloc16(cls, rloc16, big_endian=True):
|
||||
if isinstance(rloc16, int) or isinstance(rloc16, long):
|
||||
mac_address = struct.pack(">H", rloc16)
|
||||
elif isinstance(rloc16, bytearray):
|
||||
mac_address = rloc16[:2]
|
||||
else:
|
||||
raise RuntimeError("Could not create MAC address from RLOC16. Invalid data type: {}".format(type(rloc16)))
|
||||
|
||||
return cls(mac_address, MacAddress.SHORT)
|
||||
|
||||
def __eq__(self, other):
|
||||
return (self.type == other.type) and (self.mac_address == other.mac_address)
|
||||
|
||||
def __repr__(self):
|
||||
return "MacAddress(mac_address=b'{}', type={})".format(hexlify(self.mac_address), self.type_str)
|
||||
@@ -0,0 +1,247 @@
|
||||
#!/usr/bin/python
|
||||
#
|
||||
# Copyright (c) 2016, 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.
|
||||
#
|
||||
|
||||
import ipv6
|
||||
import lowpan
|
||||
import message
|
||||
import mle
|
||||
import net_crypto
|
||||
import network_data
|
||||
import sniffer
|
||||
|
||||
|
||||
DEFAULT_MASTER_KEY = bytearray([0x00, 0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77,
|
||||
0x88, 0x99, 0xaa, 0xbb, 0xcc, 0xdd, 0xee, 0xff])
|
||||
|
||||
|
||||
def create_default_network_data_prefix_sub_tlvs_factories():
|
||||
return {
|
||||
0: network_data.HasRouteFactory(
|
||||
routes_factory=network_data.RoutesFactory(
|
||||
route_factory=network_data.RouteFactory())
|
||||
),
|
||||
2: network_data.BorderRouterFactory(),
|
||||
3: network_data.LowpanIdFactory()
|
||||
}
|
||||
|
||||
|
||||
def create_default_network_data_prefix_sub_tlvs_factory():
|
||||
return network_data.PrefixSubTlvsFactory(
|
||||
sub_tlvs_factories=create_default_network_data_prefix_sub_tlvs_factories())
|
||||
|
||||
|
||||
def create_default_network_data_service_sub_tlvs_factories():
|
||||
return {
|
||||
6: network_data.ServerFactory()
|
||||
}
|
||||
|
||||
|
||||
def create_default_network_data_service_sub_tlvs_factory():
|
||||
return network_data.ServiceSubTlvsFactory(
|
||||
sub_tlvs_factories=create_default_network_data_service_sub_tlvs_factories())
|
||||
|
||||
|
||||
def create_default_network_data_tlvs_factories():
|
||||
return {
|
||||
1: network_data.PrefixFactory(
|
||||
sub_tlvs_factory=create_default_network_data_prefix_sub_tlvs_factory()
|
||||
),
|
||||
5: network_data.ServiceFactory(
|
||||
sub_tlvs_factory=create_default_network_data_service_sub_tlvs_factory()
|
||||
)
|
||||
}
|
||||
|
||||
|
||||
def create_default_network_data_tlvs_factory():
|
||||
return network_data.NetworkDataTlvsFactory(
|
||||
sub_tlvs_factories=create_default_network_data_tlvs_factories())
|
||||
|
||||
|
||||
def create_default_mle_tlv_route64_factory():
|
||||
return mle.Route64Factory(
|
||||
link_quality_and_route_data_factory=mle.LinkQualityAndRouteDataFactory())
|
||||
|
||||
|
||||
def create_default_mle_tlv_network_data_factory():
|
||||
return mle.NetworkDataFactory(
|
||||
network_data_tlvs_factory=create_default_network_data_tlvs_factory())
|
||||
|
||||
|
||||
def create_default_mle_tlv_address_registration_factory():
|
||||
return mle.AddressRegistrationFactory(
|
||||
addr_compressed_factory=mle.AddressCompressedFactory(),
|
||||
addr_full_factory=mle.AddressFullFactory())
|
||||
|
||||
|
||||
def create_default_mle_tlvs_factories():
|
||||
return {
|
||||
mle.TlvType.SOURCE_ADDRESS: mle.SourceAddressFactory(),
|
||||
mle.TlvType.MODE: mle.ModeFactory(),
|
||||
mle.TlvType.TIMEOUT: mle.TimeoutFactory(),
|
||||
mle.TlvType.CHALLENGE: mle.ChallengeFactory(),
|
||||
mle.TlvType.RESPONSE: mle.ResponseFactory(),
|
||||
mle.TlvType.LINK_LAYER_FRAME_COUNTER: mle.LinkLayerFrameCounterFactory(),
|
||||
mle.TlvType.MLE_FRAME_COUNTER: mle.MleFrameCounterFactory(),
|
||||
mle.TlvType.ROUTE64: create_default_mle_tlv_route64_factory(),
|
||||
mle.TlvType.ADDRESS16: mle.Address16Factory(),
|
||||
mle.TlvType.LEADER_DATA: mle.LeaderDataFactory(),
|
||||
mle.TlvType.NETWORK_DATA: create_default_mle_tlv_network_data_factory(),
|
||||
mle.TlvType.TLV_REQUEST: mle.TlvRequestFactory(),
|
||||
mle.TlvType.SCAN_MASK: mle.ScanMaskFactory(),
|
||||
mle.TlvType.CONNECTIVITY: mle.ConnectivityFactory(),
|
||||
mle.TlvType.LINK_MARGIN: mle.LinkMarginFactory(),
|
||||
mle.TlvType.STATUS: mle.StatusFactory(),
|
||||
mle.TlvType.VERSION: mle.VersionFactory(),
|
||||
mle.TlvType.ADDRESS_REGISTRATION: create_default_mle_tlv_address_registration_factory(),
|
||||
mle.TlvType.CHANNEL: mle.ChannelFactory(),
|
||||
mle.TlvType.PANID: mle.PanIdFactory(),
|
||||
mle.TlvType.ACTIVE_TIMESTAMP: mle.ActiveTimestampFactory(),
|
||||
mle.TlvType.PENDING_TIMESTAMP: mle.PendingTimestampFactory(),
|
||||
mle.TlvType.ACTIVE_OPERATIONAL_DATASET: mle.ActiveOperationalDatasetFactory(),
|
||||
mle.TlvType.PENDING_OPERATIONAL_DATASET: mle.PendingOperationalDatasetFactory(),
|
||||
mle.TlvType.THREAD_DISCOVERY: mle.ThreadDiscoveryFactory()
|
||||
}
|
||||
|
||||
|
||||
def create_default_mle_crypto_engine(master_key):
|
||||
return net_crypto.CryptoEngine(crypto_material_creator=net_crypto.MleCryptoMaterialCreator(master_key))
|
||||
|
||||
|
||||
def create_default_mle_message_factory(master_key):
|
||||
return mle.MleMessageFactory(
|
||||
aux_sec_hdr_factory=net_crypto.AuxiliarySecurityHeaderFactory(),
|
||||
mle_command_factory=mle.MleCommandFactory(
|
||||
tlvs_factories=create_default_mle_tlvs_factories()),
|
||||
crypto_engine=create_default_mle_crypto_engine(master_key))
|
||||
|
||||
|
||||
def create_default_ipv6_hop_by_hop_options_factories():
|
||||
return {
|
||||
109: ipv6.MPLOptionFactory()
|
||||
}
|
||||
|
||||
|
||||
def create_default_ipv6_hop_by_hop_options_factory():
|
||||
return ipv6.HopByHopOptionsFactory(
|
||||
options_factories=create_default_ipv6_hop_by_hop_options_factories())
|
||||
|
||||
|
||||
def create_default_ipv6_udp_dst_port_factories(master_key):
|
||||
mle_message_factory = create_default_mle_message_factory(master_key)
|
||||
|
||||
return {
|
||||
19788: mle_message_factory,
|
||||
|
||||
# TODO: Improve CoAP support
|
||||
61631: ipv6.UDPBytesPayloadFactory(),
|
||||
49152: ipv6.UDPBytesPayloadFactory(),
|
||||
49153: ipv6.UDPBytesPayloadFactory(),
|
||||
49154: ipv6.UDPBytesPayloadFactory()
|
||||
}
|
||||
|
||||
|
||||
def create_default_ipv6_icmp_body_factories():
|
||||
return {
|
||||
0: ipv6.ICMPv6DestinationUnreachableFactory(),
|
||||
128: ipv6.ICMPv6EchoBodyFactory(),
|
||||
129: ipv6.ICMPv6EchoBodyFactory()
|
||||
}
|
||||
|
||||
|
||||
def create_default_ipv6_upper_layer_factories(master_key):
|
||||
return {
|
||||
17: ipv6.UDPDatagramFactory(
|
||||
udp_header_factory=ipv6.UDPHeaderFactory(),
|
||||
dst_port_factories=create_default_ipv6_udp_dst_port_factories(master_key)
|
||||
),
|
||||
58: ipv6.ICMPv6Factory(
|
||||
body_factories=create_default_ipv6_icmp_body_factories()
|
||||
)
|
||||
}
|
||||
|
||||
|
||||
def create_default_lowpan_extension_headers_factories():
|
||||
return {
|
||||
0: lowpan.LowpanHopByHopFactory(
|
||||
hop_by_hop_options_factory=create_default_ipv6_hop_by_hop_options_factory()
|
||||
)
|
||||
}
|
||||
|
||||
|
||||
def create_default_ipv6_extension_headers_factories():
|
||||
return {
|
||||
0: ipv6.HopByHopFactory(
|
||||
hop_by_hop_options_factory=create_default_ipv6_hop_by_hop_options_factory())
|
||||
}
|
||||
|
||||
|
||||
def create_default_ipv6_packet_factory(master_key):
|
||||
return ipv6.IPv6PacketFactory(
|
||||
ehf=create_default_ipv6_extension_headers_factories(),
|
||||
ulpf=create_default_ipv6_upper_layer_factories(master_key)
|
||||
)
|
||||
|
||||
|
||||
def create_default_lowpan_decompressor(context_manager):
|
||||
return lowpan.LowpanDecompressor(
|
||||
lowpan_ip_header_factory=lowpan.LowpanIpv6HeaderFactory(
|
||||
context_manager=context_manager
|
||||
),
|
||||
lowpan_extension_headers_factory=lowpan.LowpanExtensionHeadersFactory(
|
||||
ext_headers_factories=create_default_lowpan_extension_headers_factories()
|
||||
),
|
||||
lowpan_udp_header_factory=lowpan.LowpanUdpHeaderFactory()
|
||||
)
|
||||
|
||||
|
||||
def create_default_thread_context_manager():
|
||||
context_manager = lowpan.ContextManager()
|
||||
context_manager[0] = lowpan.Context("fd00:0db8::/64")
|
||||
|
||||
return context_manager
|
||||
|
||||
|
||||
def create_default_lowpan_parser(context_manager, master_key=DEFAULT_MASTER_KEY):
|
||||
return lowpan.LowpanParser(
|
||||
lowpan_mesh_header_factory=lowpan.LowpanMeshHeaderFactory(),
|
||||
lowpan_decompressor=create_default_lowpan_decompressor(context_manager),
|
||||
lowpan_fragements_buffers_manager=lowpan.LowpanFragmentsBuffersManager(),
|
||||
ipv6_packet_factory=create_default_ipv6_packet_factory(master_key)
|
||||
)
|
||||
|
||||
|
||||
def create_default_thread_message_factory(master_key=DEFAULT_MASTER_KEY):
|
||||
context_manager = create_default_thread_context_manager()
|
||||
lowpan_parser = create_default_lowpan_parser(context_manager, master_key)
|
||||
|
||||
return message.MessageFactory(lowpan_parser=lowpan_parser)
|
||||
|
||||
|
||||
def create_default_thread_sniffer(nodeid):
|
||||
return sniffer.Sniffer(nodeid, create_default_thread_message_factory())
|
||||
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,263 @@
|
||||
#!/usr/bin/python
|
||||
#
|
||||
# Copyright (c) 2016, 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.
|
||||
#
|
||||
|
||||
"""
|
||||
This module provides simple 802.15.4 MAC parser.
|
||||
"""
|
||||
|
||||
import io
|
||||
import struct
|
||||
|
||||
import config
|
||||
from common import MacAddress, MessageInfo
|
||||
from net_crypto import AuxiliarySecurityHeader, CryptoEngine, MacCryptoMaterialCreator
|
||||
|
||||
|
||||
class DeviceDescriptors:
|
||||
|
||||
"""Class representing 802.15.4 Device Descriptors."""
|
||||
|
||||
device_descriptors = {}
|
||||
|
||||
@classmethod
|
||||
def add(cls, short_address, extended_address):
|
||||
short_address = cls._get_short_address_value(short_address)
|
||||
cls.device_descriptors[short_address] = extended_address
|
||||
|
||||
@classmethod
|
||||
def get_extended(cls, short_address):
|
||||
short_address = cls._get_short_address_value(short_address)
|
||||
return cls.device_descriptors[short_address]
|
||||
|
||||
@staticmethod
|
||||
def _get_short_address_value(short_address):
|
||||
if isinstance(short_address, MacAddress):
|
||||
short_address = short_address.rloc
|
||||
return short_address
|
||||
|
||||
|
||||
class MacHeader:
|
||||
|
||||
"""Class representing 802.15.4 MAC header."""
|
||||
|
||||
class FrameType:
|
||||
BEACON = 0
|
||||
DATA = 1
|
||||
ACK = 2
|
||||
COMMAND = 3
|
||||
|
||||
class AddressMode:
|
||||
NOT_PRESENT = 0
|
||||
SHORT = 2
|
||||
EXTENDED = 3
|
||||
|
||||
def __init__(self, frame_type, frame_pending, ack_request, frame_version, seq,
|
||||
dest_pan_id=None, dest_address=None, src_pan_id=None, src_address=None, command_type=None,
|
||||
aux_sec_header=None, mic=None,
|
||||
fcs=None):
|
||||
|
||||
self.frame_type = frame_type
|
||||
self.frame_pending = frame_pending
|
||||
self.ack_request = ack_request
|
||||
self.frame_version = frame_version
|
||||
self.seq = seq
|
||||
|
||||
self.dest_pan_id = dest_pan_id
|
||||
self.dest_address = dest_address
|
||||
self.src_pan_id = src_pan_id
|
||||
self.src_address = src_address
|
||||
|
||||
self.aux_sec_header = aux_sec_header
|
||||
self.mic = mic
|
||||
|
||||
self.fcs = fcs
|
||||
|
||||
|
||||
class MacPayload:
|
||||
|
||||
"""Class representing 802.15.4 MAC payload."""
|
||||
|
||||
def __init__(self, data):
|
||||
self.data = bytearray(data)
|
||||
|
||||
|
||||
class MacFrame:
|
||||
|
||||
"""Class representing 802.15.4 MAC frame."""
|
||||
|
||||
def parse(self, data):
|
||||
mhr_start = data.tell()
|
||||
|
||||
fc, seq = struct.unpack("<HB", data.read(3))
|
||||
|
||||
frame_type = fc & 0x0007
|
||||
security_enabled = bool(fc & 0x0008)
|
||||
frame_pending = bool(fc & 0x0010)
|
||||
ack_request = bool(fc & 0x0020)
|
||||
panid_compression = bool(fc & 0x0040)
|
||||
dest_addr_mode = (fc & 0x0c00) >> 10
|
||||
frame_version = (fc & 0x3000) >> 12
|
||||
source_addr_mode = (fc & 0xc000) >> 14
|
||||
|
||||
if frame_type == MacHeader.FrameType.ACK:
|
||||
fcs = self._parse_fcs(data, data.tell())
|
||||
self.header = MacHeader(frame_type, frame_pending, ack_request, frame_version, seq, fcs=fcs)
|
||||
self.payload = None
|
||||
return
|
||||
|
||||
# Presence of PAN Ids is not fully implemented yet but should be enough for Thread.
|
||||
dest_pan_id = struct.unpack("<H", data.read(2))[0]
|
||||
|
||||
dest_address = self._parse_address(data, dest_addr_mode)
|
||||
|
||||
if not panid_compression:
|
||||
src_pan_id = struct.unpack("<H", data.read(2))[0]
|
||||
else:
|
||||
src_pan_id = dest_pan_id
|
||||
|
||||
src_address = self._parse_address(data, source_addr_mode)
|
||||
|
||||
mhr_end = data.tell()
|
||||
|
||||
if security_enabled:
|
||||
aux_sec_header = self._parse_aux_sec_header(data)
|
||||
aux_sec_header_end = data.tell()
|
||||
else:
|
||||
aux_sec_header = None
|
||||
|
||||
# Check end of MAC frame
|
||||
if frame_type == MacHeader.FrameType.COMMAND:
|
||||
command_type = data.read(1)
|
||||
else:
|
||||
command_type = None
|
||||
|
||||
payload_pos = data.tell()
|
||||
|
||||
data.seek(-2, io.SEEK_END)
|
||||
fcs_start = data.tell()
|
||||
|
||||
if aux_sec_header and aux_sec_header.security_level:
|
||||
mic, payload_end = self._parse_mic(data, aux_sec_header.security_level)
|
||||
else:
|
||||
payload_end = data.tell()
|
||||
mic = None
|
||||
|
||||
fcs = self._parse_fcs(data, fcs_start)
|
||||
|
||||
# Create Header object
|
||||
self.header = MacHeader(frame_type, frame_pending, ack_request, frame_version, seq,
|
||||
dest_pan_id, dest_address, src_pan_id, src_address, command_type,
|
||||
aux_sec_header, mic,
|
||||
fcs)
|
||||
|
||||
# Create Payload object
|
||||
payload_len = payload_end - payload_pos
|
||||
data.seek(payload_pos)
|
||||
|
||||
payload = data.read(payload_len)
|
||||
|
||||
if security_enabled:
|
||||
mhr_len = mhr_end - mhr_start
|
||||
data.seek(mhr_start)
|
||||
mhr_bytes = data.read(mhr_len)
|
||||
|
||||
aux_sec_header_len = aux_sec_header_end - mhr_end
|
||||
aux_sec_hdr_bytes = data.read(aux_sec_header_len)
|
||||
|
||||
non_payload_fields = bytearray([])
|
||||
|
||||
if command_type is not None:
|
||||
non_payload_fields.append(command_type)
|
||||
|
||||
message_info = MessageInfo()
|
||||
message_info.aux_sec_hdr = aux_sec_header
|
||||
message_info.aux_sec_hdr_bytes = aux_sec_hdr_bytes
|
||||
message_info.nonpayload_fields = non_payload_fields
|
||||
message_info.mhr_bytes = mhr_bytes
|
||||
if src_address.type == MacAddress.SHORT:
|
||||
message_info.source_mac_address = DeviceDescriptors.get_extended(src_address).mac_address
|
||||
else:
|
||||
message_info.source_mac_address = src_address.mac_address
|
||||
|
||||
sec_obj = CryptoEngine(MacCryptoMaterialCreator(config.DEFAULT_MASTER_KEY))
|
||||
self.payload = MacPayload(sec_obj.decrypt(payload, mic, message_info))
|
||||
|
||||
else:
|
||||
self.payload = MacPayload(payload)
|
||||
|
||||
def _parse_address(self, data, mode):
|
||||
if mode == MacHeader.AddressMode.SHORT:
|
||||
return MacAddress(data.read(2), MacAddress.SHORT, big_endian=False)
|
||||
|
||||
if mode == MacHeader.AddressMode.EXTENDED:
|
||||
return MacAddress(data.read(8), MacAddress.LONG, big_endian=False)
|
||||
|
||||
else:
|
||||
return None
|
||||
|
||||
def _parse_aux_sec_header(self, data):
|
||||
security_control, frame_counter = struct.unpack("<BL", data.read(5))
|
||||
|
||||
security_level = security_control & 0x07
|
||||
key_id_mode = (security_control & 0x18) >> 3
|
||||
|
||||
if key_id_mode == 1:
|
||||
key_id = data.read(1)
|
||||
elif key_id_mode == 2:
|
||||
key_id = data.read(5)
|
||||
elif key_id_mode == 3:
|
||||
key_id = data.read(9)
|
||||
else:
|
||||
key_source = None
|
||||
key_index = None
|
||||
|
||||
return AuxiliarySecurityHeader(key_id_mode, security_level, frame_counter, key_id, big_endian=False)
|
||||
|
||||
def _parse_mic(self, data, security_level):
|
||||
if security_level in (1, 5):
|
||||
data.seek(-4, io.SEEK_CUR)
|
||||
payload_end = data.tell()
|
||||
mic = data.read(4)
|
||||
elif security_level in (2, 6):
|
||||
data.seek(-8, io.SEEK_CUR)
|
||||
payload_end = data.tell()
|
||||
mic = data.read(8)
|
||||
elif security_level in (3, 7):
|
||||
data.seek(-16, io.SEEK_CUR)
|
||||
payload_end = data.tell()
|
||||
mic = data.read(16)
|
||||
else:
|
||||
payload_end = data.tell()
|
||||
|
||||
return mic, payload_end
|
||||
|
||||
def _parse_fcs(self, data, fcs_start):
|
||||
data.seek(fcs_start)
|
||||
fcs = bytearray(data.read(2))
|
||||
return fcs
|
||||
@@ -0,0 +1,301 @@
|
||||
#!/usr/bin/python
|
||||
#
|
||||
# Copyright (c) 2016, 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.
|
||||
#
|
||||
|
||||
import io
|
||||
import ipaddress
|
||||
import struct
|
||||
|
||||
import common
|
||||
import ipv6
|
||||
import lowpan
|
||||
import mac802154
|
||||
import mle
|
||||
|
||||
|
||||
MessageType = common.enum("MLE",
|
||||
"COAP",
|
||||
"ICMP",
|
||||
"ACK",
|
||||
"BEACON",
|
||||
"DATA")
|
||||
|
||||
|
||||
class Message(object):
|
||||
|
||||
def __init__(self):
|
||||
self._type = None
|
||||
self._channel = None
|
||||
self._mac_header = None
|
||||
self._ipv6_packet = None
|
||||
self._mle = None
|
||||
self._icmp = None
|
||||
|
||||
def _extract_udp_datagram(self, udp_datagram):
|
||||
if isinstance(udp_datagram.payload, mle.MleMessage):
|
||||
self._type = MessageType.MLE
|
||||
self._mle = udp_datagram.payload
|
||||
|
||||
# TODO: Initial version doesn't support CoAP
|
||||
elif isinstance(udp_datagram.payload, ipv6.UDPBytesPayload):
|
||||
self._type = MessageType.COAP
|
||||
self._coap = udp_datagram.payload
|
||||
|
||||
def _extract_upper_layer_protocol(self, upper_layer_protocol):
|
||||
if isinstance(upper_layer_protocol, ipv6.ICMPv6):
|
||||
self._type = MessageType.ICMP
|
||||
self._icmp = upper_layer_protocol
|
||||
|
||||
elif isinstance(upper_layer_protocol, ipv6.UDPDatagram):
|
||||
self._extract_udp_datagram(upper_layer_protocol)
|
||||
|
||||
@property
|
||||
def type(self):
|
||||
return self._type
|
||||
|
||||
@type.setter
|
||||
def type(self, value):
|
||||
self._type = value
|
||||
|
||||
@property
|
||||
def channel(self):
|
||||
return self._channel
|
||||
|
||||
@channel.setter
|
||||
def channel(self, value):
|
||||
self._channel = value
|
||||
|
||||
@property
|
||||
def mac_header(self):
|
||||
return self._mac_header
|
||||
|
||||
@mac_header.setter
|
||||
def mac_header(self, value):
|
||||
self._mac_header = value
|
||||
|
||||
if self._mac_header.frame_type == mac802154.MacHeader.FrameType.BEACON:
|
||||
self._type = MessageType.BEACON
|
||||
|
||||
elif self._mac_header.frame_type == mac802154.MacHeader.FrameType.ACK:
|
||||
self._type = MessageType.ACK
|
||||
|
||||
elif self._mac_header.frame_type == mac802154.MacHeader.FrameType.DATA:
|
||||
self._type = MessageType.DATA
|
||||
|
||||
@property
|
||||
def ipv6_packet(self):
|
||||
return self._ipv6_packet
|
||||
|
||||
@ipv6_packet.setter
|
||||
def ipv6_packet(self, value):
|
||||
self._ipv6_packet = value
|
||||
self._extract_upper_layer_protocol(value.upper_layer_protocol)
|
||||
|
||||
@property
|
||||
def mle(self):
|
||||
return self._mle
|
||||
|
||||
@mle.setter
|
||||
def mle(self, value):
|
||||
self._mle = value
|
||||
|
||||
@property
|
||||
def icmp(self):
|
||||
return self._icmp
|
||||
|
||||
@icmp.setter
|
||||
def icmp(self, value):
|
||||
self._icmp = value
|
||||
|
||||
def get_mle_message_tlv(self, tlv_class_type):
|
||||
if self.type != MessageType.MLE:
|
||||
raise ValueError("Invalid message type. Expected MLE message.")
|
||||
|
||||
for tlv in self.mle.command.tlvs:
|
||||
if isinstance(tlv, tlv_class_type):
|
||||
return tlv
|
||||
|
||||
def assertMleMessageIsType(self, command_type):
|
||||
if self.type != MessageType.MLE:
|
||||
raise ValueError("Invalid message type. Expected MLE message.")
|
||||
|
||||
assert(self.mle.command.type == command_type)
|
||||
|
||||
def assertMleMessageContainsTlv(self, tlv_class_type):
|
||||
if self.type != MessageType.MLE:
|
||||
raise ValueError("Invalid message type. Expected MLE message.")
|
||||
|
||||
contains_tlv = False
|
||||
for tlv in self.mle.command.tlvs:
|
||||
if isinstance(tlv, tlv_class_type):
|
||||
contains_tlv = True
|
||||
break
|
||||
|
||||
assert(contains_tlv == True)
|
||||
|
||||
def assertMleMessageDoesNotContainTlv(self, tlv_class_type):
|
||||
if self.type != MessageType.MLE:
|
||||
raise ValueError("Invalid message type. Expected MLE message.")
|
||||
|
||||
contains_tlv = False
|
||||
for tlv in self.mle.command.tlvs:
|
||||
if isinstance(tlv, tlv_class_type):
|
||||
contains_tlv = True
|
||||
break
|
||||
|
||||
assert(contains_tlv == False)
|
||||
|
||||
def assertMleMessageContainsOptionalTlv(self, tlv_class_type):
|
||||
if self.type != MessageType.MLE:
|
||||
raise ValueError("Invalid message type. Expected MLE message.")
|
||||
|
||||
contains_tlv = False
|
||||
for tlv in self.mle.command.tlvs:
|
||||
if isinstance(tlv, tlv_class_type):
|
||||
contains_tlv = True
|
||||
break
|
||||
|
||||
print("MleMessage doesn't contain optional TLV: {}".format(tlv_class_type))
|
||||
|
||||
def assertSentToNode(self, node):
|
||||
sent_to_node = False
|
||||
dst_addr = self.ipv6_packet.ipv6_header.destination_address
|
||||
|
||||
for addr in node.get_addrs():
|
||||
if dst_addr == ipaddress.ip_address(addr):
|
||||
sent_to_node = True
|
||||
|
||||
assert sent_to_node == True
|
||||
|
||||
def assertSentToDestinationAddress(self, ipv6_address):
|
||||
assert self.ipv6_packet.ipv6_header.destination_address == ipaddress.ip_address(unicode(ipv6_address))
|
||||
|
||||
def assertSentWithHopLimit(self, hop_limit):
|
||||
assert self.ipv6_packet.ipv6_header.hop_limit == hop_limit
|
||||
|
||||
def __repr__(self):
|
||||
return "Message(type={})".format(MessageType.name[self.type])
|
||||
|
||||
|
||||
class MessagesSet(object):
|
||||
|
||||
def __init__(self, messages):
|
||||
self._messages = messages
|
||||
|
||||
@property
|
||||
def messages(self):
|
||||
return self._messages
|
||||
|
||||
def next_mle_message(self, command_type, assert_enabled=True):
|
||||
message = self.next_mle_message_of_one_of_command_types(command_type,)
|
||||
|
||||
if assert_enabled:
|
||||
assert message is not None, "Could not find MleMessage of the type: {}".format(command_type)
|
||||
|
||||
return message
|
||||
|
||||
def next_mle_message_of_one_of_command_types(self, *command_types):
|
||||
message = None
|
||||
|
||||
while self.messages:
|
||||
m = self.messages.pop(0)
|
||||
|
||||
if m.type != MessageType.MLE:
|
||||
continue
|
||||
|
||||
command_found = False
|
||||
|
||||
for command_type in command_types:
|
||||
if m.mle.command.type == command_type:
|
||||
command_found = True
|
||||
break
|
||||
|
||||
if command_found:
|
||||
message = m
|
||||
break
|
||||
|
||||
return message
|
||||
|
||||
def contains_mle_message(self, command_type):
|
||||
for m in self.messages:
|
||||
if m.type != MessageType.MLE:
|
||||
continue
|
||||
|
||||
if m.mle.command.type == command_type:
|
||||
return True
|
||||
|
||||
return False
|
||||
|
||||
|
||||
class MessageFactory:
|
||||
|
||||
def __init__(self, lowpan_parser):
|
||||
self._lowpan_parser = lowpan_parser
|
||||
|
||||
def _add_device_descriptors(self, message):
|
||||
for tlv in message.mle.command.tlvs:
|
||||
|
||||
if isinstance(tlv, mle.SourceAddress):
|
||||
mac802154.DeviceDescriptors.add(tlv.address, message.mac_header.src_address)
|
||||
|
||||
if isinstance(tlv, mle.Address16):
|
||||
mac802154.DeviceDescriptors.add(tlv.address, message.mac_header.dest_address)
|
||||
|
||||
def _parse_mac_frame(self, data):
|
||||
mac_frame = mac802154.MacFrame()
|
||||
mac_frame.parse(data)
|
||||
return mac_frame
|
||||
|
||||
def create(self, data):
|
||||
message = Message()
|
||||
message.channel = struct.unpack(">B", data.read(1))
|
||||
|
||||
# Parse MAC header
|
||||
mac_frame = self._parse_mac_frame(data)
|
||||
message.mac_header = mac_frame.header
|
||||
|
||||
if message.mac_header.frame_type != mac802154.MacHeader.FrameType.DATA:
|
||||
return message
|
||||
|
||||
message_info = common.MessageInfo()
|
||||
message_info.source_mac_address = message.mac_header.src_address
|
||||
message_info.destination_mac_address = message.mac_header.dest_address
|
||||
|
||||
# Create stream with 6LoWPAN datagram
|
||||
lowpan_payload = io.BytesIO(mac_frame.payload.data)
|
||||
|
||||
ipv6_packet = self._lowpan_parser.parse(lowpan_payload, message_info)
|
||||
if ipv6_packet is None:
|
||||
return message
|
||||
|
||||
message.ipv6_packet = ipv6_packet
|
||||
|
||||
if message.type == MessageType.MLE:
|
||||
self._add_device_descriptors(message)
|
||||
|
||||
return message
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,359 @@
|
||||
#!/usr/bin/python
|
||||
#
|
||||
# Copyright (c) 2016, 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.
|
||||
#
|
||||
|
||||
import hmac
|
||||
import hashlib
|
||||
import struct
|
||||
|
||||
from binascii import hexlify
|
||||
|
||||
from Crypto.Cipher import AES
|
||||
|
||||
|
||||
class CryptoEngine:
|
||||
|
||||
""" Class responsible for encryption and decryption of data. """
|
||||
|
||||
def __init__(self, crypto_material_creator):
|
||||
"""
|
||||
Args:
|
||||
master_key (bytearray)
|
||||
|
||||
"""
|
||||
self._crypto_material_creator = crypto_material_creator
|
||||
|
||||
@property
|
||||
def mic_length(self):
|
||||
return self._crypto_material_creator.mic_length
|
||||
|
||||
def encrypt(self, data, message_info):
|
||||
""" Encrypt message.
|
||||
|
||||
Args:
|
||||
data (bytearray)
|
||||
message_info (MessageInfo)
|
||||
|
||||
Returns:
|
||||
tuple: Encrypted message (bytearray), MIC (bytearray)
|
||||
|
||||
"""
|
||||
key, nonce, auth_data = self._crypto_material_creator.create_key_and_nonce_and_authenticated_data(message_info)
|
||||
|
||||
cipher = AES.new(key, AES.MODE_CCM, nonce, mac_len=self.mic_length)
|
||||
cipher.update(auth_data)
|
||||
|
||||
return cipher.encrypt_and_digest(bytes(data))
|
||||
|
||||
def decrypt(self, enc_data, mic, message_info):
|
||||
""" Decrypt MLE message.
|
||||
|
||||
Args:
|
||||
enc_data (bytearray)
|
||||
mic (bytearray)
|
||||
message_info (MessageInfo)
|
||||
|
||||
Returns:
|
||||
bytearray: Decrypted message.
|
||||
|
||||
"""
|
||||
key, nonce, auth_data = self._crypto_material_creator.create_key_and_nonce_and_authenticated_data(message_info)
|
||||
|
||||
cipher = AES.new(key, AES.MODE_CCM, nonce, mac_len=self.mic_length)
|
||||
cipher.update(auth_data)
|
||||
|
||||
dec_data = cipher.decrypt_and_verify(bytes(enc_data), bytes(mic))
|
||||
return bytearray(dec_data)
|
||||
|
||||
|
||||
class CryptoMaterialCreator(object):
|
||||
|
||||
_salt = b'Thread'
|
||||
|
||||
def __init__(self, master_key):
|
||||
"""
|
||||
Args:
|
||||
master_key (bytearray)
|
||||
|
||||
"""
|
||||
self.master_key = master_key
|
||||
|
||||
def _generate_keys(self, sequence_counter):
|
||||
""" Generate MLE and MAC keys.
|
||||
|
||||
Read more: 7.1.4. Key Generation - Thread v1.1 Specification Final
|
||||
|
||||
Args:
|
||||
sequence_counter (int)
|
||||
|
||||
Returns:
|
||||
tuple: MLE and MAC as bytes
|
||||
|
||||
"""
|
||||
k = self.master_key
|
||||
s = struct.pack(">L", sequence_counter) + self._salt
|
||||
d = hmac.new(k, s, digestmod=hashlib.sha256).digest()
|
||||
|
||||
mle = d[:16]
|
||||
mac = d[16:]
|
||||
return mle, mac
|
||||
|
||||
def create_key_and_nonce_and_authenticated_data(self, message_info):
|
||||
raise NotImplementedError
|
||||
|
||||
@property
|
||||
def mic_length(self):
|
||||
raise NotImplementedError
|
||||
|
||||
|
||||
class MacCryptoMaterialCreator(CryptoMaterialCreator):
|
||||
|
||||
def __init__(self, master_key):
|
||||
"""
|
||||
Args:
|
||||
master_key (bytearray)
|
||||
|
||||
"""
|
||||
super(MacCryptoMaterialCreator, self).__init__(master_key)
|
||||
|
||||
def _create_nonce(self, eui64, frame_counter, security_level):
|
||||
""" Create CCM Nonce required by AES-128 CCM for encryption and decryption.
|
||||
|
||||
Read more: 7.6.3.2 CCM Nonce - Std 802.15.4-2006
|
||||
|
||||
Args:
|
||||
eui64 (bytes)
|
||||
frame_counter (int)
|
||||
security_level (int)
|
||||
|
||||
Returns:
|
||||
bytes: created Nonce
|
||||
|
||||
"""
|
||||
return bytes(eui64 + struct.pack(">LB", frame_counter, security_level))
|
||||
|
||||
def _create_authenticated_data(self, mhr, auxiliary_security_header, nonpayload_fields):
|
||||
""" Create Authenticated Data
|
||||
|
||||
Read more: 7.6.3.3 CCM prerequisites - Std 802.15.4-2006
|
||||
|
||||
Args:
|
||||
mhr (bytes)
|
||||
auxiliary_security_header (bytes)
|
||||
nonpayload_fields (bytes)
|
||||
|
||||
Returns:
|
||||
bytes: Authenticated Data
|
||||
|
||||
"""
|
||||
return bytes(mhr + auxiliary_security_header + nonpayload_fields)
|
||||
|
||||
def create_key_and_nonce_and_authenticated_data(self, message_info):
|
||||
_, mac_key = self._generate_keys(message_info.aux_sec_hdr.sequence_counter)
|
||||
|
||||
nonce = self._create_nonce(message_info.source_mac_address,
|
||||
message_info.aux_sec_hdr.frame_counter,
|
||||
message_info.aux_sec_hdr.security_level)
|
||||
|
||||
auth_data = self._create_authenticated_data(message_info.mhr_bytes,
|
||||
message_info.aux_sec_hdr_bytes,
|
||||
message_info.nonpayload_fields)
|
||||
|
||||
return mac_key, nonce, auth_data
|
||||
|
||||
@property
|
||||
def mic_length(self):
|
||||
return 4
|
||||
|
||||
|
||||
class MleCryptoMaterialCreator(CryptoMaterialCreator):
|
||||
|
||||
def __init__(self, master_key):
|
||||
"""
|
||||
Args:
|
||||
master_key (bytearray)
|
||||
|
||||
"""
|
||||
super(MleCryptoMaterialCreator, self).__init__(master_key)
|
||||
|
||||
def _create_nonce(self, source_eui64, frame_counter, security_level):
|
||||
""" Create CCM Nonce required by AES-128 CCM for encryption and decryption.
|
||||
|
||||
Read more: 7.6.3.2 CCM Nonce - Std 802.15.4-2006
|
||||
|
||||
Args:
|
||||
eui64 (bytearray)
|
||||
frame_counter (int)
|
||||
security_level (int)
|
||||
|
||||
Returns:
|
||||
bytes: created Nonce
|
||||
|
||||
"""
|
||||
return bytes(source_eui64[:8] + struct.pack(">LB", frame_counter, security_level))
|
||||
|
||||
def _create_authenticated_data(self, source_address, destination_address, auxiliary_security_header):
|
||||
""" Create Authenticated Data
|
||||
|
||||
Read more: 4.8 - Thread v1.0 Specification
|
||||
|
||||
Args:
|
||||
source_address (ip_address)
|
||||
destination_address (ip_address)
|
||||
auxiliary_security_header (bytearray)
|
||||
|
||||
Returns:
|
||||
bytes: Authenticated Data
|
||||
|
||||
"""
|
||||
return bytes(source_address.packed + destination_address.packed + auxiliary_security_header)
|
||||
|
||||
def create_key_and_nonce_and_authenticated_data(self, message_info):
|
||||
mle_key, _ = self._generate_keys(message_info.aux_sec_hdr.sequence_counter)
|
||||
|
||||
nonce = self._create_nonce(message_info.source_mac_address.mac_address,
|
||||
message_info.aux_sec_hdr.frame_counter,
|
||||
message_info.aux_sec_hdr.security_level)
|
||||
|
||||
auth_data = self._create_authenticated_data(message_info.source_ipv6,
|
||||
message_info.destination_ipv6,
|
||||
message_info.aux_sec_hdr_bytes)
|
||||
|
||||
return mle_key, nonce, auth_data
|
||||
|
||||
@property
|
||||
def mic_length(self):
|
||||
return 4
|
||||
|
||||
|
||||
class AuxiliarySecurityHeader:
|
||||
|
||||
def __init__(self, key_id_mode, security_level, frame_counter, key_id, big_endian=True):
|
||||
"""
|
||||
Args:
|
||||
key_id_mode (int)
|
||||
security_level (int)
|
||||
frame_counter (int)
|
||||
key_id (bytearray)
|
||||
"""
|
||||
self._key_id_mode = key_id_mode
|
||||
self._security_level = security_level
|
||||
self._frame_counter = frame_counter
|
||||
self._key_id = key_id
|
||||
self._big_endian = big_endian
|
||||
|
||||
@property
|
||||
def sequence_counter(self):
|
||||
""" Compute or extract sequence counter based on currently set Key Index Mode. """
|
||||
|
||||
if self.key_id_mode == 1:
|
||||
# Try to guess valid Key Sequence Counter based on Key Index. This one should work for now.
|
||||
return self.key_index - 1
|
||||
elif self.key_id_mode == 2:
|
||||
# In this mode sequence counter is stored on the first four bytes of Key ID.
|
||||
key_source = self.key_id[:4]
|
||||
format = ">I" if self._big_endian else "<I"
|
||||
elif self.key_id_mode == 3:
|
||||
key_source = self.key_id[:8]
|
||||
format = ">Q" if self._big_endian else "<Q"
|
||||
|
||||
else:
|
||||
raise ValueError("Unsupported Key Index Mode: {}".format(self.key_id_mode))
|
||||
|
||||
return struct.unpack(format, key_source)[0]
|
||||
|
||||
@property
|
||||
def key_index(self):
|
||||
return struct.unpack(">B", self.key_id[-1:])[0]
|
||||
|
||||
@property
|
||||
def key_id_mode(self):
|
||||
return self._key_id_mode
|
||||
|
||||
@property
|
||||
def security_level(self):
|
||||
return self._security_level
|
||||
|
||||
@property
|
||||
def frame_counter(self):
|
||||
return self._frame_counter
|
||||
|
||||
@property
|
||||
def key_id(self):
|
||||
return self._key_id
|
||||
|
||||
def __repr__(self):
|
||||
return "AuxiliarySecurityHeader(key_id_mode={}, security_level={}, frame_counter={}, key_id={})".format(
|
||||
self.key_id_mode, self.security_level, self.frame_counter, hexlify(self.key_id))
|
||||
|
||||
|
||||
class AuxiliarySecurityHeaderFactory:
|
||||
|
||||
_SECURITY_CONTROL_LENGTH = 1
|
||||
_FRAME_COUNTER_LENGTH = 4
|
||||
|
||||
_KEY_ID_LENGTH_KEY_ID_0 = 0
|
||||
_KEY_ID_LENGTH_KEY_ID_1 = 1
|
||||
_KEY_ID_LENGTH_KEY_ID_2 = 5
|
||||
_KEY_ID_LENGTH_KEY_ID_3 = 9
|
||||
|
||||
_key_id_lengths = {
|
||||
0: _KEY_ID_LENGTH_KEY_ID_0,
|
||||
1: _KEY_ID_LENGTH_KEY_ID_1,
|
||||
2: _KEY_ID_LENGTH_KEY_ID_2,
|
||||
3: _KEY_ID_LENGTH_KEY_ID_3
|
||||
}
|
||||
|
||||
def _parse_security_control(self, security_control_byte):
|
||||
security_level = (security_control_byte & 0x07)
|
||||
key_id_mode = (security_control_byte >> 3) & 0x03
|
||||
|
||||
return security_level, key_id_mode
|
||||
|
||||
def _parse_frame_counter(self, frame_counter_bytes):
|
||||
return struct.unpack("<I", frame_counter_bytes)[0]
|
||||
|
||||
def _key_id_length(self, key_id_mode):
|
||||
return self._key_id_lengths[key_id_mode]
|
||||
|
||||
def parse(self, data, message_info):
|
||||
security_control_bytes = bytearray(data.read(self._SECURITY_CONTROL_LENGTH))
|
||||
frame_counter_bytes = bytearray(data.read(self._FRAME_COUNTER_LENGTH))
|
||||
|
||||
security_level, key_id_mode = self._parse_security_control(security_control_bytes[0])
|
||||
frame_counter = self._parse_frame_counter(frame_counter_bytes)
|
||||
|
||||
key_id_length = self._key_id_length(key_id_mode)
|
||||
key_id_bytes = bytearray(data.read(key_id_length))
|
||||
|
||||
aux_sec_hdr = AuxiliarySecurityHeader(key_id_mode, security_level, frame_counter, key_id_bytes)
|
||||
|
||||
message_info.aux_sec_hdr_bytes = security_control_bytes + frame_counter_bytes + key_id_bytes
|
||||
message_info.aux_sec_hdr = aux_sec_hdr
|
||||
|
||||
return aux_sec_hdr
|
||||
@@ -0,0 +1,494 @@
|
||||
#!/usr/bin/python
|
||||
#
|
||||
# Copyright (c) 2016, 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.
|
||||
#
|
||||
|
||||
import io
|
||||
import math
|
||||
import struct
|
||||
|
||||
from binascii import hexlify
|
||||
|
||||
|
||||
class NetworkData(object):
|
||||
|
||||
def __init__(self, stable):
|
||||
self._stable = stable
|
||||
|
||||
@property
|
||||
def stable(self):
|
||||
return self._stable
|
||||
|
||||
|
||||
class SubTlvsFactory(object):
|
||||
|
||||
def __init__(self, sub_tlvs_factories):
|
||||
self._sub_tlvs_factories = sub_tlvs_factories
|
||||
|
||||
def _get_factory(self, _type):
|
||||
try:
|
||||
return self._sub_tlvs_factories[_type]
|
||||
except KeyError:
|
||||
raise RuntimeError("Could not find factory. Factory type = {}.".format(_type))
|
||||
|
||||
def parse(self, data, message_info):
|
||||
sub_tlvs = []
|
||||
|
||||
while data.tell() < len(data.getvalue()):
|
||||
data_byte = ord(data.read(1))
|
||||
|
||||
stable = data_byte & 0x01
|
||||
_type = (data_byte >> 1) & 0x7f
|
||||
|
||||
length = ord(data.read(1))
|
||||
value = data.read(length)
|
||||
|
||||
factory = self._get_factory(_type)
|
||||
|
||||
message_info.stable = stable
|
||||
tlv = factory.parse(io.BytesIO(value), message_info)
|
||||
|
||||
sub_tlvs.append(tlv)
|
||||
|
||||
return sub_tlvs
|
||||
|
||||
|
||||
class Route(object):
|
||||
|
||||
def __init__(self, border_router_16, prf):
|
||||
self._border_router_16 = border_router_16
|
||||
self._prf = prf
|
||||
|
||||
@property
|
||||
def border_router_16(self):
|
||||
return self._border_router_16
|
||||
|
||||
@property
|
||||
def prf(self):
|
||||
return self._prf
|
||||
|
||||
def __eq__(self, other):
|
||||
if not isinstance(other, self.__class__):
|
||||
raise TypeError("Could not compare {} and {}".format(type(self), type(other)))
|
||||
|
||||
return self.border_router_16 == other.border_router_16 and self.prf == other.prf
|
||||
|
||||
def __repr__(self):
|
||||
return "Route(border_router_16={}, prf={})".format(self.border_router_16, self.prf)
|
||||
|
||||
|
||||
class RouteFactory:
|
||||
|
||||
def parse(self, data, message_info):
|
||||
border_router_16 = struct.unpack(">H", data.read(2))[0]
|
||||
|
||||
data_byte = ord(data.read(1))
|
||||
prf = (data_byte >> 6) & 0x03
|
||||
|
||||
return Route(border_router_16, prf)
|
||||
|
||||
|
||||
class RoutesFactory:
|
||||
|
||||
def __init__(self, route_factory):
|
||||
self._route_factory = route_factory
|
||||
|
||||
def parse(self, data, message_info):
|
||||
routes = []
|
||||
|
||||
while data.tell() < len(data.getvalue()):
|
||||
route = self._route_factory.parse(data, message_info)
|
||||
|
||||
routes.append(route)
|
||||
|
||||
return routes
|
||||
|
||||
|
||||
class HasRoute(NetworkData):
|
||||
|
||||
def __init__(self, routes, stable):
|
||||
super(HasRoute, self).__init__(stable)
|
||||
self._routes = routes
|
||||
|
||||
@property
|
||||
def routes(self):
|
||||
return self._routes
|
||||
|
||||
def __eq__(self, other):
|
||||
if not isinstance(other, self.__class__):
|
||||
raise TypeError("Could not compare {} and {}".format(type(self), type(other)))
|
||||
|
||||
return self.routes == other.routes
|
||||
|
||||
def __repr__(self):
|
||||
routes_str = ", ".join(["{}".format(route) for route in self.routes])
|
||||
return "HasRoute(stable={}, routes=[{}])".format(self.stable, routes_str)
|
||||
|
||||
|
||||
class HasRouteFactory:
|
||||
|
||||
def __init__(self, routes_factory):
|
||||
self._routes_factory = routes_factory
|
||||
|
||||
def parse(self, data, message_info):
|
||||
routes = self._routes_factory.parse(data, message_info)
|
||||
|
||||
return HasRoute(routes, message_info.stable)
|
||||
|
||||
|
||||
class Prefix(NetworkData):
|
||||
|
||||
def __init__(self, domain_id, prefix_length, prefix, sub_tlvs, stable):
|
||||
super(Prefix, self).__init__(stable)
|
||||
self._domain_id = domain_id
|
||||
self._prefix_length = prefix_length
|
||||
self._prefix = prefix
|
||||
self._sub_tlvs = sub_tlvs
|
||||
|
||||
@property
|
||||
def domain_id(self):
|
||||
return self._domain_id
|
||||
|
||||
@property
|
||||
def prefix_length(self):
|
||||
return self._prefix_length
|
||||
|
||||
@property
|
||||
def prefix(self):
|
||||
return self._prefix
|
||||
|
||||
@property
|
||||
def sub_tlvs(self):
|
||||
return self._sub_tlvs
|
||||
|
||||
def __eq__(self, other):
|
||||
if not isinstance(other, self.__class__):
|
||||
raise TypeError("Could not compare {} and {}".format(type(self), type(other)))
|
||||
|
||||
return self.domain_id == other.domain_id and \
|
||||
self.prefix_length == other.prefix_length and \
|
||||
self.prefix == other.prefix and \
|
||||
self.sub_tlvs == other.sub_tlvs
|
||||
|
||||
def __repr__(self):
|
||||
sub_tlvs_str = ", ".join(["{}".format(tlv) for tlv in self.sub_tlvs])
|
||||
return "Prefix(stable={}, domain_id={}, prefix_length={}, prefix={}, sub_tlvs=[{}])".format(
|
||||
self.stable, self.domain_id, self.prefix_length, hexlify(self.prefix), sub_tlvs_str)
|
||||
|
||||
|
||||
class PrefixSubTlvsFactory(SubTlvsFactory):
|
||||
|
||||
def __init__(self, sub_tlvs_factories):
|
||||
super(PrefixSubTlvsFactory, self).__init__(sub_tlvs_factories)
|
||||
|
||||
|
||||
class PrefixFactory:
|
||||
|
||||
def __init__(self, sub_tlvs_factory):
|
||||
self._sub_tlvs_factory = sub_tlvs_factory
|
||||
|
||||
def _bits_to_bytes(self, bits):
|
||||
return int(math.ceil(bits / 8))
|
||||
|
||||
def parse(self, data, message_info):
|
||||
domain_id = ord(data.read(1))
|
||||
|
||||
prefix_length = ord(data.read(1))
|
||||
|
||||
prefix = bytearray(data.read(self._bits_to_bytes(prefix_length)))
|
||||
|
||||
sub_tlvs = self._sub_tlvs_factory.parse(io.BytesIO(data.read()), message_info)
|
||||
|
||||
return Prefix(domain_id, prefix_length, prefix, sub_tlvs, message_info.stable)
|
||||
|
||||
|
||||
class BorderRouter(NetworkData):
|
||||
|
||||
def __init__(self, border_router_16, prf, p, s, d, c, r, o, n, stable):
|
||||
super(BorderRouter, self).__init__(stable)
|
||||
self._border_router_16 = border_router_16
|
||||
self._prf = prf
|
||||
self._p = p
|
||||
self._s = s
|
||||
self._d = d
|
||||
self._c = c
|
||||
self._r = r
|
||||
self._o = o
|
||||
self._n = n
|
||||
|
||||
@property
|
||||
def border_router_16(self):
|
||||
return self._border_router_16
|
||||
|
||||
@property
|
||||
def prf(self):
|
||||
return self._prf
|
||||
|
||||
@property
|
||||
def p(self):
|
||||
return self._p
|
||||
|
||||
@property
|
||||
def s(self):
|
||||
return self._s
|
||||
|
||||
@property
|
||||
def d(self):
|
||||
return self._d
|
||||
|
||||
@property
|
||||
def c(self):
|
||||
return self._c
|
||||
|
||||
@property
|
||||
def r(self):
|
||||
return self._r
|
||||
|
||||
@property
|
||||
def o(self):
|
||||
return self._o
|
||||
|
||||
@property
|
||||
def n(self):
|
||||
return self._n
|
||||
|
||||
def __eq__(self, other):
|
||||
if not isinstance(other, self.__class__):
|
||||
raise TypeError("Could not compare {} and {}".format(type(self), type(other)))
|
||||
|
||||
return self.border_router_16 == other.border_router_16 and \
|
||||
self.prf == other.prf and \
|
||||
self.p == other.p and \
|
||||
self.s == other.s and \
|
||||
self.d == other.d and \
|
||||
self.c == other.c and \
|
||||
self.r == other.r and \
|
||||
self.o == other.o and \
|
||||
self.n == other.n
|
||||
|
||||
def __repr__(self):
|
||||
return "BorderRouter(stable={}, border_router_16={}, prf={}, p={}, s={}, d={}, c={}, r={}, o={}, n={})".format(
|
||||
self.stable, self.border_router_16, self.prf, self.p, self.s, self.d, self.c, self.r, self.o, self.n)
|
||||
|
||||
|
||||
class BorderRouterFactory:
|
||||
|
||||
def parse(self, data, message_info):
|
||||
border_router_16 = struct.unpack(">H", data.read(2))[0]
|
||||
|
||||
data_byte = ord(data.read(1))
|
||||
o = (data_byte & 0x01)
|
||||
r = (data_byte >> 1) & 0x01
|
||||
c = (data_byte >> 2) & 0x01
|
||||
d = (data_byte >> 3) & 0x01
|
||||
s = (data_byte >> 4) & 0x01
|
||||
p = (data_byte >> 5) & 0x01
|
||||
prf = (data_byte >> 6) & 0x03
|
||||
|
||||
data_byte = ord(data.read(1))
|
||||
n = (data_byte >> 7) & 0x01
|
||||
|
||||
return BorderRouter(border_router_16, prf, p, s, d, c, r, o, n, message_info.stable)
|
||||
|
||||
|
||||
class LowpanId(NetworkData):
|
||||
|
||||
def __init__(self, c, cid, context_length, stable):
|
||||
super(LowpanId, self).__init__(stable)
|
||||
self._c = c
|
||||
self._cid = cid
|
||||
self._context_length = context_length
|
||||
|
||||
@property
|
||||
def c(self):
|
||||
return self._c
|
||||
|
||||
@property
|
||||
def cid(self):
|
||||
return self._cid
|
||||
|
||||
@property
|
||||
def context_length(self):
|
||||
return self._context_length
|
||||
|
||||
def __eq__(self, other):
|
||||
if not isinstance(other, self.__class__):
|
||||
raise TypeError("Could not compare {} and {}".format(type(self), type(other)))
|
||||
|
||||
return self.c == other.c and \
|
||||
self.cid == other.cid and \
|
||||
self.context_length == other.context_length
|
||||
|
||||
def __repr__(self):
|
||||
return "LowpanId(stable={}, c={}, cid={}, context_length={})".format(
|
||||
self.stable, self.c, self.cid, self.context_length)
|
||||
|
||||
|
||||
class LowpanIdFactory:
|
||||
|
||||
def parse(self, data, message_info):
|
||||
data_byte = ord(data.read(1))
|
||||
|
||||
cid = (data_byte & 0x0f)
|
||||
c = (data_byte >> 4) & 0x01
|
||||
|
||||
context_length = ord(data.read(1))
|
||||
|
||||
return LowpanId(c, cid, context_length, message_info.stable)
|
||||
|
||||
|
||||
class CommissioningData:
|
||||
|
||||
def __init__(self):
|
||||
# TODO: Not implemented yet
|
||||
raise NotImplementedError
|
||||
|
||||
|
||||
class CommissioningDataFactory:
|
||||
|
||||
def __init__(self):
|
||||
# TODO: Not implemented yet
|
||||
raise NotImplementedError
|
||||
|
||||
|
||||
class Service(NetworkData):
|
||||
|
||||
def __init__(self, t, _id, enterprise_number, service_data_length, service_data, sub_tlvs, stable):
|
||||
super(Service, self).__init__(stable)
|
||||
self._t = t
|
||||
self._id = _id
|
||||
self._enterprise_number = enterprise_number
|
||||
self._service_data_length = service_data_length
|
||||
self._service_data = service_data
|
||||
self._sub_tlvs = sub_tlvs
|
||||
|
||||
@property
|
||||
def t(self):
|
||||
return self._t
|
||||
|
||||
@property
|
||||
def id(self):
|
||||
return self._id
|
||||
|
||||
@property
|
||||
def enterprise_number(self):
|
||||
return self._enterprise_number
|
||||
|
||||
@property
|
||||
def service_data_length(self):
|
||||
return self._service_data_length
|
||||
|
||||
@property
|
||||
def service_data(self):
|
||||
return self._service_data
|
||||
|
||||
@property
|
||||
def sub_tlvs(self):
|
||||
return self._sub_tlvs
|
||||
|
||||
def __eq__(self, other):
|
||||
if not isinstance(other, self.__class__):
|
||||
raise TypeError("Could not compare {} and {}".format(type(self), type(other)))
|
||||
|
||||
return self.t == other.t and \
|
||||
self.id == other.id and \
|
||||
self.enterprise_number == other.enterprise_number and \
|
||||
self.service_data_length == other.service_data_length and \
|
||||
self.service_data == other.service_data and \
|
||||
self.sub_tlvs == other.sub_tlvs
|
||||
|
||||
def __repr__(self):
|
||||
sub_tlvs_str = ", ".format(["{}".format(tlv) for tlv in self.sub_tlvs])
|
||||
return "LowpanId(stable={}, t={}, id={}, enterprise_number={}, service_data_length={}, service_data={}, sub_tlvs=[{}])".format(
|
||||
self.stable, self.t, self.id, self.enterprise_number, self.service_data_length, self.service_data, sub_tlvs_str)
|
||||
|
||||
|
||||
class ServiceSubTlvsFactory(SubTlvsFactory):
|
||||
|
||||
def __init__(self, sub_tlvs_factories):
|
||||
super(ServiceSubTlvsFactory, self).__init__(sub_tlvs_factories)
|
||||
|
||||
|
||||
class ServiceFactory:
|
||||
|
||||
def __init__(self, sub_tlvs_factory):
|
||||
self._sub_tlvs_factory = sub_tlvs_factory
|
||||
|
||||
def parse(self, data, message_info):
|
||||
data_byte = ord(data.read(1))
|
||||
t = (data_byte >> 7) & 0x01
|
||||
_id = (data_byte & 0x0f)
|
||||
|
||||
enterprise_number = struct.unpack(">L", data.read(4))[0]
|
||||
service_data_length = ord(data.read(1))
|
||||
service_data = data.read(service_data_length)
|
||||
|
||||
sub_tlvs = self._sub_tlvs_factory.parse(io.BytesIO(data.read()), message_info)
|
||||
|
||||
return Service(t, _id, enterprise_number, service_data_length, service_data, sub_tlvs, message_info.stable)
|
||||
|
||||
|
||||
class Server(NetworkData):
|
||||
|
||||
def __init__(self, server_16, server_data, stable):
|
||||
super(Server, self).__init__(stable)
|
||||
self._server_16 = server_16
|
||||
self._server_data = server_data
|
||||
|
||||
@property
|
||||
def server_16(self):
|
||||
return self._server_16
|
||||
|
||||
@property
|
||||
def server_data(self):
|
||||
return self._server_data
|
||||
|
||||
def __eq__(self, other):
|
||||
if not isinstance(other, self.__class__):
|
||||
raise TypeError("Could not compare {} and {}".format(type(self), type(other)))
|
||||
|
||||
return self.server_16 == other.server_16 and \
|
||||
self.server_data == other.server_data
|
||||
|
||||
def __repr__(self):
|
||||
return "LowpanId(stable={}, server_16={}, server_data=\"{}\")".format(
|
||||
self.stable, self.server_16, hexlify(self.server_data))
|
||||
|
||||
|
||||
class ServerFactory:
|
||||
|
||||
def parse(self, data, message_info):
|
||||
server_16 = struct.unpack(">H", data.read(2))[0]
|
||||
server_data = bytearray(data.read())
|
||||
|
||||
return Server(server_16, server_data, message_info.stable)
|
||||
|
||||
|
||||
class NetworkDataTlvsFactory(SubTlvsFactory):
|
||||
|
||||
def __init__(self, sub_tlvs_factories):
|
||||
super(NetworkDataTlvsFactory, self).__init__(sub_tlvs_factories)
|
||||
@@ -0,0 +1,163 @@
|
||||
#!/usr/bin/python
|
||||
#
|
||||
# Copyright (c) 2016, 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.
|
||||
#
|
||||
|
||||
import collections
|
||||
import io
|
||||
import logging
|
||||
import os
|
||||
import Queue
|
||||
import select
|
||||
import socket
|
||||
import threading
|
||||
|
||||
import message
|
||||
|
||||
|
||||
class Sniffer:
|
||||
|
||||
""" Class representing the Sniffing node, whose main task is listening and logging message exchange performed by other nodes. """
|
||||
|
||||
logger = logging.getLogger("sniffer.Sniffer")
|
||||
|
||||
POLL_TIMEOUT = 0.11
|
||||
|
||||
RECV_BUFFER_SIZE = 4096
|
||||
|
||||
BASE_PORT = 9000
|
||||
|
||||
WELLKNOWN_NODE_ID = 34
|
||||
|
||||
PORT_OFFSET = int(os.getenv('PORT_OFFSET', "0"))
|
||||
|
||||
def __init__(self, nodeid, message_factory):
|
||||
"""
|
||||
Args:
|
||||
nodeid (int): Node identifier
|
||||
message_factory (MessageFactory): Class producing messages from data bytes.
|
||||
"""
|
||||
|
||||
self.nodeid = nodeid
|
||||
self._message_factory = message_factory
|
||||
|
||||
self._socket = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
|
||||
if self._socket is None:
|
||||
raise RuntimeError("Could not create socket.")
|
||||
|
||||
self._socket.bind(self.address)
|
||||
|
||||
self._thread = None
|
||||
self._thread_alive_event = threading.Event()
|
||||
|
||||
self._poll = select.poll()
|
||||
|
||||
self._buckets = collections.defaultdict(Queue.Queue)
|
||||
|
||||
def _nodeid_to_address(self, nodeid, ip_address="localhost"):
|
||||
return "", self.BASE_PORT + (self.PORT_OFFSET * self.WELLKNOWN_NODE_ID) + nodeid
|
||||
|
||||
def _address_to_nodeid(self, address):
|
||||
ip_address, port = address
|
||||
return (port - self.BASE_PORT - (self.PORT_OFFSET * self.WELLKNOWN_NODE_ID))
|
||||
|
||||
def _recv(self, fd):
|
||||
""" Receive data from socket with passed file descriptor. """
|
||||
|
||||
data, address = socket.fromfd(fd, socket.AF_INET, socket.SOCK_DGRAM).recvfrom(self.RECV_BUFFER_SIZE)
|
||||
|
||||
msg = self._message_factory.create(io.BytesIO(data))
|
||||
|
||||
if msg is None:
|
||||
self.logger.debug("Received 6LowPAN fragment.")
|
||||
return
|
||||
|
||||
nodeid = self._address_to_nodeid(address)
|
||||
|
||||
self._buckets[nodeid].put(msg)
|
||||
|
||||
@property
|
||||
def address(self):
|
||||
""" Sniffer address. """
|
||||
|
||||
return self._nodeid_to_address(self.nodeid)
|
||||
|
||||
def _run(self):
|
||||
""" Receive thread main loop. """
|
||||
|
||||
while self._thread_alive_event.is_set():
|
||||
reported_events = self._poll.poll(self.POLL_TIMEOUT)
|
||||
|
||||
for fd_event_pair in reported_events:
|
||||
fd, event = fd_event_pair
|
||||
|
||||
if event & select.POLLIN or event & select.POLLPRI:
|
||||
self._recv(fd)
|
||||
|
||||
elif event & select.POLLERR:
|
||||
self.logger.error("Error condition of some sort")
|
||||
self._thread_alive_event.clear()
|
||||
break
|
||||
|
||||
elif event & select.POLLNVAL:
|
||||
self.logger.error("Invalid request: descriptor not open")
|
||||
self._thread_alive_event.clear()
|
||||
break
|
||||
|
||||
def start(self):
|
||||
""" Start sniffing. """
|
||||
|
||||
self._poll.register(self._socket, select.POLLIN | select.POLLPRI | select.POLLERR | select.POLLNVAL)
|
||||
|
||||
self._thread = threading.Thread(target=self._run)
|
||||
self._thread.daemon = True
|
||||
|
||||
self._thread_alive_event.set()
|
||||
self._thread.start()
|
||||
|
||||
def stop(self):
|
||||
""" Stop sniffing. """
|
||||
|
||||
self._poll.unregister(self._socket)
|
||||
|
||||
self._thread_alive_event.clear()
|
||||
self._thread.join()
|
||||
self._thread = None
|
||||
|
||||
def get_messages_sent_by(self, nodeid):
|
||||
""" Get sniffed messages.
|
||||
|
||||
Note! This method flushes the message queue so calling this method again will return only the newly logged messages.
|
||||
|
||||
"""
|
||||
bucket = self._buckets[nodeid]
|
||||
messages = []
|
||||
|
||||
while not bucket.empty():
|
||||
messages.append(bucket.get_nowait())
|
||||
|
||||
return message.MessagesSet(messages)
|
||||
@@ -0,0 +1,170 @@
|
||||
#!/usr/bin/python
|
||||
#
|
||||
# Copyright (c) 2016, 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.
|
||||
#
|
||||
|
||||
import random
|
||||
import struct
|
||||
import unittest
|
||||
import ipaddress
|
||||
|
||||
import common
|
||||
|
||||
|
||||
def any_eui64():
|
||||
return bytearray([random.getrandbits(8) for _ in xrange(8)])
|
||||
|
||||
|
||||
def any_rloc16_int():
|
||||
return random.getrandbits(16)
|
||||
|
||||
|
||||
def any_rloc16_bytearray():
|
||||
return bytearray([random.getrandbits(8) for _ in xrange(2)])
|
||||
|
||||
|
||||
def any_ipv6_address():
|
||||
return bytearray([random.getrandbits(8) for _ in xrange(16)])
|
||||
|
||||
|
||||
class TestMessageInfo(unittest.TestCase):
|
||||
|
||||
def test_should_return_source_ipv6_value_when_source_ipv6_property_is_called(self):
|
||||
# GIVEN
|
||||
source_ipv6 = any_ipv6_address()
|
||||
|
||||
message_info = common.MessageInfo()
|
||||
message_info.source_ipv6 = source_ipv6
|
||||
|
||||
# WHEN
|
||||
actual_source_ipv6 = message_info.source_ipv6
|
||||
|
||||
# THEN
|
||||
self.assertEqual(ipaddress.ip_address(bytes(source_ipv6)), actual_source_ipv6)
|
||||
|
||||
def test_should_return_destination_ipv6_value_when_destination_ipv6_property_is_called(self):
|
||||
# GIVEN
|
||||
destination_ipv6 = any_ipv6_address()
|
||||
|
||||
message_info = common.MessageInfo()
|
||||
message_info.destination_ipv6 = destination_ipv6
|
||||
|
||||
# WHEN
|
||||
actual_destination_ipv6 = message_info.destination_ipv6
|
||||
|
||||
# THEN
|
||||
self.assertEqual(ipaddress.ip_address(bytes(destination_ipv6)), actual_destination_ipv6)
|
||||
|
||||
def test_should_return_source_eui64_value_when_source_eui64_property_is_called(self):
|
||||
# GIVEN
|
||||
source_mac_address = any_eui64()
|
||||
|
||||
message_info = common.MessageInfo()
|
||||
message_info.source_mac_address = source_mac_address
|
||||
|
||||
# WHEN
|
||||
actual_source_mac_address = message_info.source_mac_address
|
||||
|
||||
# THEN
|
||||
self.assertEqual(source_mac_address, actual_source_mac_address)
|
||||
|
||||
def test_should_return_destination_eui64_value_when_destination_eui64_property_is_called(self):
|
||||
# GIVEN
|
||||
destination_mac_address = any_eui64()
|
||||
|
||||
message_info = common.MessageInfo()
|
||||
message_info.destination_mac_address = destination_mac_address
|
||||
|
||||
# WHEN
|
||||
actual_destination_mac_address = message_info.destination_mac_address
|
||||
|
||||
# THEN
|
||||
self.assertEqual(destination_mac_address, actual_destination_mac_address)
|
||||
|
||||
|
||||
class TestMacAddress(unittest.TestCase):
|
||||
|
||||
def test_should_create_MacAddress_from_eui64_when_from_eui64_classmethod_is_called(self):
|
||||
# GIVEN
|
||||
eui64 = any_eui64()
|
||||
|
||||
# WHEN
|
||||
mac_address = common.MacAddress.from_eui64(eui64)
|
||||
|
||||
# THEN
|
||||
self.assertEqual(common.MacAddress.LONG, mac_address.type)
|
||||
self.assertEqual(eui64, mac_address.mac_address)
|
||||
|
||||
def test_should_create_MacAddress_from_rloc16_int_when_from_rloc16_classmethod_is_called(self):
|
||||
# GIVEN
|
||||
rloc16 = any_rloc16_int()
|
||||
|
||||
# WHEN
|
||||
mac_address = common.MacAddress.from_rloc16(rloc16)
|
||||
|
||||
# THEN
|
||||
self.assertEqual(common.MacAddress.SHORT, mac_address.type)
|
||||
self.assertEqual(struct.pack(">H", rloc16), mac_address.mac_address)
|
||||
|
||||
def test_should_create_MacAddress_from_rloc16_bytearray_when_from_rloc16_classmethod_is_called(self):
|
||||
# GIVEN
|
||||
rloc16 = any_rloc16_bytearray()
|
||||
|
||||
# WHEN
|
||||
mac_address = common.MacAddress.from_rloc16(rloc16)
|
||||
|
||||
# THEN
|
||||
self.assertEqual(common.MacAddress.SHORT, mac_address.type)
|
||||
self.assertEqual(rloc16, mac_address.mac_address)
|
||||
|
||||
def test_should_convert_short_MacAddress_to_iid_when_convert_method_is_called(self):
|
||||
# GIVEN
|
||||
rloc16 = any_rloc16_bytearray()
|
||||
|
||||
mac_address = common.MacAddress.from_rloc16(rloc16)
|
||||
|
||||
# WHEN
|
||||
iid = mac_address.convert_to_iid()
|
||||
|
||||
# THEN
|
||||
self.assertEqual(bytearray([0x00, 0x00, 0x00, 0xff, 0xfe, 0x00]) + rloc16, iid)
|
||||
|
||||
def test_should_convert_short_MacAddress_to_iid_when_convert_method_is_called(self):
|
||||
# GIVEN
|
||||
eui64 = any_eui64()
|
||||
|
||||
mac_address = common.MacAddress.from_eui64(eui64)
|
||||
|
||||
# WHEN
|
||||
iid = mac_address.convert_to_iid()
|
||||
|
||||
# THEN
|
||||
self.assertEqual(bytearray([eui64[0] ^ 0x02]) + eui64[1:], iid)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
unittest.main()
|
||||
@@ -0,0 +1,431 @@
|
||||
#!/usr/bin/python
|
||||
#
|
||||
# Copyright (c) 2016, 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.
|
||||
#
|
||||
|
||||
import io
|
||||
import random
|
||||
import struct
|
||||
import unittest
|
||||
import ipaddress
|
||||
|
||||
import common
|
||||
import net_crypto
|
||||
import mle
|
||||
|
||||
master_key = bytearray([0x00, 0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77,
|
||||
0x88, 0x99, 0xaa, 0xbb, 0xcc, 0xdd, 0xee, 0xff])
|
||||
|
||||
|
||||
def convert_aux_sec_hdr_to_bytearray(aux_sec_hdr):
|
||||
data = bytearray([aux_sec_hdr.security_level | ((aux_sec_hdr.key_id_mode & 0x03) << 3)])
|
||||
data += struct.pack("<L", aux_sec_hdr.frame_counter)
|
||||
data += aux_sec_hdr.key_id
|
||||
return data
|
||||
|
||||
|
||||
def any_eui64():
|
||||
return bytearray([random.getrandbits(8) for _ in xrange(8)])
|
||||
|
||||
|
||||
def any_security_level():
|
||||
return random.getrandbits(3)
|
||||
|
||||
|
||||
def any_key_id_mode():
|
||||
"""
|
||||
Only key id mode 2.
|
||||
"""
|
||||
return 2
|
||||
|
||||
|
||||
def any_key_id(key_id_mode):
|
||||
if key_id_mode == 2:
|
||||
length = 5
|
||||
|
||||
return bytearray([random.getrandbits(8) for _ in xrange(length)])
|
||||
|
||||
|
||||
def any_auxiliary_security_header():
|
||||
key_id_mode = any_key_id_mode()
|
||||
key_id = any_key_id(key_id_mode)
|
||||
|
||||
return net_crypto.AuxiliarySecurityHeader(key_id_mode, any_security_level(), any_frame_counter(), key_id)
|
||||
|
||||
|
||||
def any_frame_counter():
|
||||
return random.getrandbits(32)
|
||||
|
||||
|
||||
def any_ip_address():
|
||||
ip_address_bytes = bytearray([random.getrandbits(8) for _ in xrange(16)])
|
||||
return ipaddress.ip_address(bytes(ip_address_bytes))
|
||||
|
||||
|
||||
def any_data(length=None):
|
||||
length = length if length is not None else random.randint(0, 128)
|
||||
return bytearray([random.getrandbits(8) for _ in xrange(length)])
|
||||
|
||||
|
||||
def any_master_key():
|
||||
return bytearray([random.getrandbits(8) for _ in xrange(16)])
|
||||
|
||||
|
||||
class TestCryptoEngine(unittest.TestCase):
|
||||
|
||||
def test_should_decrypt_bytearray_to_mle_message_when_decrypt_method_is_called(self):
|
||||
# GIVEN
|
||||
message_info = common.MessageInfo()
|
||||
message_info.source_mac_address = common.MacAddress.from_eui64(
|
||||
bytearray([0x00, 0x35, 0xcc, 0x94, 0xd7, 0x7a, 0x07, 0xe8]))
|
||||
|
||||
message_info.source_ipv6 = "fe80::235:cc94:d77a:07e8"
|
||||
message_info.destination_ipv6 = "ff02::2"
|
||||
|
||||
message_info.aux_sec_hdr = net_crypto.AuxiliarySecurityHeader(key_id_mode=2,
|
||||
security_level=5,
|
||||
frame_counter=262165,
|
||||
key_id=bytearray([0x00, 0x00, 0x00, 0x00, 0x01]))
|
||||
message_info.aux_sec_hdr_bytes = convert_aux_sec_hdr_to_bytearray(message_info.aux_sec_hdr)
|
||||
|
||||
data = bytearray([0x9a, 0x5a, 0x9a, 0x5b, 0xba, 0x25, 0x9c, 0x5e,
|
||||
0x58, 0xa2, 0x7e, 0x75, 0x74, 0xef, 0x79, 0xbc,
|
||||
0x4f, 0xa3, 0xf9, 0xae, 0xa8, 0x34, 0xf6, 0xf2,
|
||||
0x37, 0x21, 0x93, 0x60])
|
||||
|
||||
mic = bytearray([0xe1, 0xb5, 0xa2, 0x53])
|
||||
|
||||
net_crypto_engine = net_crypto.CryptoEngine(net_crypto.MleCryptoMaterialCreator(master_key))
|
||||
|
||||
# WHEN
|
||||
mle_msg = net_crypto_engine.decrypt(data, mic, message_info)
|
||||
|
||||
# THEN
|
||||
expected_mle_msg = bytearray([0x04, 0x00, 0x02, 0x00, 0x00, 0x09, 0x0b, 0x8f,
|
||||
0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x40, 0x00,
|
||||
0x01, 0xf1, 0x0b, 0x08, 0x65, 0x5e, 0x0f, 0x83,
|
||||
0x40, 0xc7, 0x83, 0x31])
|
||||
self.assertEqual(expected_mle_msg, mle_msg)
|
||||
|
||||
def test_should_encrypt_mle_message_to_bytearray_when_encrypt_method_is_called(self):
|
||||
# GIVEN
|
||||
message_info = common.MessageInfo()
|
||||
message_info.source_mac_address = common.MacAddress.from_eui64(
|
||||
bytearray([0x00, 0x35, 0xcc, 0x94, 0xd7, 0x7a, 0x07, 0xe8]))
|
||||
|
||||
message_info.source_ipv6 = "fe80::235:cc94:d77a:07e8"
|
||||
message_info.destination_ipv6 = "ff02::2"
|
||||
|
||||
message_info.aux_sec_hdr = net_crypto.AuxiliarySecurityHeader(key_id_mode=2,
|
||||
security_level=5,
|
||||
frame_counter=262165,
|
||||
key_id=bytearray([0x00, 0x00, 0x00, 0x00, 0x01]))
|
||||
message_info.aux_sec_hdr_bytes = convert_aux_sec_hdr_to_bytearray(message_info.aux_sec_hdr)
|
||||
|
||||
mle_msg = bytearray([0x04, 0x00, 0x02, 0x00, 0x00, 0x09, 0x0b, 0x8f,
|
||||
0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x40, 0x00,
|
||||
0x01, 0xf1, 0x0b, 0x08, 0x65, 0x5e, 0x0f, 0x83,
|
||||
0x40, 0xc7, 0x83, 0x31])
|
||||
|
||||
net_crypto_engine = net_crypto.CryptoEngine(net_crypto.MleCryptoMaterialCreator(master_key))
|
||||
|
||||
# WHEN
|
||||
encrypted_data, mic = net_crypto_engine.encrypt(mle_msg, message_info)
|
||||
|
||||
# THEN
|
||||
expected_encrypted_data = bytearray([0x9a, 0x5a, 0x9a, 0x5b, 0xba, 0x25, 0x9c, 0x5e,
|
||||
0x58, 0xa2, 0x7e, 0x75, 0x74, 0xef, 0x79, 0xbc,
|
||||
0x4f, 0xa3, 0xf9, 0xae, 0xa8, 0x34, 0xf6, 0xf2,
|
||||
0x37, 0x21, 0x93, 0x60, 0xe1, 0xb5, 0xa2, 0x53])
|
||||
|
||||
self.assertEqual(expected_encrypted_data, encrypted_data + mic)
|
||||
|
||||
def test_should_encrypt_and_decrypt_random_data_content_when_proper_methods_are_called(self):
|
||||
# GIVEN
|
||||
data = any_data()
|
||||
|
||||
master_key = any_master_key()
|
||||
|
||||
key_id_mode = 2
|
||||
security_level = 5
|
||||
|
||||
message_info = common.MessageInfo()
|
||||
message_info.source_mac_address = common.MacAddress.from_eui64(any_eui64())
|
||||
|
||||
message_info.source_ipv6 = any_ip_address()
|
||||
message_info.destination_ipv6 = any_ip_address()
|
||||
|
||||
message_info.aux_sec_hdr = net_crypto.AuxiliarySecurityHeader(key_id_mode=key_id_mode,
|
||||
security_level=security_level,
|
||||
frame_counter=any_frame_counter(),
|
||||
key_id=any_key_id(key_id_mode))
|
||||
message_info.aux_sec_hdr_bytes = convert_aux_sec_hdr_to_bytearray(message_info.aux_sec_hdr)
|
||||
|
||||
net_crypto_engine = net_crypto.CryptoEngine(net_crypto.MleCryptoMaterialCreator(master_key))
|
||||
|
||||
# WHEN
|
||||
enc_data, mic = net_crypto_engine.encrypt(data, message_info)
|
||||
dec_data = net_crypto_engine.decrypt(enc_data, mic, message_info)
|
||||
|
||||
# THEN
|
||||
self.assertEqual(data, dec_data)
|
||||
|
||||
|
||||
class TestCryptoMaterialCreator(unittest.TestCase):
|
||||
|
||||
""" Key generaion was described in Thread specification.
|
||||
|
||||
Read more: Thread 1.1.0 Specification Candidate Final - 7.1.4 Key Generation
|
||||
|
||||
Test vectors was taken from thread specification.
|
||||
"""
|
||||
|
||||
def test_should_generate_mle_and_mac_key_when_generate_keys_method_is_called_with_sequence_counter_equal_0(self):
|
||||
"""
|
||||
7.1.4.1 Test Vector 1
|
||||
"""
|
||||
|
||||
# GIVEN
|
||||
sequence_counter = 0
|
||||
|
||||
creator = net_crypto.CryptoMaterialCreator(master_key)
|
||||
|
||||
# WHEN
|
||||
mle_key, mac_key = creator._generate_keys(sequence_counter)
|
||||
|
||||
# THEN
|
||||
self.assertEqual(mle_key, bytearray([0x54, 0x45, 0xf4, 0x15, 0x8f, 0xd7, 0x59, 0x12,
|
||||
0x17, 0x58, 0x09, 0xf8, 0xb5, 0x7a, 0x66, 0xa4]))
|
||||
self.assertEqual(mac_key, bytearray([0xde, 0x89, 0xc5, 0x3a, 0xf3, 0x82, 0xb4, 0x21,
|
||||
0xe0, 0xfd, 0xe5, 0xa9, 0xba, 0xe3, 0xbe, 0xf0]))
|
||||
|
||||
def test_should_generate_mle_and_mac_key_when_generate_keys_method_is_called_with_sequence_counter_equal_1(self):
|
||||
"""
|
||||
7.1.4.2 Test Vector 2
|
||||
"""
|
||||
|
||||
# GIVEN
|
||||
sequence_counter = 1
|
||||
|
||||
creator = net_crypto.CryptoMaterialCreator(master_key)
|
||||
|
||||
# WHEN
|
||||
mle_key, mac_key = creator._generate_keys(sequence_counter)
|
||||
|
||||
# THEN
|
||||
self.assertEqual(mle_key, bytearray([0x8f, 0x4c, 0xd1, 0xa2, 0x7d, 0x95, 0xc0, 0x7d,
|
||||
0x12, 0xdb, 0x89, 0x74, 0xbd, 0x61, 0x5c, 0x13]))
|
||||
self.assertEqual(mac_key, bytearray([0x9b, 0xe0, 0xd1, 0xaf, 0x7b, 0xd8, 0x73, 0x50,
|
||||
0xde, 0xab, 0xcd, 0xd0, 0x7f, 0xeb, 0xb9, 0xd5]))
|
||||
|
||||
def test_should_generate_mle_and_mac_key_when_generate_keys_method_is_called_with_sequence_counter_equal_2(self):
|
||||
"""
|
||||
7.1.4.3 Test Vector 3
|
||||
"""
|
||||
|
||||
# GIVEN
|
||||
sequence_counter = 2
|
||||
|
||||
creator = net_crypto.CryptoMaterialCreator(master_key)
|
||||
|
||||
# WHEN
|
||||
mle_key, mac_key = creator._generate_keys(sequence_counter)
|
||||
|
||||
# THEN
|
||||
self.assertEqual(mle_key, bytearray([0x01, 0x6e, 0x2a, 0xb8, 0xec, 0x88, 0x87, 0x96,
|
||||
0x87, 0xa7, 0x2e, 0x0a, 0x35, 0x7e, 0xcf, 0x2a]))
|
||||
self.assertEqual(mac_key, bytearray([0x56, 0x41, 0x09, 0xe9, 0xd2, 0xaa, 0xd7, 0xf7,
|
||||
0x23, 0xec, 0x3b, 0x96, 0x11, 0x0e, 0xef, 0xa3]))
|
||||
|
||||
|
||||
class TestMleCryptoMaterialCreator(unittest.TestCase):
|
||||
|
||||
def test_should_create_nonce_when_create_nonce_method_is_called(self):
|
||||
# GIVEN
|
||||
source_eui64 = any_eui64()
|
||||
frame_counter = any_frame_counter()
|
||||
security_level = any_security_level()
|
||||
|
||||
creator = net_crypto.MleCryptoMaterialCreator(master_key)
|
||||
|
||||
# WHEN
|
||||
nonce = creator._create_nonce(source_eui64, frame_counter, security_level)
|
||||
|
||||
# THEN
|
||||
nonce_bytes = io.BytesIO(nonce)
|
||||
|
||||
self.assertEqual(source_eui64, nonce_bytes.read(8))
|
||||
self.assertEqual(struct.pack(">L", frame_counter), nonce_bytes.read(4))
|
||||
self.assertEqual(security_level, ord(nonce_bytes.read(1)))
|
||||
|
||||
def test_should_create_authenticated_data_when_create_authenticated_data_method_is_called(self):
|
||||
"""
|
||||
Only Key id mode 2.
|
||||
Length of the Auxiliary Security Header is constantly equal 10.
|
||||
"""
|
||||
|
||||
# GIVEN
|
||||
source_address = any_ip_address()
|
||||
destination_address = any_ip_address()
|
||||
auxiliary_security_header_bytes = convert_aux_sec_hdr_to_bytearray(any_auxiliary_security_header())
|
||||
|
||||
creator = net_crypto.MleCryptoMaterialCreator(master_key)
|
||||
|
||||
# WHEN
|
||||
authenticated_data = creator._create_authenticated_data(
|
||||
source_address, destination_address, auxiliary_security_header_bytes)
|
||||
|
||||
# THEN
|
||||
authenticated_data_bytes = io.BytesIO(authenticated_data)
|
||||
|
||||
self.assertEqual(source_address.packed, authenticated_data_bytes.read(16))
|
||||
self.assertEqual(destination_address.packed, authenticated_data_bytes.read(16))
|
||||
self.assertEqual(auxiliary_security_header_bytes, authenticated_data_bytes.read(10))
|
||||
|
||||
def test_should_create_key_and_nonce_and_authenticated_data_when_create_key_and_nonce_and_authenticated_data_is_called(self):
|
||||
# GIVEN
|
||||
message_info = common.MessageInfo()
|
||||
message_info.source_mac_address = common.MacAddress.from_eui64(any_eui64())
|
||||
|
||||
message_info.source_ipv6 = any_ip_address()
|
||||
message_info.destination_ipv6 = any_ip_address()
|
||||
|
||||
message_info.aux_sec_hdr = any_auxiliary_security_header()
|
||||
message_info.aux_sec_hdr_bytes = convert_aux_sec_hdr_to_bytearray(message_info.aux_sec_hdr)
|
||||
|
||||
creator = net_crypto.MleCryptoMaterialCreator(master_key)
|
||||
|
||||
# WHEN
|
||||
key, nonce, auth_data = creator.create_key_and_nonce_and_authenticated_data(message_info)
|
||||
|
||||
# THEN
|
||||
self.assertEqual(message_info.source_mac_address.mac_address +
|
||||
struct.pack(">LB",
|
||||
message_info.aux_sec_hdr.frame_counter,
|
||||
message_info.aux_sec_hdr.security_level), nonce)
|
||||
|
||||
self.assertEqual(message_info.source_ipv6.packed +
|
||||
message_info.destination_ipv6.packed +
|
||||
message_info.aux_sec_hdr_bytes, auth_data)
|
||||
|
||||
|
||||
class TestAuxiliarySecurityHeader(unittest.TestCase):
|
||||
|
||||
def test_should_return_key_id_mode_value_when_key_id_mode_property_is_called(self):
|
||||
# GIVEN
|
||||
key_id_mode = any_key_id_mode()
|
||||
|
||||
aux_sec_hdr_obj = net_crypto.AuxiliarySecurityHeader(
|
||||
key_id_mode, any_security_level(), any_frame_counter(), any_key_id(key_id_mode))
|
||||
|
||||
# WHEN
|
||||
actual_key_id_mode = aux_sec_hdr_obj.key_id_mode
|
||||
|
||||
# THEN
|
||||
self.assertEqual(key_id_mode, actual_key_id_mode)
|
||||
|
||||
def test_should_return_security_level_value_when_security_level_property_is_called(self):
|
||||
# GIVEN
|
||||
security_level = any_security_level()
|
||||
key_id_mode = any_key_id_mode()
|
||||
|
||||
aux_sec_hdr_obj = net_crypto.AuxiliarySecurityHeader(
|
||||
key_id_mode, security_level, any_frame_counter(), any_key_id(key_id_mode))
|
||||
|
||||
# WHEN
|
||||
actual_security_level = aux_sec_hdr_obj.security_level
|
||||
|
||||
# THEN
|
||||
self.assertEqual(security_level, actual_security_level)
|
||||
|
||||
def test_should_return_frame_counter_value_when_frame_counter_property_is_called(self):
|
||||
# GIVEN
|
||||
frame_counter = any_frame_counter()
|
||||
key_id_mode = any_key_id_mode()
|
||||
|
||||
aux_sec_hdr_obj = net_crypto.AuxiliarySecurityHeader(
|
||||
key_id_mode, any_security_level(), frame_counter, any_key_id(key_id_mode))
|
||||
|
||||
# WHEN
|
||||
actual_frame_counter = aux_sec_hdr_obj.frame_counter
|
||||
|
||||
# THEN
|
||||
self.assertEqual(frame_counter, actual_frame_counter)
|
||||
|
||||
def test_should_return_key_id_value_when_key_id_property_is_called(self):
|
||||
# GIVEN
|
||||
key_id_mode = any_key_id_mode()
|
||||
key_id = any_key_id(key_id_mode)
|
||||
|
||||
aux_sec_hdr_obj = net_crypto.AuxiliarySecurityHeader(
|
||||
key_id_mode, any_security_level(), any_frame_counter(), key_id)
|
||||
|
||||
# WHEN
|
||||
actual_key_id = aux_sec_hdr_obj.key_id
|
||||
|
||||
# THEN
|
||||
self.assertEqual(key_id, actual_key_id)
|
||||
|
||||
def test_should_return_sequence_counter_value_when_sequence_counter_property_is_called(self):
|
||||
# GIVEN
|
||||
key_id_mode = 2
|
||||
key_id = any_key_id(key_id_mode)
|
||||
|
||||
aux_sec_hdr_obj = net_crypto.AuxiliarySecurityHeader(
|
||||
key_id_mode, any_security_level(), any_frame_counter(), key_id)
|
||||
|
||||
# WHEN
|
||||
actual_sequence_counter = aux_sec_hdr_obj.sequence_counter
|
||||
|
||||
# THEN
|
||||
self.assertEqual(struct.unpack(">I", key_id[:4])[0], actual_sequence_counter)
|
||||
|
||||
|
||||
class TestAuxiliarySecurityHeaderFactory(unittest.TestCase):
|
||||
|
||||
def test_should_create_AuxiliarySecurityHeader_from_bytearray_when_parse_method_is_called(self):
|
||||
# GIVEN
|
||||
key_id_mode = any_key_id_mode()
|
||||
sec_lvl = any_security_level()
|
||||
frame_counter = any_frame_counter()
|
||||
key_id = any_key_id(key_id_mode)
|
||||
|
||||
factory = net_crypto.AuxiliarySecurityHeaderFactory()
|
||||
|
||||
data = bytearray([sec_lvl | key_id_mode << 3]) + struct.pack("<I", frame_counter) + key_id
|
||||
|
||||
# WHEN
|
||||
aux_sec_hdr = factory.parse(io.BytesIO(data), common.MessageInfo())
|
||||
|
||||
# THEN
|
||||
self.assertTrue(isinstance(aux_sec_hdr, net_crypto.AuxiliarySecurityHeader))
|
||||
self.assertEqual(key_id_mode, aux_sec_hdr.key_id_mode)
|
||||
self.assertEqual(sec_lvl, aux_sec_hdr.security_level)
|
||||
self.assertEqual(frame_counter, aux_sec_hdr.frame_counter)
|
||||
|
||||
if __name__ == "__main__":
|
||||
unittest.main()
|
||||
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,235 @@
|
||||
#!/usr/bin/python
|
||||
#
|
||||
# Copyright (c) 2016, 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.
|
||||
#
|
||||
|
||||
import io
|
||||
import unittest
|
||||
|
||||
from common import MacAddress
|
||||
import mac802154
|
||||
|
||||
longaddrs = bytearray([0x61, 0xcc, 0x00, 0xce, 0xfa])
|
||||
shortaddrs = bytearray([0x61, 0x88, 0x00, 0xce, 0xfa])
|
||||
longshortaddrs = bytearray([0x61, 0xc8, 0x00, 0xce, 0xfa])
|
||||
shortlongaddrs = bytearray([0x61, 0x8c, 0x00, 0xce, 0xfa])
|
||||
|
||||
|
||||
class TestMacParser(unittest.TestCase):
|
||||
|
||||
def test_should_parse_ack_frame(self):
|
||||
frame = mac802154.MacFrame()
|
||||
frame.parse(io.BytesIO(bytearray([0x12, 0x00, 0x12, 0x34, 0x56])))
|
||||
|
||||
self.assertEqual(mac802154.MacHeader.FrameType.ACK, frame.header.frame_type)
|
||||
self.assertEqual(True, frame.header.frame_pending)
|
||||
self.assertEqual(False, frame.header.ack_request)
|
||||
self.assertEqual(0, frame.header.frame_version)
|
||||
self.assertEqual(0x12, frame.header.seq)
|
||||
self.assertEqual(bytearray([0x34, 0x56]), frame.header.fcs)
|
||||
self.assertEqual(None, frame.payload)
|
||||
|
||||
def test_should_parse_data_frame_with_short_addresses(self):
|
||||
frame = mac802154.MacFrame()
|
||||
frame.parse(io.BytesIO(bytearray([0x61, 0x88, 0x34, 0xce, 0xfa,
|
||||
0xad, 0xde, 0xef, 0xbe, 0x12, 0x34, 0xfe, 0xdc])))
|
||||
|
||||
self.assertEqual(mac802154.MacHeader.FrameType.DATA, frame.header.frame_type)
|
||||
self.assertEqual(False, frame.header.frame_pending)
|
||||
self.assertEqual(True, frame.header.ack_request)
|
||||
self.assertEqual(0, frame.header.frame_version)
|
||||
self.assertEqual(0x34, frame.header.seq)
|
||||
self.assertEqual(bytearray([0xfe, 0xdc]), frame.header.fcs)
|
||||
self.assertEqual(0xface, frame.header.dest_pan_id)
|
||||
self.assertEqual(0xdead, frame.header.dest_address.rloc)
|
||||
self.assertEqual(0xface, frame.header.src_pan_id)
|
||||
self.assertEqual(0xbeef, frame.header.src_address.rloc)
|
||||
|
||||
self.assertEqual(bytearray([0x12, 0x34]), frame.payload.data)
|
||||
|
||||
def test_should_parse_data_frame_with_extended_addresses(self):
|
||||
frame = mac802154.MacFrame()
|
||||
frame.parse(io.BytesIO(bytearray([0x61, 0xcc,
|
||||
0x56,
|
||||
0xce, 0xfa,
|
||||
0xde, 0xad, 0xbe, 0xef, 0xde, 0xad, 0xbe, 0xef,
|
||||
0x0b, 0xad, 0xf0, 0x0d, 0xba, 0xd0, 0xd0, 0x0d,
|
||||
0x12, 0x34,
|
||||
0xfe, 0xdc])))
|
||||
|
||||
self.assertEqual(mac802154.MacHeader.FrameType.DATA, frame.header.frame_type)
|
||||
self.assertEqual(False, frame.header.frame_pending)
|
||||
self.assertEqual(True, frame.header.ack_request)
|
||||
self.assertEqual(0, frame.header.frame_version)
|
||||
self.assertEqual(0x56, frame.header.seq)
|
||||
self.assertEqual(bytearray([0xfe, 0xdc]), frame.header.fcs)
|
||||
self.assertEqual(0xface, frame.header.dest_pan_id)
|
||||
self.assertEqual(bytearray(reversed([0xde, 0xad, 0xbe, 0xef, 0xde, 0xad, 0xbe, 0xef])),
|
||||
frame.header.dest_address.mac_address)
|
||||
self.assertEqual(0xface, frame.header.src_pan_id)
|
||||
self.assertEqual(bytearray(reversed([0x0b, 0xad, 0xf0, 0x0d, 0xba, 0xd0, 0xd0, 0x0d])),
|
||||
frame.header.src_address.mac_address)
|
||||
|
||||
self.assertEqual(bytearray([0x12, 0x34]), frame.payload.data)
|
||||
|
||||
def test_should_parse_data_frame_with_short_and_extended_addresses(self):
|
||||
frame = mac802154.MacFrame()
|
||||
frame.parse(io.BytesIO(bytearray([0x61, 0xc8,
|
||||
0x56,
|
||||
0xce, 0xfa,
|
||||
0xad, 0xde,
|
||||
0x0b, 0xad, 0xf0, 0x0d, 0xba, 0xd0, 0xd0, 0x0d,
|
||||
0x12, 0x34,
|
||||
0xfe, 0xdc])))
|
||||
|
||||
self.assertEqual(mac802154.MacHeader.FrameType.DATA, frame.header.frame_type)
|
||||
self.assertEqual(False, frame.header.frame_pending)
|
||||
self.assertEqual(True, frame.header.ack_request)
|
||||
self.assertEqual(0, frame.header.frame_version)
|
||||
self.assertEqual(0x56, frame.header.seq)
|
||||
self.assertEqual(bytearray([0xfe, 0xdc]), frame.header.fcs)
|
||||
self.assertEqual(0xface, frame.header.dest_pan_id)
|
||||
self.assertEqual(0xdead, frame.header.dest_address.rloc)
|
||||
self.assertEqual(0xface, frame.header.src_pan_id)
|
||||
self.assertEqual(bytearray(reversed([0x0b, 0xad, 0xf0, 0x0d, 0xba, 0xd0, 0xd0, 0x0d])),
|
||||
frame.header.src_address.mac_address)
|
||||
|
||||
self.assertEqual(bytearray([0x12, 0x34]), frame.payload.data)
|
||||
|
||||
def test_should_parse_data_frame_with_extended_and_short_addresses(self):
|
||||
frame = mac802154.MacFrame()
|
||||
frame.parse(io.BytesIO(bytearray([0x61, 0x8c,
|
||||
0x56,
|
||||
0xce, 0xfa,
|
||||
0xde, 0xad, 0xbe, 0xef, 0xde, 0xad, 0xbe, 0xef,
|
||||
0x0d, 0xf0,
|
||||
0x12, 0x34,
|
||||
0xfe, 0xdc])))
|
||||
|
||||
self.assertEqual(mac802154.MacHeader.FrameType.DATA, frame.header.frame_type)
|
||||
self.assertEqual(False, frame.header.frame_pending)
|
||||
self.assertEqual(True, frame.header.ack_request)
|
||||
self.assertEqual(0, frame.header.frame_version)
|
||||
self.assertEqual(0x56, frame.header.seq)
|
||||
self.assertEqual(bytearray([0xfe, 0xdc]), frame.header.fcs)
|
||||
self.assertEqual(0xface, frame.header.dest_pan_id)
|
||||
self.assertEqual(bytearray(reversed([0xde, 0xad, 0xbe, 0xef, 0xde, 0xad, 0xbe, 0xef])),
|
||||
frame.header.dest_address.mac_address)
|
||||
self.assertEqual(0xface, frame.header.src_pan_id)
|
||||
self.assertEqual(0xf00d, frame.header.src_address.rloc)
|
||||
|
||||
self.assertEqual(bytearray([0x12, 0x34]), frame.payload.data)
|
||||
|
||||
def test_should_parse_data_request_command(self):
|
||||
frame = mac802154.MacFrame()
|
||||
frame.parse(io.BytesIO(bytearray([0x63, 0x88,
|
||||
0x78,
|
||||
0xce, 0xfa,
|
||||
0xad, 0xde,
|
||||
0x0d, 0xf0,
|
||||
0x04,
|
||||
0xfe, 0xdc])))
|
||||
|
||||
self.assertEqual(mac802154.MacHeader.FrameType.COMMAND, frame.header.frame_type)
|
||||
self.assertEqual(False, frame.header.frame_pending)
|
||||
self.assertEqual(True, frame.header.ack_request)
|
||||
self.assertEqual(0, frame.header.frame_version)
|
||||
self.assertEqual(0x78, frame.header.seq)
|
||||
self.assertEqual(bytearray([0xfe, 0xdc]), frame.header.fcs)
|
||||
self.assertEqual(0xface, frame.header.dest_pan_id)
|
||||
self.assertEqual(0xdead, frame.header.dest_address.rloc)
|
||||
self.assertEqual(0xface, frame.header.src_pan_id)
|
||||
self.assertEqual(0xf00d, frame.header.src_address.rloc)
|
||||
|
||||
self.assertEqual(bytearray([]), frame.payload.data)
|
||||
|
||||
def test_should_decrypt_data_frame(self):
|
||||
|
||||
mac802154.DeviceDescriptors.add(0x2001, MacAddress(bytearray([0x16, 0x6e, 0x0a, 0x00, 0x00, 0x00, 0x00, 0x07]),
|
||||
MacAddress.LONG))
|
||||
|
||||
frame = mac802154.MacFrame()
|
||||
frame.parse(io.BytesIO(bytearray([0x69, 0x98, 0x68, # FC, seq
|
||||
0xce, 0xfa, # Pan Id
|
||||
0x00, 0x20, # Dst addr
|
||||
0x01, 0x20, # Src addr
|
||||
0x0d, 0x00, 0x00, 0x00, 0x00, 0x01, # Aux Security Header
|
||||
0xb5, 0x5a, 0x0d, 0x8e, 0x18, 0x5c, 0xb1, 0x06, # Payload
|
||||
0xc4, 0x6f, 0x7d, 0x6b, 0xb5, 0x4a, 0x87, 0x14,
|
||||
0xae, 0xdd, 0x8e, 0xb7, 0x37, 0x62, 0x27, 0x48,
|
||||
0xc9, 0x53, 0x0c, 0x44, 0x31, 0x59, 0x8b, 0xa2,
|
||||
0x83, 0x59, 0xa1, 0x43,
|
||||
0x74, 0xe0, 0x2a, 0xf6, # MIC (valid)
|
||||
0x99, 0xfc]))) # FCS (valid)
|
||||
|
||||
self.assertEqual(mac802154.MacHeader.FrameType.DATA, frame.header.frame_type)
|
||||
self.assertEqual(False, frame.header.frame_pending)
|
||||
self.assertEqual(True, frame.header.ack_request)
|
||||
self.assertEqual(1, frame.header.frame_version)
|
||||
self.assertEqual(0x68, frame.header.seq)
|
||||
self.assertEqual(bytearray([0x99, 0xfc]), frame.header.fcs)
|
||||
self.assertEqual(0xface, frame.header.dest_pan_id)
|
||||
self.assertEqual(0x2000, frame.header.dest_address.rloc)
|
||||
self.assertEqual(0xface, frame.header.src_pan_id)
|
||||
self.assertEqual(0x2001, frame.header.src_address.rloc)
|
||||
|
||||
self.assertEqual(0, frame.header.aux_sec_header.frame_counter)
|
||||
self.assertEqual(5, frame.header.aux_sec_header.security_level)
|
||||
|
||||
self.assertEqual(bytes(bytearray([0x7c, 0x77, 0x80, 0xf0, 0x4d, 0x4d, 0x4d, 0x4d,
|
||||
0xe0, 0x04, 0x44, 0x02, 0x44, 0x66, 0x13, 0x5f,
|
||||
0x22, 0x80, 0xb1, 0x61, 0x02, 0x61, 0x73, 0x11,
|
||||
0x2a, 0xff, 0x01, 0x08, 0x16, 0x6e, 0x0a, 0x00,
|
||||
0x00, 0x00, 0x00, 0x07])), frame.payload.data)
|
||||
|
||||
def test_should_decrypt_command_frame(self):
|
||||
frame = mac802154.MacFrame()
|
||||
frame.parse(io.BytesIO(bytearray([0x6b, 0xdc, 0xce, 0xce, 0xfa, 0x02, 0x00, 0x00,
|
||||
0x00, 0x00, 0x0a, 0x6e, 0x16, 0x03, 0x00, 0x00,
|
||||
0x00, 0x00, 0x0a, 0x6e, 0x16, 0x0d, 0x00, 0x00,
|
||||
0x00, 0x00, 0x01, 0x04, 0x2d, 0xbc, 0x12, 0xbe,
|
||||
0x0a, 0x4f])))
|
||||
|
||||
self.assertEqual(mac802154.MacHeader.FrameType.COMMAND, frame.header.frame_type)
|
||||
self.assertEqual(False, frame.header.frame_pending)
|
||||
self.assertEqual(True, frame.header.ack_request)
|
||||
self.assertEqual(1, frame.header.frame_version)
|
||||
self.assertEqual(206, frame.header.seq)
|
||||
self.assertEqual(bytearray([0x0a, 0x4f]), frame.header.fcs)
|
||||
self.assertEqual(0xface, frame.header.dest_pan_id)
|
||||
self.assertEqual(bytearray([0x16, 0x6e, 0x0a, 0x00, 0x00, 0x00, 0x00, 0x02]),
|
||||
frame.header.dest_address.mac_address)
|
||||
self.assertEqual(0xface, frame.header.src_pan_id)
|
||||
self.assertEqual(bytearray([0x16, 0x6e, 0x0a, 0x00, 0x00, 0x00, 0x00, 0x03]),
|
||||
frame.header.src_address.mac_address)
|
||||
|
||||
self.assertEqual(0, frame.header.aux_sec_header.frame_counter)
|
||||
self.assertEqual(5, frame.header.aux_sec_header.security_level)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
unittest.main()
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,934 @@
|
||||
import io
|
||||
import random
|
||||
import struct
|
||||
import unittest
|
||||
|
||||
import common
|
||||
import config
|
||||
import network_data
|
||||
|
||||
|
||||
def convert_route_to_bytearray(route):
|
||||
return struct.pack(">HB", route.border_router_16, ((route.prf & 0x03) << 6))
|
||||
|
||||
|
||||
def convert_routes_to_bytearray(routes):
|
||||
data = bytearray()
|
||||
for route in routes:
|
||||
data += convert_route_to_bytearray(route)
|
||||
|
||||
return data
|
||||
|
||||
|
||||
def convert_has_route_to_bytearray(has_route):
|
||||
return convert_routes_to_bytearray(has_route.routes)
|
||||
|
||||
|
||||
def convert_border_router_to_bytearray(border_router):
|
||||
data = struct.pack(">HBB", border_router.border_router_16,
|
||||
(border_router.o & 0x01) |
|
||||
((border_router.r & 0x01) << 1) |
|
||||
((border_router.c & 0x01) << 2) |
|
||||
((border_router.d & 0x01) << 3) |
|
||||
((border_router.s & 0x01) << 4) |
|
||||
((border_router.p & 0x01) << 5) |
|
||||
((border_router.prf & 0x03) << 6),
|
||||
((border_router.n & 0x01) << 7))
|
||||
|
||||
return data
|
||||
|
||||
|
||||
def convert_lowpan_id_to_bytearray(lowpan_id):
|
||||
return bytearray([lowpan_id.cid | (lowpan_id.c << 4), lowpan_id.context_length])
|
||||
|
||||
|
||||
def convert_prefix_sub_tlvs_to_bytearray(sub_tlvs):
|
||||
data = bytearray()
|
||||
|
||||
for sub_tlv in sub_tlvs:
|
||||
if isinstance(sub_tlv, network_data.HasRoute):
|
||||
value = convert_has_route_to_bytearray(sub_tlv)
|
||||
_type = sub_tlv.stable | ((0 & 0x7f) << 1)
|
||||
|
||||
elif isinstance(sub_tlv, network_data.BorderRouter):
|
||||
value = convert_border_router_to_bytearray(sub_tlv)
|
||||
_type = sub_tlv.stable | ((2 & 0x7f) << 1)
|
||||
|
||||
elif isinstance(sub_tlv, network_data.LowpanId):
|
||||
value = convert_lowpan_id_to_bytearray(sub_tlv)
|
||||
_type = sub_tlv.stable | ((3 & 0x7f) << 1)
|
||||
|
||||
else:
|
||||
raise ValueError
|
||||
|
||||
data += bytearray([_type, len(value)]) + value
|
||||
|
||||
return data
|
||||
|
||||
|
||||
def convert_server_to_bytearray(server):
|
||||
return struct.pack(">H", server.server_16) + server.server_data
|
||||
|
||||
|
||||
def convert_service_sub_tlvs_to_bytearray(sub_tlvs):
|
||||
data = bytearray()
|
||||
|
||||
for sub_tlv in sub_tlvs:
|
||||
if isinstance(sub_tlv, network_data.Server):
|
||||
value = convert_server_to_bytearray(sub_tlv)
|
||||
_type = sub_tlv.stable | ((6 & 0x7f) << 1)
|
||||
|
||||
else:
|
||||
raise ValueError
|
||||
|
||||
data += bytearray([_type, len(value)]) + value
|
||||
|
||||
return data
|
||||
|
||||
|
||||
def convert_service_to_bytearray(service):
|
||||
return struct.pack(">BLB", ((service.t & 0x01) << 7) | ((service.id) & 0x0f),
|
||||
service.enterprise_number,
|
||||
service.service_data_length) + \
|
||||
service.service_data + \
|
||||
convert_service_sub_tlvs_to_bytearray(service.sub_tlvs)
|
||||
|
||||
|
||||
def any_border_router_16():
|
||||
return random.getrandbits(16)
|
||||
|
||||
|
||||
def any_prf():
|
||||
return random.getrandbits(2)
|
||||
|
||||
|
||||
def any_route():
|
||||
return network_data.Route(any_border_router_16(), any_prf())
|
||||
|
||||
|
||||
def any_routes(count=None):
|
||||
if count is None:
|
||||
count = random.randint(0, 16)
|
||||
|
||||
return [any_route() for _ in xrange(6)]
|
||||
|
||||
|
||||
def any_has_route():
|
||||
return network_data.HasRoute(any_routes(), any_stable())
|
||||
|
||||
|
||||
def any_domain_id():
|
||||
return random.getrandbits(8)
|
||||
|
||||
|
||||
def any_prefix_length():
|
||||
return random.randint(1, 16)
|
||||
|
||||
|
||||
def any_prefix(prefix_length=None):
|
||||
if prefix_length is None:
|
||||
prefix_length = any_prefix_length()
|
||||
|
||||
return bytearray([random.getrandbits(8) for _ in xrange(prefix_length / 8)])
|
||||
|
||||
|
||||
def any_p():
|
||||
return random.getrandbits(1)
|
||||
|
||||
|
||||
def any_s():
|
||||
return random.getrandbits(1)
|
||||
|
||||
|
||||
def any_d():
|
||||
return random.getrandbits(1)
|
||||
|
||||
|
||||
def any_c():
|
||||
return random.getrandbits(1)
|
||||
|
||||
|
||||
def any_r():
|
||||
return random.getrandbits(1)
|
||||
|
||||
|
||||
def any_o():
|
||||
return random.getrandbits(1)
|
||||
|
||||
|
||||
def any_n():
|
||||
return random.getrandbits(1)
|
||||
|
||||
|
||||
def any_cid():
|
||||
return random.getrandbits(4)
|
||||
|
||||
|
||||
def any_context_length():
|
||||
return random.getrandbits(8)
|
||||
|
||||
|
||||
def any_border_router():
|
||||
return network_data.BorderRouter(any_border_router_16(), any_prf(), any_p(), any_s(), any_d(), any_c(), any_r(), any_o(), any_n(), any_stable())
|
||||
|
||||
|
||||
def any_lowpan_id():
|
||||
return network_data.LowpanId(any_c(), any_cid(), any_context_length(), any_stable())
|
||||
|
||||
|
||||
def any_prefix_sub_tlvs():
|
||||
creator = [
|
||||
any_has_route,
|
||||
any_border_router,
|
||||
any_lowpan_id
|
||||
]
|
||||
|
||||
sub_tlvs = []
|
||||
|
||||
for _id in xrange(random.randint(0, 16)):
|
||||
c = random.choice(creator)
|
||||
sub_tlvs.append(c())
|
||||
|
||||
return sub_tlvs
|
||||
|
||||
|
||||
def any_t():
|
||||
return random.getrandbits(1)
|
||||
|
||||
|
||||
def any_id():
|
||||
return random.getrandbits(4)
|
||||
|
||||
|
||||
def any_enterprise_number():
|
||||
return random.getrandbits(32)
|
||||
|
||||
|
||||
def any_service_data_length():
|
||||
return random.getrandbits(8)
|
||||
|
||||
|
||||
def any_service_data(data_length=None):
|
||||
if data_length is None:
|
||||
data_length = random.randint(0, 16)
|
||||
|
||||
return bytearray([random.getrandbits(8) for _ in xrange(data_length)])
|
||||
|
||||
|
||||
def any_server_16():
|
||||
return random.getrandbits(16)
|
||||
|
||||
|
||||
def any_server_data(data_length=None):
|
||||
if data_length is None:
|
||||
data_length = random.randint(0, 32)
|
||||
|
||||
return bytearray([random.getrandbits(8) for _ in xrange(data_length)])
|
||||
|
||||
|
||||
def any_server():
|
||||
return network_data.Server(any_server_16(), any_server_data(), any_stable())
|
||||
|
||||
|
||||
def any_service_sub_tlvs():
|
||||
creator = [
|
||||
any_server
|
||||
]
|
||||
|
||||
sub_tlvs = []
|
||||
|
||||
for _id in xrange(random.randint(0, 16)):
|
||||
c = random.choice(creator)
|
||||
sub_tlvs.append(c())
|
||||
|
||||
return sub_tlvs
|
||||
|
||||
|
||||
def any_stable():
|
||||
return random.getrandbits(1)
|
||||
|
||||
|
||||
class TestRoute(unittest.TestCase):
|
||||
|
||||
def test_should_return_border_router_16_value_when_border_router_16_property_is_called(self):
|
||||
# GIVEN
|
||||
border_router_16 = any_border_router_16()
|
||||
|
||||
route = network_data.Route(border_router_16, any_prf())
|
||||
|
||||
# WHEN
|
||||
actual_border_router_16 = route.border_router_16
|
||||
|
||||
# THEN
|
||||
self.assertEqual(border_router_16, actual_border_router_16)
|
||||
|
||||
def test_should_return_prf_value_when_prf_property_is_called(self):
|
||||
# GIVEN
|
||||
prf = any_prf()
|
||||
|
||||
route = network_data.Route(any_border_router_16(), prf)
|
||||
|
||||
# WHEN
|
||||
actual_prf = route.prf
|
||||
|
||||
# THEN
|
||||
self.assertEqual(prf, actual_prf)
|
||||
|
||||
|
||||
class TestRouteFactory(unittest.TestCase):
|
||||
|
||||
def test_should_create_Route_from_bytearray_when_parse_method_is_called(self):
|
||||
# GIVEN
|
||||
border_router_16 = any_border_router_16()
|
||||
prf = any_prf()
|
||||
|
||||
factory = network_data.RouteFactory()
|
||||
|
||||
data = convert_route_to_bytearray(network_data.Route(border_router_16, prf))
|
||||
|
||||
# WHEN
|
||||
actual_route = factory.parse(io.BytesIO(data), None)
|
||||
|
||||
# THEN
|
||||
self.assertTrue(isinstance(actual_route, network_data.Route))
|
||||
self.assertEqual(border_router_16, actual_route.border_router_16)
|
||||
self.assertEqual(prf, actual_route.prf)
|
||||
|
||||
|
||||
class TestRoutesFactory(unittest.TestCase):
|
||||
|
||||
def test_should_create_Route_list_from_bytearray_when_parse_method_is_called(self):
|
||||
# GIVEN
|
||||
routes = any_routes()
|
||||
|
||||
factory = network_data.RoutesFactory(network_data.RouteFactory())
|
||||
|
||||
data = convert_routes_to_bytearray(routes)
|
||||
# WHEN
|
||||
actual_routes = factory.parse(io.BytesIO(data), None)
|
||||
|
||||
# THEN
|
||||
self.assertTrue(isinstance(actual_routes, list))
|
||||
self.assertEqual(routes, actual_routes)
|
||||
|
||||
|
||||
class TestHasRoute(unittest.TestCase):
|
||||
|
||||
def test_should_return_routes_value_when_routes_property_is_called(self):
|
||||
# GIVEN
|
||||
routes = any_routes()
|
||||
|
||||
has_route = network_data.HasRoute(routes, any_stable())
|
||||
|
||||
# WHEN
|
||||
actual_routes = has_route.routes
|
||||
|
||||
# THEN
|
||||
self.assertEqual(routes, actual_routes)
|
||||
|
||||
def test_should_return_stable_value_when_stable_property_is_called(self):
|
||||
# GIVEN
|
||||
stable = any_stable()
|
||||
|
||||
has_route = network_data.HasRoute(any_routes(), stable)
|
||||
|
||||
# WHEN
|
||||
actual_stable = has_route.stable
|
||||
|
||||
# THEN
|
||||
self.assertEqual(stable, actual_stable)
|
||||
|
||||
|
||||
class TestHasRouteFactory(unittest.TestCase):
|
||||
|
||||
def test_should_create_HasRoute_from_bytearray_when_parse_method_is_called(self):
|
||||
# GIVEN
|
||||
routes = any_routes()
|
||||
stable = any_stable()
|
||||
|
||||
factory = network_data.HasRouteFactory(network_data.RoutesFactory(network_data.RouteFactory()))
|
||||
|
||||
data = convert_routes_to_bytearray(routes)
|
||||
|
||||
message_info = common.MessageInfo()
|
||||
message_info.stable = stable
|
||||
|
||||
# WHEN
|
||||
actual_has_route = factory.parse(io.BytesIO(data), message_info)
|
||||
|
||||
# THEN
|
||||
self.assertTrue(isinstance(actual_has_route, network_data.HasRoute))
|
||||
self.assertEqual(routes, actual_has_route.routes)
|
||||
self.assertEqual(stable, actual_has_route.stable)
|
||||
|
||||
|
||||
class TestPrefix(unittest.TestCase):
|
||||
|
||||
def test_should_return_domain_id_value_when_domain_id_property_is_called(self):
|
||||
# GIVEN
|
||||
domain_id = any_domain_id()
|
||||
|
||||
prefix = network_data.Prefix(domain_id, any_prefix_length(), any_prefix(), any_prefix_sub_tlvs(), any_stable())
|
||||
|
||||
# WHEN
|
||||
actual_domain_id = prefix.domain_id
|
||||
|
||||
# THEN
|
||||
self.assertEqual(domain_id, actual_domain_id)
|
||||
|
||||
def test_should_return_prefix_length_value_when_prefix_length_property_is_called(self):
|
||||
# GIVEN
|
||||
prefix_length = any_prefix_length()
|
||||
|
||||
prefix = network_data.Prefix(any_domain_id(), prefix_length, any_prefix(), any_prefix_sub_tlvs(), any_stable())
|
||||
|
||||
# WHEN
|
||||
actual_prefix_length = prefix.prefix_length
|
||||
|
||||
# THEN
|
||||
self.assertEqual(prefix_length, actual_prefix_length)
|
||||
|
||||
def test_should_return_prefix_value_when_prefix_property_is_called(self):
|
||||
# GIVEN
|
||||
prefix = any_prefix()
|
||||
|
||||
prefix_obj = network_data.Prefix(any_domain_id(), any_prefix_length(),
|
||||
prefix, any_prefix_sub_tlvs(), any_stable())
|
||||
|
||||
# WHEN
|
||||
actual_prefix = prefix_obj.prefix
|
||||
|
||||
# THEN
|
||||
self.assertEqual(prefix, actual_prefix)
|
||||
|
||||
def test_should_return_sub_tlvs_value_when_sub_tlvs_property_is_called(self):
|
||||
# GIVEN
|
||||
sub_tlvs = any_prefix_sub_tlvs()
|
||||
|
||||
prefix_obj = network_data.Prefix(any_domain_id(), any_prefix_length(), any_prefix(), sub_tlvs, any_stable())
|
||||
|
||||
# WHEN
|
||||
actual_sub_tlvs = prefix_obj.sub_tlvs
|
||||
|
||||
# THEN
|
||||
self.assertEqual(sub_tlvs, actual_sub_tlvs)
|
||||
|
||||
def test_should_return_stable_value_when_stable_property_is_called(self):
|
||||
# GIVEN
|
||||
stable = any_stable()
|
||||
|
||||
prefix_obj = network_data.Prefix(any_domain_id(), any_prefix_length(),
|
||||
any_prefix(), any_prefix_sub_tlvs(), stable)
|
||||
|
||||
# WHEN
|
||||
actual_stable = prefix_obj.stable
|
||||
|
||||
# THEN
|
||||
self.assertEqual(stable, actual_stable)
|
||||
|
||||
|
||||
class TestPrefixSubTlvsFactory(unittest.TestCase):
|
||||
|
||||
def test_should_create_SubTlvs_from_bytearray_when_parse_method_is_called(self):
|
||||
# GIVEN
|
||||
sub_tlvs = any_prefix_sub_tlvs()
|
||||
|
||||
factory = network_data.PrefixSubTlvsFactory(config.create_default_network_data_prefix_sub_tlvs_factories())
|
||||
|
||||
data = convert_prefix_sub_tlvs_to_bytearray(sub_tlvs)
|
||||
|
||||
# WHEN
|
||||
actual_sub_tlvs = factory.parse(io.BytesIO(data), common.MessageInfo())
|
||||
|
||||
# THEN
|
||||
self.assertTrue(isinstance(actual_sub_tlvs, list))
|
||||
self.assertEqual(sub_tlvs, actual_sub_tlvs)
|
||||
|
||||
|
||||
class TestPrefixFactory(unittest.TestCase):
|
||||
|
||||
def test_should_create_Prefix_from_bytearray_when_parse_method_is_called(self):
|
||||
# GIVEN
|
||||
domain_id = any_domain_id()
|
||||
prefix_length = any_prefix_length()
|
||||
prefix = any_prefix(prefix_length)
|
||||
sub_tlvs = any_prefix_sub_tlvs()
|
||||
|
||||
factory = network_data.PrefixFactory(config.create_default_network_data_prefix_sub_tlvs_factory())
|
||||
|
||||
data = bytearray([domain_id, prefix_length]) + prefix + convert_prefix_sub_tlvs_to_bytearray(sub_tlvs)
|
||||
|
||||
message_info = common.MessageInfo()
|
||||
|
||||
# WHEN
|
||||
actual_prefix = factory.parse(io.BytesIO(data), message_info)
|
||||
|
||||
# THEN
|
||||
self.assertTrue(isinstance(actual_prefix, network_data.Prefix))
|
||||
self.assertEqual(domain_id, actual_prefix.domain_id)
|
||||
self.assertEqual(prefix_length, actual_prefix.prefix_length)
|
||||
self.assertEqual(prefix, actual_prefix.prefix)
|
||||
self.assertEqual(sub_tlvs, actual_prefix.sub_tlvs)
|
||||
|
||||
|
||||
class TestBorderRouter(unittest.TestCase):
|
||||
|
||||
def test_should_return_border_router_16_value_when_border_router_16_property_is_called(self):
|
||||
# GIVEN
|
||||
border_router_16 = any_border_router_16()
|
||||
|
||||
border_router = network_data.BorderRouter(border_router_16, any_prf(
|
||||
), any_p(), any_s(), any_d(), any_c(), any_r(), any_o(), any_n(), any_stable())
|
||||
|
||||
# WHEN
|
||||
actual_border_router_16 = border_router.border_router_16
|
||||
|
||||
# THEN
|
||||
self.assertEqual(border_router_16, actual_border_router_16)
|
||||
|
||||
def test_should_return_prf_value_when_prf_property_is_called(self):
|
||||
# GIVEN
|
||||
prf = any_prf()
|
||||
|
||||
border_router = network_data.BorderRouter(any_border_router_16(
|
||||
), prf, any_p(), any_s(), any_d(), any_c(), any_r(), any_o(), any_n(), any_stable())
|
||||
|
||||
# WHEN
|
||||
actual_prf = border_router.prf
|
||||
|
||||
# THEN
|
||||
self.assertEqual(prf, actual_prf)
|
||||
|
||||
def test_should_return_p_value_when_p_property_is_called(self):
|
||||
# GIVEN
|
||||
p = any_p()
|
||||
|
||||
border_router = network_data.BorderRouter(any_border_router_16(), any_prf(
|
||||
), p, any_s(), any_d(), any_c(), any_r(), any_o(), any_n(), any_stable())
|
||||
|
||||
# WHEN
|
||||
actual_p = border_router.p
|
||||
|
||||
# THEN
|
||||
self.assertEqual(p, actual_p)
|
||||
|
||||
def test_should_return_s_value_when_s_property_is_called(self):
|
||||
# GIVEN
|
||||
s = any_s()
|
||||
|
||||
border_router = network_data.BorderRouter(any_border_router_16(), any_prf(
|
||||
), any_p(), s, any_d(), any_c(), any_r(), any_o(), any_n(), any_stable())
|
||||
|
||||
# WHEN
|
||||
actual_s = border_router.s
|
||||
|
||||
# THEN
|
||||
self.assertEqual(s, actual_s)
|
||||
|
||||
def test_should_return_d_value_when_d_property_is_called(self):
|
||||
# GIVEN
|
||||
d = any_d()
|
||||
|
||||
border_router = network_data.BorderRouter(any_border_router_16(), any_prf(
|
||||
), any_p(), any_s(), d, any_c(), any_r(), any_o(), any_n(), any_stable())
|
||||
|
||||
# WHEN
|
||||
actual_d = border_router.d
|
||||
|
||||
# THEN
|
||||
self.assertEqual(d, actual_d)
|
||||
|
||||
def test_should_return_c_value_when_c_property_is_called(self):
|
||||
# GIVEN
|
||||
c = any_c()
|
||||
|
||||
border_router = network_data.BorderRouter(any_border_router_16(), any_prf(
|
||||
), any_p(), any_s(), any_d(), c, any_r(), any_o(), any_n(), any_stable())
|
||||
|
||||
# WHEN
|
||||
actual_c = border_router.c
|
||||
|
||||
# THEN
|
||||
self.assertEqual(c, actual_c)
|
||||
|
||||
def test_should_return_r_value_when_r_property_is_called(self):
|
||||
# GIVEN
|
||||
r = any_r()
|
||||
|
||||
border_router = network_data.BorderRouter(any_border_router_16(), any_prf(
|
||||
), any_p(), any_s(), any_d(), any_c(), r, any_o(), any_n(), any_stable())
|
||||
|
||||
# WHEN
|
||||
actual_r = border_router.r
|
||||
|
||||
# THEN
|
||||
self.assertEqual(r, actual_r)
|
||||
|
||||
def test_should_return_o_value_when_o_property_is_called(self):
|
||||
# GIVEN
|
||||
o = any_o()
|
||||
|
||||
border_router = network_data.BorderRouter(any_border_router_16(), any_prf(
|
||||
), any_p(), any_s(), any_d(), any_c(), any_r(), o, any_n(), any_stable())
|
||||
|
||||
# WHEN
|
||||
actual_o = border_router.o
|
||||
|
||||
# THEN
|
||||
self.assertEqual(o, actual_o)
|
||||
|
||||
def test_should_return_n_value_when_n_property_is_called(self):
|
||||
# GIVEN
|
||||
n = any_n()
|
||||
|
||||
border_router = network_data.BorderRouter(any_border_router_16(), any_prf(
|
||||
), any_p(), any_s(), any_d(), any_c(), any_r(), any_o(), n, any_stable())
|
||||
|
||||
# WHEN
|
||||
actual_n = border_router.n
|
||||
|
||||
# THEN
|
||||
self.assertEqual(n, actual_n)
|
||||
|
||||
def test_should_return_stable_value_when_stable_property_is_called(self):
|
||||
# GIVEN
|
||||
stable = any_stable()
|
||||
|
||||
border_router = network_data.BorderRouter(any_border_router_16(), any_prf(
|
||||
), any_p(), any_s(), any_d(), any_c(), any_r(), any_o(), any_n(), stable)
|
||||
|
||||
# WHEN
|
||||
actual_stable = border_router.stable
|
||||
|
||||
# THEN
|
||||
self.assertEqual(stable, actual_stable)
|
||||
|
||||
|
||||
class TestBorderRouterFactory(unittest.TestCase):
|
||||
|
||||
def test_should_create_BorderRouter_from_bytearray_when_parse_method_is_called(self):
|
||||
# GIVEN
|
||||
border_router_16 = any_border_router_16()
|
||||
prf = any_prf()
|
||||
p = any_p()
|
||||
s = any_s()
|
||||
d = any_d()
|
||||
c = any_c()
|
||||
r = any_r()
|
||||
o = any_o()
|
||||
n = any_n()
|
||||
stable = any_stable()
|
||||
|
||||
factory = network_data.BorderRouterFactory()
|
||||
|
||||
data = convert_border_router_to_bytearray(
|
||||
network_data.BorderRouter(border_router_16, prf, p, s, d, c, r, o, n, stable))
|
||||
|
||||
message_info = common.MessageInfo()
|
||||
message_info.stable = stable
|
||||
|
||||
# WHEN
|
||||
actual_border_router = factory.parse(io.BytesIO(data), message_info)
|
||||
|
||||
# THEN
|
||||
self.assertTrue(isinstance(actual_border_router, network_data.BorderRouter))
|
||||
self.assertEqual(border_router_16, actual_border_router.border_router_16)
|
||||
self.assertEqual(prf, actual_border_router.prf)
|
||||
self.assertEqual(p, actual_border_router.p)
|
||||
self.assertEqual(s, actual_border_router.s)
|
||||
self.assertEqual(d, actual_border_router.d)
|
||||
self.assertEqual(c, actual_border_router.c)
|
||||
self.assertEqual(r, actual_border_router.r)
|
||||
self.assertEqual(o, actual_border_router.o)
|
||||
self.assertEqual(n, actual_border_router.n)
|
||||
self.assertEqual(stable, actual_border_router.stable)
|
||||
|
||||
|
||||
class TestLowpanId(unittest.TestCase):
|
||||
|
||||
def test_should_return_c_value_when_c_property_is_called(self):
|
||||
# GIVEN
|
||||
c = any_c()
|
||||
|
||||
lowpan_id = network_data.LowpanId(c, any_cid(), any_context_length(), any_stable())
|
||||
|
||||
# WHEN
|
||||
actual_c = lowpan_id.c
|
||||
|
||||
# THEN
|
||||
self.assertEqual(c, actual_c)
|
||||
|
||||
def test_should_return_cid_value_when_cid_property_is_called(self):
|
||||
# GIVEN
|
||||
cid = any_cid()
|
||||
|
||||
lowpan_id = network_data.LowpanId(any_c(), cid, any_context_length(), any_stable())
|
||||
|
||||
# WHEN
|
||||
actual_cid = lowpan_id.cid
|
||||
|
||||
# THEN
|
||||
self.assertEqual(cid, actual_cid)
|
||||
|
||||
def test_should_return_context_length_value_when_context_length_property_is_called(self):
|
||||
# GIVEN
|
||||
context_length = any_context_length()
|
||||
|
||||
lowpan_id = network_data.LowpanId(any_c(), any_cid(), context_length, any_stable())
|
||||
|
||||
# WHEN
|
||||
actual_context_length = lowpan_id.context_length
|
||||
|
||||
# THEN
|
||||
self.assertEqual(context_length, actual_context_length)
|
||||
|
||||
def test_should_return_stable_value_when_stable_property_is_called(self):
|
||||
# GIVEN
|
||||
stable = any_stable()
|
||||
|
||||
lowpan_id = network_data.LowpanId(any_c(), any_cid(), any_context_length(), stable)
|
||||
|
||||
# WHEN
|
||||
actual_stable = lowpan_id.stable
|
||||
|
||||
# THEN
|
||||
self.assertEqual(stable, actual_stable)
|
||||
|
||||
|
||||
class TestLowpanIdFactory(unittest.TestCase):
|
||||
|
||||
def test_should_create_LowpanId_from_bytearray_when_parse_method_is_called(self):
|
||||
# GIVEN
|
||||
c = any_c()
|
||||
cid = any_cid()
|
||||
context_length = any_context_length()
|
||||
stable = any_stable()
|
||||
|
||||
factory = network_data.LowpanIdFactory()
|
||||
|
||||
data = convert_lowpan_id_to_bytearray(network_data.LowpanId(c, cid, context_length, stable))
|
||||
|
||||
message_info = common.MessageInfo()
|
||||
message_info.stable = stable
|
||||
|
||||
# WHEN
|
||||
actual_lowpan_id = factory.parse(io.BytesIO(data), message_info)
|
||||
|
||||
# THEN
|
||||
self.assertTrue(isinstance(actual_lowpan_id, network_data.LowpanId))
|
||||
self.assertEqual(c, actual_lowpan_id.c)
|
||||
self.assertEqual(cid, actual_lowpan_id.cid)
|
||||
self.assertEqual(context_length, actual_lowpan_id.context_length)
|
||||
|
||||
|
||||
class TestService(unittest.TestCase):
|
||||
|
||||
def test_should_return_t_value_when_t_property_is_called(self):
|
||||
# GIVEN
|
||||
t = any_t()
|
||||
|
||||
service = network_data.Service(t, any_id(), any_enterprise_number(), any_service_data_length(),
|
||||
any_service_data(), any_service_sub_tlvs(), any_stable())
|
||||
|
||||
# WHEN
|
||||
actual_t = service.t
|
||||
|
||||
# THEN
|
||||
self.assertEqual(t, actual_t)
|
||||
|
||||
def test_should_return_id_value_when_id_property_is_called(self):
|
||||
# GIVEN
|
||||
_id = any_id()
|
||||
|
||||
service = network_data.Service(any_t(), _id, any_enterprise_number(), any_service_data_length(),
|
||||
any_service_data(), any_service_sub_tlvs(), any_stable())
|
||||
|
||||
# WHEN
|
||||
actual_id = service.id
|
||||
|
||||
# THEN
|
||||
self.assertEqual(_id, actual_id)
|
||||
|
||||
def test_should_return_enterprise_number_value_when_enterprise_number_property_is_called(self):
|
||||
# GIVEN
|
||||
enterprise_number = any_enterprise_number()
|
||||
|
||||
service = network_data.Service(any_t(), any_id(), enterprise_number, any_service_data_length(),
|
||||
any_service_data(), any_service_sub_tlvs(), any_stable())
|
||||
|
||||
# WHEN
|
||||
actual_enterprise_number = service.enterprise_number
|
||||
|
||||
# THEN
|
||||
self.assertEqual(enterprise_number, actual_enterprise_number)
|
||||
|
||||
def test_should_return_service_data_length_value_when_service_data_length_property_is_called(self):
|
||||
# GIVEN
|
||||
service_data_length = any_service_data_length()
|
||||
|
||||
service = network_data.Service(any_t(), any_id(), any_enterprise_number(), service_data_length,
|
||||
any_service_data(), any_service_sub_tlvs(), any_stable())
|
||||
|
||||
# WHEN
|
||||
actual_service_data_length = service.service_data_length
|
||||
|
||||
# THEN
|
||||
self.assertEqual(service_data_length, actual_service_data_length)
|
||||
|
||||
def test_should_return_service_data_value_when_service_data_property_is_called(self):
|
||||
# GIVEN
|
||||
service_data = any_service_data()
|
||||
|
||||
service = network_data.Service(any_t(), any_id(), any_enterprise_number(), any_service_data_length(),
|
||||
service_data, any_service_sub_tlvs(), any_stable())
|
||||
|
||||
# WHEN
|
||||
actual_service_data = service.service_data
|
||||
|
||||
# THEN
|
||||
self.assertEqual(service_data, actual_service_data)
|
||||
|
||||
def test_should_return_sub_tlvs_value_when_sub_tlvs_property_is_called(self):
|
||||
# GIVEN
|
||||
sub_tlvs = any_service_sub_tlvs()
|
||||
|
||||
service = network_data.Service(any_t(), any_id(), any_enterprise_number(), any_service_data_length(),
|
||||
any_service_data(), sub_tlvs, any_stable())
|
||||
|
||||
# WHEN
|
||||
actual_sub_tlvs = service.sub_tlvs
|
||||
|
||||
# THEN
|
||||
self.assertEqual(sub_tlvs, actual_sub_tlvs)
|
||||
|
||||
def test_should_return_stable_value_when_stable_property_is_called(self):
|
||||
# GIVEN
|
||||
stable = any_stable()
|
||||
|
||||
service = network_data.Service(any_t(), any_id(), any_enterprise_number(), any_service_data_length(),
|
||||
any_service_data(), any_service_sub_tlvs(), stable)
|
||||
|
||||
# WHEN
|
||||
actual_stable = service.stable
|
||||
|
||||
# THEN
|
||||
self.assertEqual(stable, actual_stable)
|
||||
|
||||
|
||||
class TestServiceSubTlvsFactory(unittest.TestCase):
|
||||
|
||||
def test_should_create_SubTlvs_from_bytearray_when_parse_method_is_called(self):
|
||||
# GIVEN
|
||||
sub_tlvs = any_service_sub_tlvs()
|
||||
|
||||
factory = network_data.ServiceSubTlvsFactory(config.create_default_network_data_service_sub_tlvs_factories())
|
||||
|
||||
data = convert_service_sub_tlvs_to_bytearray(sub_tlvs)
|
||||
|
||||
# WHEN
|
||||
actual_sub_tlvs = factory.parse(io.BytesIO(data), common.MessageInfo())
|
||||
|
||||
# THEN
|
||||
self.assertTrue(isinstance(actual_sub_tlvs, list))
|
||||
self.assertEqual(sub_tlvs, actual_sub_tlvs)
|
||||
|
||||
|
||||
class TestServiceFactory(unittest.TestCase):
|
||||
|
||||
def test_should_create_Service_from_bytearray_when_parse_method_is_called(self):
|
||||
# GIVEN
|
||||
t = any_t()
|
||||
_id = any_id()
|
||||
enterprise_number = any_enterprise_number()
|
||||
service_data_length = any_service_data_length()
|
||||
service_data = any_service_data(service_data_length)
|
||||
sub_tlvs = any_service_sub_tlvs()
|
||||
stable = any_stable()
|
||||
|
||||
factory = network_data.ServiceFactory(config.create_default_network_data_service_sub_tlvs_factory())
|
||||
|
||||
data = convert_service_to_bytearray(network_data.Service(
|
||||
t, _id, enterprise_number, service_data_length, service_data, sub_tlvs, stable))
|
||||
|
||||
message_info = common.MessageInfo()
|
||||
message_info.stable = stable
|
||||
|
||||
# WHEN
|
||||
actual_service = factory.parse(io.BytesIO(data), message_info)
|
||||
|
||||
# THEN
|
||||
self.assertTrue(isinstance(actual_service, network_data.Service))
|
||||
self.assertEqual(t, actual_service.t)
|
||||
self.assertEqual(_id, actual_service.id)
|
||||
self.assertEqual(enterprise_number, actual_service.enterprise_number)
|
||||
self.assertEqual(service_data_length, actual_service.service_data_length)
|
||||
self.assertEqual(service_data, actual_service.service_data)
|
||||
self.assertEqual(sub_tlvs, actual_service.sub_tlvs)
|
||||
|
||||
|
||||
class TestServer(unittest.TestCase):
|
||||
|
||||
def test_should_return_server_16_value_when_server_16_property_is_called(self):
|
||||
# GIVEN
|
||||
server_16 = any_server_16()
|
||||
|
||||
server = network_data.Server(server_16, any_server_data(), any_stable())
|
||||
|
||||
# WHEN
|
||||
actual_server_16 = server.server_16
|
||||
|
||||
# THEN
|
||||
self.assertEqual(server_16, actual_server_16)
|
||||
|
||||
def test_should_return_server_data_value_when_server_data_property_is_called(self):
|
||||
# GIVEN
|
||||
server_data = any_server_data()
|
||||
|
||||
server = network_data.Server(any_server_16(), server_data, any_stable())
|
||||
|
||||
# WHEN
|
||||
actual_server_data = server.server_data
|
||||
|
||||
# THEN
|
||||
self.assertEqual(server_data, actual_server_data)
|
||||
|
||||
def test_should_return_stable_value_when_stable_property_is_called(self):
|
||||
# GIVEN
|
||||
stable = any_stable()
|
||||
|
||||
server = network_data.Server(any_server_16(), any_server_data(), stable)
|
||||
|
||||
# WHEN
|
||||
actual_stable = server.stable
|
||||
|
||||
# THEN
|
||||
self.assertEqual(stable, actual_stable)
|
||||
|
||||
|
||||
class TestServerFactory(unittest.TestCase):
|
||||
|
||||
def test_should_create_Server_from_bytearray_when_parse_method_is_called(self):
|
||||
# GIVEN
|
||||
server_16 = any_server_16()
|
||||
server_data = any_server_data()
|
||||
stable = any_stable()
|
||||
|
||||
factory = network_data.ServerFactory()
|
||||
|
||||
data = convert_server_to_bytearray(network_data.Server(server_16, server_data, stable))
|
||||
|
||||
message_info = common.MessageInfo()
|
||||
message_info.stable = stable
|
||||
|
||||
# WHEN
|
||||
actual_server = factory.parse(io.BytesIO(data), message_info)
|
||||
|
||||
# THEN
|
||||
self.assertTrue(isinstance(actual_server, network_data.Server))
|
||||
self.assertEqual(server_16, actual_server.server_16)
|
||||
self.assertEqual(server_data, actual_server.server_data)
|
||||
self.assertEqual(stable, actual_server.stable)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
unittest.main()
|
||||
Reference in New Issue
Block a user