Add sniffer. Extend some tests according to Thread Certification Test Plan V1.1 Preview 1612. (#957)

This commit is contained in:
PFierek
2016-11-10 12:59:25 +08:00
committed by Jonathan Hui
parent b78376e5a9
commit bd99468e85
31 changed files with 13577 additions and 14 deletions
+13
View File
@@ -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 ] || {
+10
View File
@@ -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()
+151
View File
@@ -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)
+247
View File
@@ -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
+263
View File
@@ -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
+301
View File
@@ -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
+359
View File
@@ -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
+494
View File
@@ -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)
+163
View File
@@ -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)
+170
View File
@@ -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()
+431
View File
@@ -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
+235
View File
@@ -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()