diff --git a/src/cli/cli.cpp b/src/cli/cli.cpp index 1d87b951e..a078a44ab 100644 --- a/src/cli/cli.cpp +++ b/src/cli/cli.cpp @@ -1011,7 +1011,7 @@ void Interpreter::ProcessPanId(int argc, char *argv[]) if (argc == 0) { - sServer->OutputFormat("%d\r\n", otGetPanId(mInstance)); + sServer->OutputFormat("%04x\r\n", otGetPanId(mInstance)); } else { diff --git a/src/core/thread/meshcop_dataset.cpp b/src/core/thread/meshcop_dataset.cpp index 510de2f6d..9591501b2 100644 --- a/src/core/thread/meshcop_dataset.cpp +++ b/src/core/thread/meshcop_dataset.cpp @@ -406,6 +406,32 @@ void Dataset::SetTimestamp(const Timestamp &aTimestamp) Set(timestampTlv.tlv); } +int Dataset::Compare(const Dataset &aCompare) const +{ + const Timestamp *thisTimestamp = GetTimestamp(); + const Timestamp *compareTimestamp = aCompare.GetTimestamp(); + int rval; + + if (compareTimestamp == NULL && thisTimestamp == NULL) + { + rval = 0; + } + else if (compareTimestamp == NULL && thisTimestamp != NULL) + { + rval = -1; + } + else if (compareTimestamp != NULL && thisTimestamp == NULL) + { + rval = 1; + } + else + { + rval = thisTimestamp->Compare(*compareTimestamp); + } + + return rval; +} + ThreadError Dataset::Set(const Tlv &aTlv) { ThreadError error = kThreadError_None; diff --git a/src/core/thread/meshcop_dataset.hpp b/src/core/thread/meshcop_dataset.hpp index 3c1486058..07872257e 100644 --- a/src/core/thread/meshcop_dataset.hpp +++ b/src/core/thread/meshcop_dataset.hpp @@ -116,6 +116,18 @@ public: */ void SetTimestamp(const Timestamp &aTimestamp); + /** + * This method compares this dataset to another based on the timestamp. + * + * @param[in] aCompare A reference to the timestamp to compare. + * + * @retval -1 if @p aCompare is older than this dataset. + * @retval 0 if @p aCompare is equal to this dataset. + * @retval 1 if @p aCompare is newer than this dataset. + * + */ + int Compare(const Dataset &aCompare) const; + /** * This method sets a TLV in the Dataset. * diff --git a/src/core/thread/meshcop_dataset_manager.cpp b/src/core/thread/meshcop_dataset_manager.cpp index a53fdf054..1750712d2 100644 --- a/src/core/thread/meshcop_dataset_manager.cpp +++ b/src/core/thread/meshcop_dataset_manager.cpp @@ -58,32 +58,17 @@ DatasetManager::DatasetManager(ThreadNetif &aThreadNetif, const Tlv::Type aType, const char *aUriGet): mLocal(aType), mNetwork(aType), + mCoapServer(aThreadNetif.GetCoapServer()), mMle(aThreadNetif.GetMle()), mNetif(aThreadNetif), mNetworkDataLeader(aThreadNetif.GetNetworkDataLeader()), - mResourceSet(aUriSet, &DatasetManager::HandleSet, this), - mResourceGet(aUriGet, &DatasetManager::HandleGet, this), mTimer(aThreadNetif.GetIp6().mTimerScheduler, &DatasetManager::HandleTimer, this), mSocket(aThreadNetif.GetIp6().mUdp), mUriSet(aUriSet), - mUriGet(aUriGet), - mCoapServer(aThreadNetif.GetCoapServer()) + mUriGet(aUriGet) { } -void DatasetManager::StartLeader(void) -{ - mNetwork = mLocal; - mCoapServer.AddResource(mResourceSet); - mCoapServer.AddResource(mResourceGet); -} - -void DatasetManager::StopLeader(void) -{ - mCoapServer.RemoveResource(mResourceSet); - mCoapServer.RemoveResource(mResourceGet); -} - ThreadError DatasetManager::Set(const otOperationalDataset &aDataset, uint8_t &aFlags) { ThreadError error = kThreadError_None; @@ -113,6 +98,13 @@ exit: return error; } +ThreadError DatasetManager::Clear(uint8_t &aFlags) +{ + mNetwork.Clear(); + HandleNetworkUpdate(aFlags); + return kThreadError_None; +} + ThreadError DatasetManager::Set(const Dataset &aDataset, uint8_t &aFlags) { mNetwork.Set(aDataset); @@ -135,18 +127,10 @@ exit: void DatasetManager::HandleNetworkUpdate(uint8_t &aFlags) { - const Timestamp *localTimestamp; - const Timestamp *networkTimestamp; - int compare; + int compare = mLocal.Compare(mNetwork); aFlags = kFlagNetworkUpdated; - networkTimestamp = mNetwork.GetTimestamp(); - assert(networkTimestamp != NULL); - - localTimestamp = mLocal.GetTimestamp(); - compare = (localTimestamp == NULL) ? 1 : localTimestamp->Compare(*networkTimestamp); - if (compare > 0) { mLocal = mNetwork; @@ -165,9 +149,9 @@ void DatasetManager::HandleTimer(void *aContext) void DatasetManager::HandleTimer(void) { - VerifyOrExit(mMle.IsAttached() && ((mNetwork.GetTimestamp() == NULL) || - (mLocal.GetTimestamp() && mNetwork.GetTimestamp()->Compare(*mLocal.GetTimestamp()) > 0)), ;); - VerifyOrExit(mLocal.Get(Tlv::kDelayTimer) != NULL, ;); + VerifyOrExit(mMle.IsAttached(),); + + VerifyOrExit(mLocal.Compare(mNetwork) < 0,); Register(); mTimer.Start(1000); @@ -199,6 +183,12 @@ ThreadError DatasetManager::Register(void) header.AppendUriPathOptions(mUriSet); header.Finalize(); + if (strcmp(mUriSet, OPENTHREAD_URI_PENDING_SET) == 0) + { + PendingDataset *pending = static_cast(this); + pending->UpdateDelayTimer(); + } + VerifyOrExit((message = mSocket.NewMessage(0)) != NULL, error = kThreadError_NoBufs); SuccessOrExit(error = message->Append(header.GetBytes(), header.GetLength())); SuccessOrExit(error = message->Append(mLocal.GetBytes(), mLocal.GetSize())); @@ -246,13 +236,31 @@ exit: return; } -void DatasetManager::HandleSet(void *aContext, Coap::Header &aHeader, Message &aMessage, - const Ip6::MessageInfo &aMessageInfo) +void DatasetManager::Get(Coap::Header &aHeader, Message &aMessage, const Ip6::MessageInfo &aMessageInfo) { - static_cast(aContext)->HandleSet(aHeader, aMessage, aMessageInfo); + Tlv tlv; + uint16_t offset = aMessage.GetOffset(); + uint8_t tlvs[Dataset::kMaxSize]; + uint8_t length = 0; + + while (offset < aMessage.GetLength()) + { + aMessage.Read(offset, sizeof(tlv), &tlv); + + if (tlv.GetType() == Tlv::kGet) + { + length = tlv.GetLength(); + aMessage.Read(offset + sizeof(Tlv), length, tlvs); + break; + } + + offset += sizeof(tlv) + tlv.GetLength(); + } + + SendGetResponse(aHeader, aMessageInfo, tlvs, length); } -void DatasetManager::HandleSet(Coap::Header &aHeader, Message &aMessage, const Ip6::MessageInfo &aMessageInfo) +ThreadError DatasetManager::Set(Coap::Header &aHeader, Message &aMessage, const Ip6::MessageInfo &aMessageInfo) { Tlv tlv; Timestamp timestamp; @@ -355,37 +363,7 @@ exit: SendSetResponse(aHeader, aMessageInfo, state); } - return; -} - -void DatasetManager::HandleGet(void *aContext, Coap::Header &aHeader, Message &aMessage, - const Ip6::MessageInfo &aMessageInfo) -{ - static_cast(aContext)->HandleGet(aHeader, aMessage, aMessageInfo); -} - -void DatasetManager::HandleGet(Coap::Header &aHeader, Message &aMessage, const Ip6::MessageInfo &aMessageInfo) -{ - Tlv tlv; - uint16_t offset = aMessage.GetOffset(); - uint8_t tlvs[Dataset::kMaxSize]; - uint8_t length = 0; - - while (offset < aMessage.GetLength()) - { - aMessage.Read(offset, sizeof(tlv), &tlv); - - if (tlv.GetType() == Tlv::kGet) - { - length = tlv.GetLength(); - aMessage.Read(offset + sizeof(Tlv), length, tlvs); - break; - } - - offset += sizeof(tlv) + tlv.GetLength(); - } - - SendGetResponse(aHeader, aMessageInfo, tlvs, length); + return state == StateTlv::kAccept ? kThreadError_None : kThreadError_Drop; } ThreadError DatasetManager::SendSetRequest(const otOperationalDataset &aDataset, const uint8_t *aTlvs, uint8_t aLength) @@ -639,10 +617,36 @@ exit: } ActiveDataset::ActiveDataset(ThreadNetif &aThreadNetif): - DatasetManager(aThreadNetif, Tlv::kActiveTimestamp, OPENTHREAD_URI_ACTIVE_SET, OPENTHREAD_URI_ACTIVE_GET) + DatasetManager(aThreadNetif, Tlv::kActiveTimestamp, OPENTHREAD_URI_ACTIVE_SET, OPENTHREAD_URI_ACTIVE_GET), + mResourceGet(OPENTHREAD_URI_ACTIVE_GET, &ActiveDataset::HandleGet, this), + mResourceSet(OPENTHREAD_URI_ACTIVE_SET, &ActiveDataset::HandleSet, this) { } +void ActiveDataset::StartLeader(void) +{ + mNetwork = mLocal; + mCoapServer.AddResource(mResourceGet); + mCoapServer.AddResource(mResourceSet); +} + +void ActiveDataset::StopLeader(void) +{ + mCoapServer.RemoveResource(mResourceGet); + mCoapServer.RemoveResource(mResourceSet); +} + +ThreadError ActiveDataset::Clear(void) +{ + ThreadError error = kThreadError_None; + uint8_t flags; + + SuccessOrExit(error = DatasetManager::Clear(flags)); + +exit: + return error; +} + ThreadError ActiveDataset::Set(const otOperationalDataset &aDataset) { ThreadError error = kThreadError_None; @@ -680,6 +684,32 @@ exit: return error; } +void ActiveDataset::HandleGet(void *aContext, Coap::Header &aHeader, Message &aMessage, + const Ip6::MessageInfo &aMessageInfo) +{ + static_cast(aContext)->HandleGet(aHeader, aMessage, aMessageInfo); +} + +void ActiveDataset::HandleGet(Coap::Header &aHeader, Message &aMessage, const Ip6::MessageInfo &aMessageInfo) +{ + DatasetManager::Get(aHeader, aMessage, aMessageInfo); +} + +void ActiveDataset::HandleSet(void *aContext, Coap::Header &aHeader, Message &aMessage, + const Ip6::MessageInfo &aMessageInfo) +{ + static_cast(aContext)->HandleSet(aHeader, aMessage, aMessageInfo); +} + +void ActiveDataset::HandleSet(Coap::Header &aHeader, Message &aMessage, const Ip6::MessageInfo &aMessageInfo) +{ + SuccessOrExit(DatasetManager::Set(aHeader, aMessage, aMessageInfo)); + ApplyConfiguration(); + +exit: + return; +} + ThreadError ActiveDataset::ApplyConfiguration(void) { ThreadError error = kThreadError_None; @@ -755,10 +785,38 @@ ThreadError ActiveDataset::ApplyConfiguration(void) PendingDataset::PendingDataset(ThreadNetif &aThreadNetif): DatasetManager(aThreadNetif, Tlv::kPendingTimestamp, OPENTHREAD_URI_PENDING_SET, OPENTHREAD_URI_PENDING_GET), - mTimer(aThreadNetif.GetIp6().mTimerScheduler, PendingDataset::HandleTimer, this) + mResourceGet(OPENTHREAD_URI_PENDING_GET, &PendingDataset::HandleGet, this), + mResourceSet(OPENTHREAD_URI_PENDING_SET, &PendingDataset::HandleSet, this), + mTimer(aThreadNetif.GetIp6().mTimerScheduler, &PendingDataset::HandleTimer, this) { } +void PendingDataset::StartLeader(void) +{ + mNetwork = mLocal; + mNetworkTime = mLocalTime; + mCoapServer.AddResource(mResourceGet); + mCoapServer.AddResource(mResourceSet); +} + +void PendingDataset::StopLeader(void) +{ + mCoapServer.RemoveResource(mResourceGet); + mCoapServer.RemoveResource(mResourceSet); +} + +ThreadError PendingDataset::Clear(void) +{ + ThreadError error = kThreadError_None; + uint8_t flags; + + SuccessOrExit(error = DatasetManager::Clear(flags)); + ResetDelayTimer(flags); + +exit: + return error; +} + ThreadError PendingDataset::Set(const otOperationalDataset &aDataset) { ThreadError error = kThreadError_None; @@ -784,10 +842,30 @@ exit: return error; } -void PendingDataset::StartLeader(void) +void PendingDataset::HandleGet(void *aContext, Coap::Header &aHeader, Message &aMessage, + const Ip6::MessageInfo &aMessageInfo) { - DatasetManager::StartLeader(); - mNetworkTime = mLocalTime; + static_cast(aContext)->HandleGet(aHeader, aMessage, aMessageInfo); +} + +void PendingDataset::HandleGet(Coap::Header &aHeader, Message &aMessage, const Ip6::MessageInfo &aMessageInfo) +{ + DatasetManager::Get(aHeader, aMessage, aMessageInfo); +} + +void PendingDataset::HandleSet(void *aContext, Coap::Header &aHeader, Message &aMessage, + const Ip6::MessageInfo &aMessageInfo) +{ + static_cast(aContext)->HandleSet(aHeader, aMessage, aMessageInfo); +} + +void PendingDataset::HandleSet(Coap::Header &aHeader, Message &aMessage, const Ip6::MessageInfo &aMessageInfo) +{ + SuccessOrExit(DatasetManager::Set(aHeader, aMessage, aMessageInfo)); + ResetDelayTimer(kFlagLocalUpdated | kFlagNetworkUpdated); + +exit: + return; } void PendingDataset::ResetDelayTimer(uint8_t aFlags) diff --git a/src/core/thread/meshcop_dataset_manager.hpp b/src/core/thread/meshcop_dataset_manager.hpp index adf9bbfd5..153ba919e 100644 --- a/src/core/thread/meshcop_dataset_manager.hpp +++ b/src/core/thread/meshcop_dataset_manager.hpp @@ -53,9 +53,6 @@ namespace MeshCoP { class DatasetManager { public: - void StartLeader(void); - void StopLeader(void); - Dataset &GetLocal(void) { return mLocal; } Dataset &GetNetwork(void) { return mNetwork; } @@ -71,6 +68,8 @@ protected: DatasetManager(ThreadNetif &aThreadNetif, const Tlv::Type aType, const char *aUriSet, const char *aUriGet); + ThreadError Clear(uint8_t &aFlags); + ThreadError Set(const otOperationalDataset &aDataset, uint8_t &aFlags); ThreadError Set(const Dataset &aDataset, uint8_t &aFlags); @@ -78,9 +77,14 @@ protected: ThreadError Set(const Timestamp &aTimestamp, const Message &aMessage, uint16_t aOffset, uint8_t aLength, uint8_t &aFlags); + void Get(Coap::Header &aHeader, Message &aMessage, const Ip6::MessageInfo &aMessageInfo); + + ThreadError Set(Coap::Header &aHeader, Message &aMessage, const Ip6::MessageInfo &aMessageInfo); + Dataset mLocal; Dataset mNetwork; + Coap::Server &mCoapServer; Mle::Mle &mMle; ThreadNetif &mNetif; NetworkData::Leader &mNetworkDataLeader; @@ -89,14 +93,6 @@ private: static void HandleUdpReceive(void *aContext, otMessage aMessage, const otMessageInfo *aMessageInfo); void HandleUdpReceive(Message &aMessage, const Ip6::MessageInfo &aMessageInfo); - static void HandleSet(void *aContext, Coap::Header &aHeader, Message &aMessage, - const Ip6::MessageInfo &aMessageInfo); - void HandleSet(Coap::Header &aHeader, Message &aMessage, const Ip6::MessageInfo &aMessageInfo); - - static void HandleGet(void *aContext, Coap::Header &aHeader, Message &aMessage, - const Ip6::MessageInfo &aMessageInfo); - void HandleGet(Coap::Header &aHeader, Message &aMessage, const Ip6::MessageInfo &aMessageInfo); - static void HandleTimer(void *aContext); void HandleTimer(void); @@ -107,8 +103,6 @@ private: void SendGetResponse(const Coap::Header &aRequestHeader, const Ip6::MessageInfo &aMessageInfo, uint8_t *aTlvs, uint8_t aLength); - Coap::Resource mResourceSet; - Coap::Resource mResourceGet; Timer mTimer; Ip6::UdpSocket mSocket; @@ -117,8 +111,6 @@ private: const char *mUriSet; const char *mUriGet; - - Coap::Server &mCoapServer; }; class ActiveDataset: public DatasetManager @@ -126,6 +118,12 @@ class ActiveDataset: public DatasetManager public: ActiveDataset(ThreadNetif &aThreadNetif); + void StartLeader(void); + + void StopLeader(void); + + ThreadError Clear(void); + ThreadError Set(const otOperationalDataset &aDataset); ThreadError Set(const Dataset &aDataset); @@ -133,6 +131,18 @@ public: ThreadError Set(const Timestamp &aTimestamp, const Message &aMessage, uint16_t aOffset, uint8_t aLength); ThreadError ApplyConfiguration(void); + +private: + static void HandleGet(void *aContext, Coap::Header &aHeader, Message &aMessage, + const Ip6::MessageInfo &aMessageInfo); + void HandleGet(Coap::Header &aHeader, Message &aMessage, const Ip6::MessageInfo &aMessageInfo); + + static void HandleSet(void *aContext, Coap::Header &aHeader, Message &aMessage, + const Ip6::MessageInfo &aMessageInfo); + void HandleSet(Coap::Header &aHeader, Message &aMessage, const Ip6::MessageInfo &aMessageInfo); + + Coap::Resource mResourceGet; + Coap::Resource mResourceSet; }; class PendingDataset: public DatasetManager @@ -142,6 +152,10 @@ public: void StartLeader(void); + void StopLeader(void); + + ThreadError Clear(void); + ThreadError Set(const otOperationalDataset &aDataset); ThreadError Set(const Timestamp &aTimestamp, const Message &aMessage, uint16_t aOffset, uint8_t aLength); @@ -149,12 +163,23 @@ public: void UpdateDelayTimer(void); private: + static void HandleGet(void *aContext, Coap::Header &aHeader, Message &aMessage, + const Ip6::MessageInfo &aMessageInfo); + void HandleGet(Coap::Header &aHeader, Message &aMessage, const Ip6::MessageInfo &aMessageInfo); + + static void HandleSet(void *aContext, Coap::Header &aHeader, Message &aMessage, + const Ip6::MessageInfo &aMessageInfo); + void HandleSet(Coap::Header &aHeader, Message &aMessage, const Ip6::MessageInfo &aMessageInfo); + static void HandleTimer(void *aContext); void HandleTimer(void); void ResetDelayTimer(uint8_t aFlags); void UpdateDelayTimer(Dataset &aDataset, uint32_t &aStartTime); + Coap::Resource mResourceGet; + Coap::Resource mResourceSet; + Timer mTimer; uint32_t mLocalTime; uint32_t mNetworkTime; diff --git a/src/core/thread/mle.cpp b/src/core/thread/mle.cpp index a9648cef2..e3699a2a6 100644 --- a/src/core/thread/mle.cpp +++ b/src/core/thread/mle.cpp @@ -1853,7 +1853,7 @@ ThreadError Mle::HandleDataResponse(const Message &aMessage, const Ip6::MessageI } else { - mNetif.GetActiveDataset().GetNetwork().Clear(); + mNetif.GetActiveDataset().Clear(); } // Pending Dataset @@ -1864,7 +1864,7 @@ ThreadError Mle::HandleDataResponse(const Message &aMessage, const Ip6::MessageI } else { - mNetif.GetPendingDataset().GetNetwork().Clear(); + mNetif.GetPendingDataset().Clear(); } mRetrieveNewNetworkData = false; @@ -2102,6 +2102,10 @@ ThreadError Mle::HandleChildIdResponse(const Message &aMessage, const Ip6::Messa mNetif.GetActiveDataset().Set(activeTimestamp, aMessage, offset + sizeof(tlv), tlv.GetLength()); } } + else + { + mNetif.GetActiveDataset().Clear(); + } // Pending Timestamp if (Tlv::GetTlv(aMessage, Tlv::kPendingTimestamp, sizeof(pendingTimestamp), pendingTimestamp) == kThreadError_None) @@ -2115,6 +2119,10 @@ ThreadError Mle::HandleChildIdResponse(const Message &aMessage, const Ip6::Messa mNetif.GetPendingDataset().Set(pendingTimestamp, aMessage, offset + sizeof(tlv), tlv.GetLength()); } } + else + { + mNetif.GetPendingDataset().Clear(); + } // Parent Attach Success mParentRequestTimer.Stop(); diff --git a/tests/scripts/Makefile.am b/tests/scripts/Makefile.am index ac7476b28..42101ab51 100644 --- a/tests/scripts/Makefile.am +++ b/tests/scripts/Makefile.am @@ -104,6 +104,7 @@ EXTRA_DIST = \ thread-cert/Cert_7_1_05_BorderRouterAsRouter.py \ thread-cert/Cert_8_1_01_Commissioning.py \ thread-cert/Cert_8_1_02_Commissioning.py \ + thread-cert/Cert_9_2_07_DelayTimer.py \ thread-cert/Cert_9_2_12_Announce.py \ thread-cert/Cert_9_2_13_EnergyScan.py \ thread-cert/Cert_9_2_14_PanIdQuery.py \ @@ -223,6 +224,7 @@ check_SCRIPTS += \ thread-cert/Cert_7_1_05_BorderRouterAsRouter.py \ thread-cert/Cert_8_1_01_Commissioning.py \ thread-cert/Cert_8_1_02_Commissioning.py \ + thread-cert/Cert_9_2_07_DelayTimer.py \ thread-cert/Cert_9_2_12_Announce.py \ thread-cert/Cert_9_2_13_EnergyScan.py \ thread-cert/Cert_9_2_14_PanIdQuery.py \ @@ -244,6 +246,7 @@ TESTS = \ XFAIL_NCP_TESTS = \ thread-cert/Cert_8_1_01_Commissioning.py \ thread-cert/Cert_8_1_02_Commissioning.py \ + thread-cert/Cert_9_2_07_DelayTimer.py \ thread-cert/Cert_9_2_12_Announce.py \ thread-cert/Cert_9_2_13_EnergyScan.py \ thread-cert/Cert_9_2_14_PanIdQuery.py \ diff --git a/tests/scripts/thread-cert/Cert_9_2_07_DelayTimer.py b/tests/scripts/thread-cert/Cert_9_2_07_DelayTimer.py new file mode 100755 index 000000000..962e008a0 --- /dev/null +++ b/tests/scripts/thread-cert/Cert_9_2_07_DelayTimer.py @@ -0,0 +1,125 @@ +#!/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 time +import unittest + +import node + +PANID_INIT = 0xface + +COMMISSIONER = 1 +LEADER = 2 +ROUTER = 3 + +LEADER_ACTIVE_TIMESTAMP = 10 +ROUTER_ACTIVE_TIMESTAMP = 20 +ROUTER_PENDING_TIMESTAMP = 30 +ROUTER_PENDING_ACTIVE_TIMESTAMP = 21 + +COMMISSIONER_PENDING_CHANNEL = 2 +COMMISSIONER_PENDING_PANID = 0xafce + +class Cert_9_2_7_DelayTimer(unittest.TestCase): + def setUp(self): + self.nodes = {} + for i in range(1,4): + self.nodes[i] = node.Node(i) + + self.nodes[COMMISSIONER].set_panid(PANID_INIT) + self.nodes[COMMISSIONER].set_mode('rsdn') + self.nodes[COMMISSIONER].add_whitelist(self.nodes[LEADER].get_addr64()) + self.nodes[COMMISSIONER].enable_whitelist() + self.nodes[COMMISSIONER].set_router_selection_jitter(1) + + self.nodes[LEADER].set_active_dataset(LEADER_ACTIVE_TIMESTAMP) + self.nodes[LEADER].set_panid(PANID_INIT) + self.nodes[LEADER].set_mode('rsdn') + self.nodes[LEADER].set_partition_id(0xffffffff) + self.nodes[LEADER].add_whitelist(self.nodes[COMMISSIONER].get_addr64()) + self.nodes[LEADER].enable_whitelist() + self.nodes[LEADER].set_router_selection_jitter(1) + + self.nodes[ROUTER].set_active_dataset(ROUTER_ACTIVE_TIMESTAMP) + self.nodes[ROUTER].set_pending_dataset(ROUTER_PENDING_TIMESTAMP, ROUTER_PENDING_ACTIVE_TIMESTAMP) + self.nodes[ROUTER].set_panid(PANID_INIT) + self.nodes[ROUTER].set_mode('rsdn') + self.nodes[ROUTER].set_partition_id(0) + self.nodes[ROUTER].enable_whitelist() + self.nodes[ROUTER].set_router_selection_jitter(1) + + def tearDown(self): + for node in list(self.nodes.values()): + node.stop() + del self.nodes + + def test(self): + self.nodes[LEADER].start() + self.nodes[LEADER].set_state('leader') + self.assertEqual(self.nodes[LEADER].get_state(), 'leader') + + self.nodes[COMMISSIONER].start() + time.sleep(5) + self.assertEqual(self.nodes[COMMISSIONER].get_state(), 'router') + + self.nodes[ROUTER].start() + time.sleep(5) + self.assertEqual(self.nodes[ROUTER].get_state(), 'leader') + + self.nodes[LEADER].add_whitelist(self.nodes[ROUTER].get_addr64()) + self.nodes[ROUTER].add_whitelist(self.nodes[LEADER].get_addr64()) + + time.sleep(30) + self.assertEqual(self.nodes[COMMISSIONER].get_state(), 'router') + self.assertEqual(self.nodes[LEADER].get_state(), 'leader') + self.assertEqual(self.nodes[ROUTER].get_state(), 'router') + + self.nodes[COMMISSIONER].send_mgmt_pending_set(pending_timestamp=40, + active_timestamp=80, + delay_timer=10000, + channel=COMMISSIONER_PENDING_CHANNEL, + panid=COMMISSIONER_PENDING_PANID) + time.sleep(20) + self.assertEqual(self.nodes[LEADER].get_panid(), COMMISSIONER_PENDING_PANID) + self.assertEqual(self.nodes[COMMISSIONER].get_panid(), COMMISSIONER_PENDING_PANID) + self.assertEqual(self.nodes[ROUTER].get_panid(), COMMISSIONER_PENDING_PANID) + + self.assertEqual(self.nodes[LEADER].get_channel(), COMMISSIONER_PENDING_CHANNEL) + self.assertEqual(self.nodes[COMMISSIONER].get_channel(), COMMISSIONER_PENDING_CHANNEL) + self.assertEqual(self.nodes[ROUTER].get_channel(), COMMISSIONER_PENDING_CHANNEL) + + ipaddrs = self.nodes[ROUTER].get_addrs() + for ipaddr in ipaddrs: + if ipaddr[0:4] != 'fe80': + break + + self.assertTrue(self.nodes[LEADER].ping(ipaddr)) + +if __name__ == '__main__': + unittest.main() diff --git a/tests/scripts/thread-cert/node.py b/tests/scripts/thread-cert/node.py index b7f5e2ee0..39384ec33 100755 --- a/tests/scripts/thread-cert/node.py +++ b/tests/scripts/thread-cert/node.py @@ -215,6 +215,14 @@ class Node: self.pexpect.expect('Done') return addr + def get_channel(self): + self.send_command('channel') + i = self.pexpect.expect('(\d+)\r\n') + if i == 0: + channel = int(self.pexpect.match.groups()[0]) + self.pexpect.expect('Done') + return channel + def set_channel(self, channel): cmd = 'channel %d' % channel self.send_command(cmd) @@ -258,16 +266,30 @@ class Node: def get_panid(self): self.send_command('panid') - i = self.pexpect.expect('([0-9a-fA-F]{16})') + i = self.pexpect.expect('([0-9a-fA-F]{4})') if i == 0: - panid = self.pexpect.match.groups()[0] + panid = int(self.pexpect.match.groups()[0], 16) self.pexpect.expect('Done') + return panid def set_panid(self, panid): cmd = 'panid %d' % panid self.send_command(cmd) self.pexpect.expect('Done') + def get_partition_id(self): + self.send_command('leaderpartitionid') + i = self.pexpect.expect('(\d+)\r\n') + if i == 0: + weight = self.pexpect.match.groups()[0] + self.pexpect.expect('Done') + return weight + + def set_partition_id(self, partition_id): + cmd = 'leaderpartitionid %d' % partition_id + self.send_command(cmd) + self.pexpect.expect('Done') + def set_router_upgrade_threshold(self, threshold): cmd = 'routerupgradethreshold %d' % threshold self.send_command(cmd) @@ -446,10 +468,57 @@ class Node: self.send_command('dataset commit active') self.pexpect.expect('Done') + def set_pending_dataset(self, pendingtimestamp, activetimestamp, panid=None, channel=None): + self.send_command('dataset clear') + self.pexpect.expect('Done') + + cmd = 'dataset pendingtimestamp %d' % pendingtimestamp + self.send_command(cmd) + self.pexpect.expect('Done') + + cmd = 'dataset activetimestamp %d' % activetimestamp + self.send_command(cmd) + self.pexpect.expect('Done') + + if panid != None: + cmd = 'dataset panid %d' % panid + self.send_command(cmd) + self.pexpect.expect('Done') + + if channel != None: + cmd = 'dataset channel %d' % channel + self.send_command(cmd) + self.pexpect.expect('Done') + + self.send_command('dataset commit pending') + self.pexpect.expect('Done') + def announce_begin(self, mask, count, period, ipaddr): cmd = 'commissioner announce ' + str(mask) + ' ' + str(count) + ' ' + str(period) + ' ' + ipaddr self.send_command(cmd) self.pexpect.expect('Done') + def send_mgmt_pending_set(self, pending_timestamp=None, active_timestamp=None, delay_timer=None, channel=None, + panid=None): + cmd = 'dataset mgmtsetcommand pending ' + + if pending_timestamp != None: + cmd += 'pendingtimestamp %d ' % pending_timestamp + + if active_timestamp != None: + cmd += 'activetimestamp %d ' % active_timestamp + + if delay_timer != None: + cmd += 'delaytimer %d ' % delay_timer + + if channel != None: + cmd += 'channel %d ' % channel + + if panid != None: + cmd += 'panid %d ' % panid + + self.send_command(cmd) + self.pexpect.expect('Done') + if __name__ == '__main__': unittest.main()