diff --git a/examples/platforms/posix/logging.c b/examples/platforms/posix/logging.c index e3c762a67..736c4d497 100644 --- a/examples/platforms/posix/logging.c +++ b/examples/platforms/posix/logging.c @@ -107,6 +107,10 @@ void otPlatLog(otLogLevel aLogLevel, otLogRegion aLogRegion, const char *aFormat case kLogRegionNcp: fprintf(stderr, "NCP "); break; + + case kLogRegionMeshCoP: + fprintf(stderr, "MCOP "); + break; } va_start(args, aFormat); diff --git a/include/platform/logging.h b/include/platform/logging.h index 543835cb6..03d0d57df 100644 --- a/include/platform/logging.h +++ b/include/platform/logging.h @@ -86,6 +86,7 @@ typedef enum otLogRegion kLogRegionMac = 7, ///< IEEE 802.15.4 MAC kLogRegionMem = 8, ///< Memory kLogRegionNcp = 9, ///< NCP + kLogRegionMeshCoP = 10, ///< Mesh Commissioning Protocol } otLogRegion; /** diff --git a/src/cli/Makefile.am b/src/cli/Makefile.am index bbed129b2..30f1bbec0 100644 --- a/src/cli/Makefile.am +++ b/src/cli/Makefile.am @@ -39,12 +39,14 @@ libopenthread_cli_a_CPPFLAGS = \ libopenthread_cli_a_SOURCES = \ cli.cpp \ + cli_dataset.cpp \ cli_uart.cpp \ cli_udp.cpp \ $(NULL) noinst_HEADERS = \ cli.hpp \ + cli_dataset.hpp \ cli_uart.hpp \ cli_server.hpp \ cli_udp.hpp \ diff --git a/src/cli/cli.cpp b/src/cli/cli.cpp index 53d13b739..31ae84d9f 100644 --- a/src/cli/cli.cpp +++ b/src/cli/cli.cpp @@ -39,6 +39,7 @@ #include #include "cli.hpp" +#include "cli_dataset.hpp" #include #include @@ -56,6 +57,7 @@ const struct Command Interpreter::sCommands[] = { "childtimeout", &ProcessChildTimeout }, { "contextreusedelay", &ProcessContextIdReuseDelay }, { "counter", &ProcessCounters }, + { "dataset", &ProcessDataset }, { "discover", &ProcessDiscover }, { "eidcache", &ProcessEidCache }, { "extaddr", &ProcessExtAddress }, @@ -354,6 +356,13 @@ void Interpreter::ProcessCounters(int argc, char *argv[]) } } +void Interpreter::ProcessDataset(int argc, char *argv[]) +{ + ThreadError error; + error = Dataset::Process(argc, argv, *sServer); + AppendResult(error); +} + void Interpreter::ProcessDiscover(int argc, char *argv[]) { ThreadError error = kThreadError_None; diff --git a/src/cli/cli.hpp b/src/cli/cli.hpp index fafe10e70..f86406cff 100644 --- a/src/cli/cli.hpp +++ b/src/cli/cli.hpp @@ -78,6 +78,9 @@ public: */ static void ProcessLine(char *aBuf, uint16_t aBufLength, Server &aServer); + static ThreadError ParseLong(char *argv, long &value); + static int Hex2Bin(const char *aHex, uint8_t *aBin, uint16_t aBinLength); + private: enum { @@ -93,6 +96,7 @@ private: static void ProcessChildTimeout(int argc, char *argv[]); static void ProcessContextIdReuseDelay(int argc, char *argv[]); static void ProcessCounters(int argc, char *argv[]); + static void ProcessDataset(int argc, char *argv[]); static void ProcessDiscover(int argc, char *argv[]); static void ProcessEidCache(int argc, char *argv[]); static void ProcessExtAddress(int argc, char *argv[]); @@ -130,8 +134,6 @@ private: static void HandleEchoResponse(void *aContext, Message &aMessage, const Ip6::MessageInfo &aMessageInfo); static void HandlePingTimer(void *aContext); static void HandleActiveScanResult(otActiveScanResult *aResult); - static int Hex2Bin(const char *aHex, uint8_t *aBin, uint16_t aBinLength); - static ThreadError ParseLong(char *argv, long &value); static const struct Command sCommands[]; static otNetifAddress sAddress; diff --git a/src/cli/cli_dataset.cpp b/src/cli/cli_dataset.cpp new file mode 100644 index 000000000..a6dc0771b --- /dev/null +++ b/src/cli/cli_dataset.cpp @@ -0,0 +1,386 @@ +/* + * Copyright (c) 2016, Nest Labs, Inc. + * 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. + */ + +/** + * @file + * This file implements the CLI interpreter. + */ + +#include +#include +#include + +#include +#include + +#include "cli.hpp" +#include "cli_dataset.hpp" +#include +#include +#include + +using Thread::Encoding::BigEndian::HostSwap16; +using Thread::Encoding::BigEndian::HostSwap32; + +namespace Thread { + +extern ThreadNetif *sThreadNetif; + +namespace Cli { + +const DatasetCommand Dataset::sCommands[] = +{ + { "help", &ProcessHelp }, + { "active", &ProcessActive }, + { "activetimestamp", &ProcessActiveTimestamp }, + { "channel", &ProcessChannel }, + { "clear", &ProcessClear }, + { "commit", &ProcessCommit }, + { "delay", &ProcessDelay }, + { "extpanid", &ProcessExtPanId }, + { "masterkey", &ProcessMasterKey }, + { "meshlocalprefix", &ProcessMeshLocalPrefix }, + { "networkname", &ProcessNetworkName }, + { "panid", &ProcessPanId }, + { "pending", &ProcessPending }, + { "timestamp", &ProcessTimestamp }, +}; + +Server *Dataset::sServer; +MeshCoP::Dataset sDataset; + +void Dataset::OutputBytes(const uint8_t *aBytes, uint8_t aLength) +{ + for (int i = 0; i < aLength; i++) + { + sServer->OutputFormat("%02x", aBytes[i]); + } +} + +ThreadError Dataset::Print(MeshCoP::Dataset &aDataset) +{ + MeshCoP::Tlv *tlv; + + sServer->OutputFormat("Timestamp: %d\r\n", aDataset.GetTimestamp().GetSeconds()); + + if ((tlv = aDataset.Get(MeshCoP::Tlv::kActiveTimestamp)) != NULL) + { + MeshCoP::ActiveTimestampTlv *tmp = static_cast(tlv); + sServer->OutputFormat("Active Timestamp: %d\r\n", tmp->GetSeconds()); + } + + if ((tlv = aDataset.Get(MeshCoP::Tlv::kChannel)) != NULL) + { + MeshCoP::ChannelTlv *tmp = static_cast(tlv); + sServer->OutputFormat("Channel: %d\r\n", tmp->GetChannel()); + } + + if ((tlv = aDataset.Get(MeshCoP::Tlv::kDelayTimer)) != NULL) + { + MeshCoP::DelayTimerTlv *tmp = static_cast(tlv); + sServer->OutputFormat("Delay: %d\r\n", tmp->GetDelayTimer()); + } + + if ((tlv = aDataset.Get(MeshCoP::Tlv::kExtendedPanId)) != NULL) + { + MeshCoP::ExtendedPanIdTlv *tmp = static_cast(tlv); + sServer->OutputFormat("Ext PAN ID: "); + OutputBytes(tmp->GetExtendedPanId(), OT_EXT_PAN_ID_SIZE); + sServer->OutputFormat("\r\n"); + } + + if ((tlv = aDataset.Get(MeshCoP::Tlv::kMeshLocalPrefix)) != NULL) + { + MeshCoP::MeshLocalPrefixTlv *tmp = static_cast(tlv); + const uint8_t *prefix = tmp->GetMeshLocalPrefix(); + sServer->OutputFormat("Mesh Local Prefix: %x:%x:%x:%x/64\r\n", + (static_cast(prefix[0]) << 16) | prefix[1], + (static_cast(prefix[2]) << 16) | prefix[3], + (static_cast(prefix[4]) << 16) | prefix[5], + (static_cast(prefix[6]) << 16) | prefix[7]); + } + + if ((tlv = aDataset.Get(MeshCoP::Tlv::kNetworkMasterKey)) != NULL) + { + MeshCoP::NetworkMasterKeyTlv *tmp = static_cast(tlv); + sServer->OutputFormat("Master Key: "); + OutputBytes(tmp->GetNetworkMasterKey(), tmp->GetLength()); + sServer->OutputFormat("\r\n"); + } + + if ((tlv = aDataset.Get(MeshCoP::Tlv::kNetworkName)) != NULL) + { + MeshCoP::NetworkNameTlv *tmp = static_cast(tlv); + sServer->OutputFormat("Network Name: "); + sServer->OutputFormat("%.*s\r\n", OT_NETWORK_NAME_SIZE, tmp->GetNetworkName()); + } + + if ((tlv = aDataset.Get(MeshCoP::Tlv::kPanId)) != NULL) + { + MeshCoP::PanIdTlv *tmp = static_cast(tlv); + sServer->OutputFormat("PAN ID: 0x%04x\r\n", tmp->GetPanId()); + } + + return kThreadError_None; +} + +ThreadError Dataset::Process(int argc, char *argv[], Server &aServer) +{ + ThreadError error = kThreadError_None; + + sServer = &aServer; + + if (argc == 0) + { + ExitNow(error = Print(sDataset)); + } + + for (unsigned int i = 0; i < sizeof(sCommands) / sizeof(sCommands[0]); i++) + { + if (strcmp(argv[0], sCommands[i].mName) == 0) + { + error = sCommands[i].mCommand(argc - 1, argv + 1); + break; + } + } + +exit: + return error; +} + +ThreadError Dataset::ProcessHelp(int argc, char *argv[]) +{ + for (unsigned int i = 0; i < sizeof(sCommands) / sizeof(sCommands[0]); i++) + { + sServer->OutputFormat("%s\r\n", sCommands[i].mName); + } + + (void)argc; + (void)argv; + return kThreadError_None; +} + +ThreadError Dataset::ProcessActive(int argc, char *argv[]) +{ + (void)argc; + (void)argv; + return Print(sThreadNetif->GetActiveDataset().GetLocal()); +} + +ThreadError Dataset::ProcessActiveTimestamp(int argc, char *argv[]) +{ + ThreadError error = kThreadError_None; + MeshCoP::ActiveTimestampTlv tlv; + long value; + + VerifyOrExit(argc > 0, ;); + + SuccessOrExit(error = Interpreter::ParseLong(argv[0], value)); + tlv.Init(); + tlv.SetSeconds(value); + tlv.SetTicks(0); + tlv.SetAuthoritative(false); + error = sDataset.Set(tlv); + +exit: + return error; +} + +ThreadError Dataset::ProcessChannel(int argc, char *argv[]) +{ + ThreadError error = kThreadError_None; + MeshCoP::ChannelTlv tlv; + long value; + + VerifyOrExit(argc > 0, error = kThreadError_Parse); + SuccessOrExit(error = Interpreter::ParseLong(argv[0], value)); + tlv.Init(); + tlv.SetChannelPage(0); + tlv.SetChannel(value); + error = sDataset.Set(tlv); + +exit: + return error; +} + +ThreadError Dataset::ProcessClear(int argc, char *argv[]) +{ + sDataset.Clear(); + (void)argc; + (void)argv; + return kThreadError_None; +} + +ThreadError Dataset::ProcessCommit(int argc, char *argv[]) +{ + ThreadError error = kThreadError_None; + + VerifyOrExit(argc > 0, ;); + + if (strcmp(argv[0], "active") == 0) + { + sThreadNetif->GetActiveDataset().Set(sDataset); + } + else if (strcmp(argv[0], "pending") == 0) + { + sThreadNetif->GetPendingDataset().Set(sDataset); + } + else + { + ExitNow(error = kThreadError_Parse); + } + + sThreadNetif->GetNetworkDataLeader().IncrementVersion(); + sThreadNetif->GetNetworkDataLeader().IncrementStableVersion(); + +exit: + return error; +} + +ThreadError Dataset::ProcessDelay(int argc, char *argv[]) +{ + ThreadError error = kThreadError_None; + MeshCoP::DelayTimerTlv tlv; + long value; + + VerifyOrExit(argc > 0, error = kThreadError_Parse); + SuccessOrExit(error = Interpreter::ParseLong(argv[0], value)); + tlv.Init(); + tlv.SetDelayTimer(value); + error = sDataset.Set(tlv); + +exit: + return error; +} + +ThreadError Dataset::ProcessExtPanId(int argc, char *argv[]) +{ + ThreadError error = kThreadError_None; + MeshCoP::ExtendedPanIdTlv tlv; + uint8_t extPanId[8]; + + VerifyOrExit(argc > 0, error = kThreadError_Parse); + VerifyOrExit(Interpreter::Hex2Bin(argv[0], extPanId, sizeof(extPanId)) >= 0, error = kThreadError_Parse); + + tlv.Init(); + tlv.SetExtendedPanId(extPanId); + error = sDataset.Set(tlv); + +exit: + return error; +} + +ThreadError Dataset::ProcessMasterKey(int argc, char *argv[]) +{ + ThreadError error = kThreadError_None; + MeshCoP::NetworkMasterKeyTlv tlv; + int8_t keyLength; + uint8_t key[16]; + + VerifyOrExit(argc > 0, error = kThreadError_Parse); + VerifyOrExit((keyLength = Interpreter::Hex2Bin(argv[0], key, sizeof(key))) == 16, error = kThreadError_Parse); + tlv.Init(); + tlv.SetNetworkMasterKey(key); + error = sDataset.Set(tlv); + +exit: + return error; +} + +ThreadError Dataset::ProcessMeshLocalPrefix(int argc, char *argv[]) +{ + ThreadError error = kThreadError_None; + MeshCoP::MeshLocalPrefixTlv tlv; + struct otIp6Address prefix; + + VerifyOrExit(argc > 0, error = kThreadError_Parse); + SuccessOrExit(error = otIp6AddressFromString(argv[0], &prefix)); + tlv.Init(); + tlv.SetMeshLocalPrefix(prefix.mFields.m8); + error = sDataset.Set(tlv); + +exit: + return error; +} + +ThreadError Dataset::ProcessNetworkName(int argc, char *argv[]) +{ + ThreadError error = kThreadError_None; + MeshCoP::NetworkNameTlv tlv; + + VerifyOrExit(argc > 0, error = kThreadError_Parse); + tlv.Init(); + tlv.SetNetworkName(argv[0]); + error = sDataset.Set(tlv); + +exit: + return error; +} + +ThreadError Dataset::ProcessPanId(int argc, char *argv[]) +{ + ThreadError error = kThreadError_None; + MeshCoP::PanIdTlv tlv; + long value; + + VerifyOrExit(argc > 0, error = kThreadError_Parse); + SuccessOrExit(error = Interpreter::ParseLong(argv[0], value)); + tlv.Init(); + tlv.SetPanId(value); + error = sDataset.Set(tlv); + +exit: + return error; +} + +ThreadError Dataset::ProcessPending(int argc, char *argv[]) +{ + (void)argc; + (void)argv; + return Print(sThreadNetif->GetPendingDataset().GetLocal()); +} + +ThreadError Dataset::ProcessTimestamp(int argc, char *argv[]) +{ + ThreadError error = kThreadError_None; + MeshCoP::Timestamp timestamp; + long value; + + VerifyOrExit(argc > 0, error = kThreadError_Parse); + + SuccessOrExit(error = Interpreter::ParseLong(argv[0], value)); + timestamp.Init(); + timestamp.SetSeconds(value); + sDataset.SetTimestamp(timestamp); + +exit: + return error; +} + +} // namespace Cli +} // namespace Thread diff --git a/src/cli/cli_dataset.hpp b/src/cli/cli_dataset.hpp new file mode 100644 index 000000000..790ce4aea --- /dev/null +++ b/src/cli/cli_dataset.hpp @@ -0,0 +1,97 @@ +/* + * Copyright (c) 2016, Nest Labs, Inc. + * 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. + */ + +/** + * @file + * This file contains definitions for the CLI interpreter. + */ + +#ifndef CLI_DATASET_HPP_ +#define CLI_DATASET_HPP_ + +#include + +#include +#include + +namespace Thread { +namespace Cli { + +/** + * This structure represents a CLI command. + * + */ +struct DatasetCommand +{ + const char *mName; ///< A pointer to the command string. + ThreadError(*mCommand)(int argc, char *argv[]); ///< A function pointer to process the command. +}; + +/** + * This class implements the CLI interpreter. + * + */ +class Dataset +{ +public: + /** + * This method interprets a list of CLI arguments. + * + * @param[in] argc The number of elements in argv. + * @param[in] argv A pointer to an array of command line arguments. + * + */ + static ThreadError Process(int argc, char *argv[], Server &aServer); + +private: + static void OutputBytes(const uint8_t *aBytes, uint8_t aLength); + static ThreadError Print(MeshCoP::Dataset &aDataset); + + static ThreadError ProcessHelp(int argc, char *argv[]); + static ThreadError ProcessActive(int argc, char *argv[]); + static ThreadError ProcessActiveTimestamp(int argc, char *argv[]); + static ThreadError ProcessChannel(int argc, char *argv[]); + static ThreadError ProcessClear(int argc, char *argv[]); + static ThreadError ProcessCommit(int argc, char *argv[]); + static ThreadError ProcessDelay(int argc, char *argv[]); + static ThreadError ProcessExtPanId(int argc, char *argv[]); + static ThreadError ProcessMasterKey(int argc, char *argv[]); + static ThreadError ProcessMeshLocalPrefix(int argc, char *argv[]); + static ThreadError ProcessNetworkName(int argc, char *argv[]); + static ThreadError ProcessPanId(int argc, char *argv[]); + static ThreadError ProcessPending(int argc, char *argv[]); + static ThreadError ProcessTimestamp(int argc, char *argv[]); + + static const DatasetCommand sCommands[]; + static Server *sServer; +}; + +} // namespace Cli +} // namespace Thread + +#endif // CLI_DATASET_HPP_ diff --git a/src/core/Makefile.am b/src/core/Makefile.am index 40dde3326..5278263cf 100644 --- a/src/core/Makefile.am +++ b/src/core/Makefile.am @@ -59,6 +59,9 @@ libopenthread_a_SOURCES = \ thread/key_manager.cpp \ thread/link_quality.cpp \ thread/lowpan.cpp \ + thread/meshcop_dataset.cpp \ + thread/meshcop_dataset_manager.cpp \ + thread/meshcop_tlvs.cpp \ thread/mesh_forwarder.cpp \ thread/mle.cpp \ thread/mle_router.cpp \ @@ -102,6 +105,9 @@ noinst_HEADERS = \ thread/lowpan.hpp \ thread/meshcop_tlvs.hpp \ thread/mesh_forwarder.hpp \ + thread/meshcop_dataset.hpp \ + thread/meshcop_dataset_manager.hpp \ + thread/meshcop_tlvs.hpp \ thread/mle.hpp \ thread/mle_constants.hpp \ thread/mle_router.hpp \ diff --git a/src/core/common/logging.hpp b/src/core/common/logging.hpp index 04abcc6e8..32c73b33f 100644 --- a/src/core/common/logging.hpp +++ b/src/core/common/logging.hpp @@ -160,6 +160,57 @@ extern "C" { #define otLogDebgApi(aFormat, ...) #endif +/** + * @def otLogCritMeshCoP + * + * This method generates a log with level critical for the MLE region. + * + * @param[in] aFormat A pointer to the format string. + * @param[in] ... Arguments for the format specification. + * + */ + +/** + * @def otLogWarnMeshCoP + * + * This method generates a log with level warning for the MLE region. + * + * @param[in] aFormat A pointer to the format string. + * @param[in] ... Arguments for the format specification. + * + */ + +/** + * @def otLogInfoMeshCoP + * + * This method generates a log with level info for the MLE region. + * + * @param[in] aFormat A pointer to the format string. + * @param[in] ... Arguments for the format specification. + * + */ + +/** + * @def otLogDebgMeshCoP + * + * This method generates a log with level debug for the MLE region. + * + * @param[in] aFormat A pointer to the format string. + * @param[in] ... Arguments for the format specification. + * + */ +#ifdef OPENTHREAD_CONFIG_LOG_MLE +#define otLogCritMeshCoP(aFormat, ...) otLogCrit(kLogRegionMeshCoP, aFormat, ## __VA_ARGS__) +#define otLogWarnMeshCoP(aFormat, ...) otLogWarn(kLogRegionMeshCoP, aFormat, ## __VA_ARGS__) +#define otLogInfoMeshCoP(aFormat, ...) otLogInfo(kLogRegionMeshCoP, aFormat, ## __VA_ARGS__) +#define otLogDebgMeshCoP(aFormat, ...) otLogDebg(kLogRegionMeshCoP, aFormat, ## __VA_ARGS__) +#else +#define otLogCritMeshCoP(aFormat, ...) +#define otLogWarnMeshCoP(aFormat, ...) +#define otLogInfoMeshCoP(aFormat, ...) +#define otLogDebgMeshCoP(aFormat, ...) +#endif + /** * @def otLogCritMle * diff --git a/src/core/thread/meshcop_dataset.cpp b/src/core/thread/meshcop_dataset.cpp new file mode 100644 index 000000000..6c75f07e3 --- /dev/null +++ b/src/core/thread/meshcop_dataset.cpp @@ -0,0 +1,163 @@ +/* + * Copyright (c) 2016, Nest Labs, Inc. + * 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. + */ + +/** + * @file + * This file implements common methods for manipulating MeshCoP Datasets. + * + */ + +#include + +#include +#include +#include + +namespace Thread { +namespace MeshCoP { + +Dataset::Dataset(void) : + mLength(0) +{ + mTimestamp.Init(); +} + +void Dataset::Clear(void) +{ + mTimestamp.Init(); + mLength = 0; +} + +Tlv *Dataset::Get(Tlv::Type aType) +{ + Tlv *cur = reinterpret_cast(mTlvs); + Tlv *end = reinterpret_cast(mTlvs + mLength); + Tlv *rval = NULL; + + while (cur < end) + { + if (cur->GetType() == aType) + { + ExitNow(rval = cur); + } + + cur = cur->GetNext(); + } + +exit: + return rval; +} + +const Tlv *Dataset::Get(Tlv::Type aType) const +{ + const Tlv *cur = reinterpret_cast(mTlvs); + const Tlv *end = reinterpret_cast(mTlvs + mLength); + const Tlv *rval = NULL; + + while (cur < end) + { + if (cur->GetType() == aType) + { + ExitNow(rval = cur); + } + + cur = cur->GetNext(); + } + +exit: + return rval; +} + +const Timestamp &Dataset::GetTimestamp(void) const +{ + return mTimestamp; +} + +void Dataset::SetTimestamp(const Timestamp &aTimestamp) +{ + mTimestamp = aTimestamp; +} + +ThreadError Dataset::Set(const Tlv &aTlv) +{ + ThreadError error = kThreadError_None; + uint16_t bytesAvailable = sizeof(mTlvs) - mLength; + Tlv *old = Get(aTlv.GetType()); + + if (old != NULL) + { + bytesAvailable += sizeof(Tlv) + old->GetLength(); + } + + VerifyOrExit(sizeof(Tlv) + aTlv.GetLength() <= bytesAvailable, error = kThreadError_NoBufs); + + // remove old TLV + if (old != NULL) + { + Remove(reinterpret_cast(old), sizeof(Tlv) + old->GetLength()); + } + + // add new TLV + memcpy(mTlvs + mLength, &aTlv, sizeof(Tlv) + aTlv.GetLength()); + mLength += sizeof(Tlv) + aTlv.GetLength(); + +exit: + return error; +} + +ThreadError Dataset::Set(const Message &aMessage, uint16_t aOffset, uint16_t aLength) +{ + ThreadError error = kThreadError_None; + + VerifyOrExit(aLength <= kMaxSize, error = kThreadError_InvalidArgs); + aMessage.Read(aOffset, aLength, mTlvs); + mLength = aLength; + +exit: + return error; +} + +void Dataset::Remove(Tlv::Type aType) +{ + Tlv *tlv; + + VerifyOrExit((tlv = Get(aType)) != NULL, ;); + Remove(reinterpret_cast(tlv), sizeof(Tlv) + tlv->GetLength()); + +exit: + return; +} + +void Dataset::Remove(uint8_t *aStart, uint8_t aLength) +{ + memmove(aStart, aStart + aLength, mLength - ((aStart - mTlvs) + aLength)); + mLength -= aLength; +} + +} // namespace MeshCoP +} // namespace Thread diff --git a/src/core/thread/meshcop_dataset.hpp b/src/core/thread/meshcop_dataset.hpp new file mode 100644 index 000000000..3c62229b8 --- /dev/null +++ b/src/core/thread/meshcop_dataset.hpp @@ -0,0 +1,136 @@ +/* + * Copyright (c) 2016, Nest Labs, Inc. + * 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. + */ + +/** + * @file + * This file includes definitions for managing MeshCoP Datasets. + * + */ + +#ifndef MESHCOP_DATASET_HPP_ +#define MESHCOP_DATASET_HPP_ + +#include +#include + +namespace Thread { +namespace MeshCoP { + +class Dataset +{ +public: + enum + { + kMaxSize = 256, ///< Maximum size of MeshCoP Dataset (bytes) + }; + + /** + * This constructor initializes the object. + * + */ + Dataset(void); + + /** + * This method clears the Dataset. + * + */ + void Clear(void); + + /** + * This method returns a pointer to the TLV. + * + * @returns A pointer to the TLV or NULL if none is found. + * + */ + Tlv *Get(Tlv::Type aType); + + /** + * This method returns a pointer to the TLV. + * + * @returns A pointer to the TLV or NULL if none is found. + * + */ + const Tlv *Get(Tlv::Type aType) const; + + /** + * This method returns a pointer to the byte representation of the Dataset. + * + * @returns A pointer to the byte representation of the Dataset. + * + */ + const uint8_t *GetBytes(void) const { return mTlvs; } + + /** + * This method returns the Dataset size in bytes. + * + * @returns The Dataset size in bytes. + * + */ + uint16_t GetSize(void) const { return mLength; } + + /** + * This method returns a reference to the Timestamp. + * + * @returns A reference to the Timestamp. + * + */ + const Timestamp &GetTimestamp(void) const; + + /** + * This method sets the Timestamp value. + * + */ + void SetTimestamp(const Timestamp &aTimestamp); + + /** + * This method sets a TLV in the Dataset. + * + * @param[in] aTlv A reference to the TLV. + * + * @retval kThreadError_None Successfully set the TLV. + * @retval kThreadError_NoBufs Could not set the TLV due to insufficient buffer space. + * + */ + ThreadError Set(const Tlv &aTlv); + + ThreadError Set(const Message &aMessage, uint16_t aOffset, uint16_t aLength); + + void Remove(Tlv::Type aType); + +private: + void Remove(uint8_t *aStart, uint8_t aLength); + + Timestamp mTimestamp; ///< Active or Pending Timestamp + uint8_t mTlvs[kMaxSize]; ///< The Dataset buffer + uint16_t mLength; ///< The number of valid bytes in @var mTlvs +}; + +} // namespace MeshCoP +} // namespace Thread + +#endif // MESHCOP_DATASET_HPP_ diff --git a/src/core/thread/meshcop_dataset_manager.cpp b/src/core/thread/meshcop_dataset_manager.cpp new file mode 100644 index 000000000..f5ecac438 --- /dev/null +++ b/src/core/thread/meshcop_dataset_manager.cpp @@ -0,0 +1,540 @@ +/* + * Copyright (c) 2016, Nest Labs, Inc. + * 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. + */ + +/** + * @file + * This file implements common methods for manipulating MeshCoP Datasets. + * + */ + +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +namespace Thread { +namespace MeshCoP { + +DatasetManager::DatasetManager(ThreadNetif &aThreadNetif, const char *aUri): + mMle(aThreadNetif.GetMle()), + mNetif(aThreadNetif), + mNetworkDataLeader(aThreadNetif.GetNetworkDataLeader()), + mResource(aUri, &HandleSet, this), + mTimer(&HandleTimer, this), + mUri(aUri), + mCoapServer(aThreadNetif.GetCoapServer()) +{ + mCoapServer.AddResource(mResource); +} + +ThreadError DatasetManager::Set(const Dataset &aDataset, uint8_t &aFlags) +{ + ThreadError error = kThreadError_None; + + aFlags = 0; + + VerifyOrExit(mNetwork.GetTimestamp().Compare(aDataset.GetTimestamp()) > 0, error = kThreadError_InvalidArgs); + + mLocal = aDataset; + aFlags |= kFlagLocalUpdated; + + switch (mMle.GetDeviceState()) + { + case Mle::kDeviceStateChild: + case Mle::kDeviceStateRouter: + mTimer.Start(1000); + break; + + case Mle::kDeviceStateLeader: + mNetwork = mLocal; + aFlags |= kFlagNetworkUpdated; + mNetworkDataLeader.IncrementVersion(); + mNetworkDataLeader.IncrementStableVersion(); + break; + + default: + break; + } + +exit: + return error; +} + +ThreadError DatasetManager::Set(const Timestamp &aTimestamp, const Message &aMessage, + uint16_t aOffset, uint16_t aLength, uint8_t &aFlags) +{ + ThreadError error = kThreadError_None; + int compare; + + aFlags = 0; + + SuccessOrExit(error = mNetwork.Set(aMessage, aOffset, aLength)); + mNetwork.SetTimestamp(aTimestamp); + aFlags |= kFlagNetworkUpdated; + + compare = mLocal.GetTimestamp().Compare(aTimestamp); + + if (compare > 0) + { + SuccessOrExit(error = mLocal.Set(aMessage, aOffset, aLength)); + mLocal.SetTimestamp(aTimestamp); + aFlags |= kFlagLocalUpdated; + } + else if (compare < 0) + { + mTimer.Start(1000); + } + +exit: + return error; +} + +ThreadError DatasetManager::ApplyLocalToNetwork(void) +{ + mNetwork = mLocal; + return kThreadError_None; +} + +void DatasetManager::HandleTimer(void *aContext) +{ + DatasetManager *obj = static_cast(aContext); + obj->HandleTimer(); +} + +void DatasetManager::HandleTimer(void) +{ + VerifyOrExit(mMle.IsAttached() && mNetwork.GetTimestamp().Compare(mLocal.GetTimestamp()) > 0, ;); + VerifyOrExit(mLocal.Get(Tlv::kDelayTimer) != NULL, ;); + + Register(); + mTimer.Start(1000); + +exit: + return; +} + +ThreadError DatasetManager::Register(void) +{ + ThreadError error = kThreadError_None; + Coap::Header header; + Message *message; + Ip6::Address leader; + Ip6::MessageInfo messageInfo; + ActiveTimestampTlv timestamp; + + mSocket.Open(&HandleUdpReceive, this); + + for (size_t i = 0; i < sizeof(mCoapToken); i++) + { + mCoapToken[i] = otPlatRandomGet(); + } + + header.Init(); + header.SetVersion(1); + header.SetType(Coap::Header::kTypeConfirmable); + header.SetCode(Coap::Header::kCodePost); + header.SetMessageId(++mCoapMessageId); + header.SetToken(mCoapToken, sizeof(mCoapToken)); + header.AppendUriPathOptions(mUri); + header.AppendContentFormatOption(Coap::Header::kApplicationOctetStream); + header.Finalize(); + + VerifyOrExit((message = Ip6::Udp::NewMessage(0)) != NULL, error = kThreadError_NoBufs); + SuccessOrExit(error = message->Append(header.GetBytes(), header.GetLength())); + + timestamp.Init(); + *static_cast(×tamp) = mLocal.GetTimestamp(); + SuccessOrExit(error = message->Append(×tamp, sizeof(timestamp))); + SuccessOrExit(error = message->Append(mLocal.GetBytes(), mLocal.GetSize())); + + mMle.GetLeaderAddress(leader); + + memset(&messageInfo, 0, sizeof(messageInfo)); + memcpy(&messageInfo.mPeerAddr, &leader, sizeof(messageInfo.mPeerAddr)); + messageInfo.mPeerPort = kCoapUdpPort; + SuccessOrExit(error = mSocket.SendTo(*message, messageInfo)); + + otLogInfoMeshCoP("sent dataset to leader\n"); + +exit: + + if (error != kThreadError_None && message != NULL) + { + Message::Free(*message); + } + + return error; +} + +void DatasetManager::HandleUdpReceive(void *aContext, otMessage aMessage, const otMessageInfo *aMessageInfo) +{ + DatasetManager *obj = static_cast(aContext); + obj->HandleUdpReceive(*static_cast(aMessage), *static_cast(aMessageInfo)); +} + +void DatasetManager::HandleUdpReceive(Message &aMessage, const Ip6::MessageInfo &aMessageInfo) +{ + Coap::Header header; + (void)aMessageInfo; + + SuccessOrExit(header.FromMessage(aMessage)); + VerifyOrExit(header.GetType() == Coap::Header::kTypeAcknowledgment && + header.GetCode() == Coap::Header::kCodeChanged && + header.GetMessageId() == mCoapMessageId && + header.GetTokenLength() == sizeof(mCoapToken) && + memcmp(mCoapToken, header.GetToken(), sizeof(mCoapToken)) == 0, ;); + + otLogInfoMeshCoP("received response from leader\n"); + +exit: + return; +} + +void DatasetManager::HandleSet(void *aContext, Coap::Header &aHeader, Message &aMessage, + const Ip6::MessageInfo &aMessageInfo) +{ + DatasetManager *obj = static_cast(aContext); + obj->HandleSet(aHeader, aMessage, aMessageInfo); +} + +void DatasetManager::HandleSet(Coap::Header &aHeader, Message &aMessage, const Ip6::MessageInfo &aMessageInfo) +{ + Tlv tlv; + Timestamp timestamp; + uint16_t offset = aMessage.GetOffset(); + uint8_t type; + + VerifyOrExit(mMle.GetDeviceState() == Mle::kDeviceStateLeader, ;); + + type = strcmp(mUri, OPENTHREAD_URI_ACTIVE_SET) == 0 ? Tlv::kActiveTimestamp : Tlv::kPendingTimestamp; + + while (offset < aMessage.GetLength()) + { + aMessage.Read(offset, sizeof(tlv), &tlv); + + if (tlv.GetType() == type) + { + aMessage.Read(offset + sizeof(Tlv), sizeof(timestamp), ×tamp); + break; + } + + offset += sizeof(tlv) + tlv.GetLength(); + } + + // verify the request includes a timestamp that is ahead of the locally stored value + VerifyOrExit(offset < aMessage.GetLength() && mLocal.GetTimestamp().Compare(timestamp) > 0, ;); + + mLocal.Set(aMessage, aMessage.GetOffset(), aMessage.GetLength() - aMessage.GetOffset()); + mNetwork = mLocal; + mNetworkDataLeader.IncrementVersion(); + mNetworkDataLeader.IncrementStableVersion(); + + SendSetResponse(aHeader, aMessageInfo); + +exit: + return; +} + +void DatasetManager::SendSetResponse(const Coap::Header &aRequestHeader, const Ip6::MessageInfo &aMessageInfo) +{ + ThreadError error = kThreadError_None; + Coap::Header responseHeader; + Message *message; + + VerifyOrExit((message = Ip6::Udp::NewMessage(0)) != NULL, error = kThreadError_NoBufs); + responseHeader.Init(); + responseHeader.SetVersion(1); + responseHeader.SetType(Coap::Header::kTypeAcknowledgment); + responseHeader.SetCode(Coap::Header::kCodeChanged); + responseHeader.SetMessageId(aRequestHeader.GetMessageId()); + responseHeader.SetToken(aRequestHeader.GetToken(), aRequestHeader.GetTokenLength()); + responseHeader.AppendContentFormatOption(Coap::Header::kApplicationOctetStream); + responseHeader.Finalize(); + SuccessOrExit(error = message->Append(responseHeader.GetBytes(), responseHeader.GetLength())); + + SuccessOrExit(error = mCoapServer.SendMessage(*message, aMessageInfo)); + + otLogInfoMeshCoP("sent dataset response\n"); + +exit: + + if (error != kThreadError_None && message != NULL) + { + Message::Free(*message); + } +} + +ActiveDataset::ActiveDataset(ThreadNetif &aThreadNetif): + DatasetManager(aThreadNetif, OPENTHREAD_URI_ACTIVE_SET) +{ +} + +ThreadError ActiveDataset::Set(const Dataset &aDataset) +{ + ThreadError error = kThreadError_None; + uint8_t flags; + + SuccessOrExit(error = DatasetManager::Set(aDataset, flags)); + ApplyConfiguration(); + +exit: + return error; +} + +ThreadError ActiveDataset::Set(const Timestamp &aTimestamp, const Message &aMessage, + uint16_t aOffset, uint16_t aLength) +{ + ThreadError error = kThreadError_None; + uint8_t flags; + + SuccessOrExit(error = DatasetManager::Set(aTimestamp, aMessage, aOffset, aLength, flags)); + ApplyConfiguration(); + +exit: + return error; +} + +ThreadError ActiveDataset::ApplyConfiguration(void) +{ + ThreadError error = kThreadError_None; + Dataset *dataset; + const Tlv *cur; + const Tlv *end; + + dataset = mMle.IsAttached() ? &mNetwork : &mLocal; + + cur = reinterpret_cast(dataset->GetBytes()); + end = reinterpret_cast(dataset->GetBytes() + dataset->GetSize()); + + while (cur < end) + { + switch (cur->GetType()) + { + case Tlv::kChannel: + { + const ChannelTlv *channel = static_cast(cur); + mNetif.GetMac().SetChannel(channel->GetChannel()); + break; + } + + case Tlv::kPanId: + { + const PanIdTlv *panid = static_cast(cur); + mNetif.GetMac().SetPanId(panid->GetPanId()); + break; + } + + case Tlv::kExtendedPanId: + { + const ExtendedPanIdTlv *extpanid = static_cast(cur); + mNetif.GetMac().SetExtendedPanId(extpanid->GetExtendedPanId()); + break; + } + + case Tlv::kNetworkName: + { + const NetworkNameTlv *extpanid = static_cast(cur); + mNetif.GetMac().SetNetworkName(extpanid->GetNetworkName()); + break; + } + + case Tlv::kNetworkMasterKey: + { + const NetworkMasterKeyTlv *key = static_cast(cur); + mNetif.GetKeyManager().SetMasterKey(key->GetNetworkMasterKey(), key->GetLength()); + break; + } + + case Tlv::kMeshLocalPrefix: + { + const MeshLocalPrefixTlv *prefix = static_cast(cur); + mMle.SetMeshLocalPrefix(prefix->GetMeshLocalPrefix()); + break; + } + + default: + { + break; + } + } + + cur = cur->GetNext(); + } + + return error; +} + +PendingDataset::PendingDataset(ThreadNetif &aThreadNetif): + DatasetManager(aThreadNetif, OPENTHREAD_URI_PENDING_SET), + mTimer(HandleTimer, this) +{ +} + +ThreadError PendingDataset::Set(const Dataset &aDataset) +{ + ThreadError error = kThreadError_None; + uint8_t flags; + + SuccessOrExit(error = DatasetManager::Set(aDataset, flags)); + ResetDelayTimer(flags); + +exit: + return error; +} + +ThreadError PendingDataset::Set(const Timestamp &aTimestamp, const Message &aMessage, + uint16_t aOffset, uint16_t aLength) +{ + ThreadError error = kThreadError_None; + uint8_t flags; + + SuccessOrExit(error = DatasetManager::Set(aTimestamp, aMessage, aOffset, aLength, flags)); + ResetDelayTimer(flags); + +exit: + return error; +} + +void PendingDataset::ApplyLocalToNetwork(void) +{ + DatasetManager::ApplyLocalToNetwork(); + mNetworkTime = mLocalTime; +} + +void PendingDataset::ResetDelayTimer(uint8_t aFlags) +{ + DelayTimerTlv *delayTimer; + + if (aFlags & kFlagLocalUpdated) + { + mLocalTime = Timer::GetNow(); + + mTimer.Stop(); + + if ((delayTimer = static_cast(mLocal.Get(Tlv::kDelayTimer))) != NULL) + { + mTimer.Start(delayTimer->GetDelayTimer()); + otLogInfoMeshCoP("delay timer started\n"); + } + } + + if (aFlags & kFlagNetworkUpdated) + { + mNetworkTime = Timer::GetNow(); + + // if partition is up to date and delay timer already expired + if ((mNetwork.GetTimestamp().Compare(mLocal.GetTimestamp()) == 0) && + (delayTimer = static_cast(mLocal.Get(Tlv::kDelayTimer))) != NULL && + (delayTimer->GetDelayTimer() == 0)) + { + HandleTimer(); + } + } +} + +void PendingDataset::UpdateDelayTimer(void) +{ + UpdateDelayTimer(mLocal, mLocalTime); + UpdateDelayTimer(mNetwork, mNetworkTime); +} + +void PendingDataset::UpdateDelayTimer(Dataset &aDataset, uint32_t &aStartTime) +{ + DelayTimerTlv *delayTimer; + uint32_t now = Timer::GetNow(); + uint32_t elapsed; + uint32_t delay; + + VerifyOrExit((delayTimer = static_cast(aDataset.Get(Tlv::kDelayTimer))) != NULL, ;); + + elapsed = now - aStartTime; + + delay = delayTimer->GetDelayTimer(); + + if (delay > elapsed) + { + delay -= elapsed; + } + else + { + delay = 0; + } + + delayTimer->SetDelayTimer(delay); + + aStartTime = now; + +exit: + return; +} + +void PendingDataset::HandleTimer(void *aContext) +{ + PendingDataset *obj = static_cast(aContext); + obj->HandleTimer(); +} + +void PendingDataset::HandleTimer(void) +{ + ActiveTimestampTlv *activeTimestamp; + + otLogInfoMeshCoP("pending delay timer expired\n"); + + UpdateDelayTimer(); + + // update only if one of the following is true + // 1) not attached + // 2) partition's pending dataset is up to date + VerifyOrExit(!mMle.IsAttached() || mNetwork.GetTimestamp().Compare(mLocal.GetTimestamp()) == 0, ;); + + mLocal.Remove(Tlv::kDelayTimer); + + VerifyOrExit((activeTimestamp = static_cast(mLocal.Get(Tlv::kActiveTimestamp))) != NULL, ;); + + mNetif.GetActiveDataset().GetLocal() = mLocal; + mNetif.GetActiveDataset().GetLocal().SetTimestamp(*activeTimestamp); + mNetif.GetActiveDataset().GetLocal().Remove(Tlv::kActiveTimestamp); + mNetif.GetActiveDataset().GetNetwork() = mNetif.GetActiveDataset().GetLocal(); + mNetif.GetActiveDataset().ApplyConfiguration(); + mNetworkDataLeader.IncrementVersion(); + mNetworkDataLeader.IncrementStableVersion(); + +exit: + return; +} + +} // namespace MeshCoP +} // namespace Thread diff --git a/src/core/thread/meshcop_dataset_manager.hpp b/src/core/thread/meshcop_dataset_manager.hpp new file mode 100644 index 000000000..ff05ffd1f --- /dev/null +++ b/src/core/thread/meshcop_dataset_manager.hpp @@ -0,0 +1,147 @@ +/* + * Copyright (c) 2016, Nest Labs, Inc. + * 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. + */ + +/** + * @file + * This file includes definitions for managing MeshCoP Datasets. + * + */ + +#ifndef MESHCOP_DATASET_MANAGER_HPP_ +#define MESHCOP_DATASET_MANAGER_HPP_ + +#include +#include +#include +#include +#include +#include + +namespace Thread { + +class ThreadNetif; + +namespace MeshCoP { + +class DatasetManager +{ +public: + ThreadError ApplyLocalToNetwork(void); + + Dataset &GetLocal(void) { return mLocal; } + + Dataset &GetNetwork(void) { return mNetwork; } + +protected: + enum + { + kFlagLocalUpdated = 1 << 0, + kFlagNetworkUpdated = 1 << 1, + }; + + DatasetManager(ThreadNetif &aThreadNetif, const char *aUri); + + ThreadError Set(const Dataset &aDataset, uint8_t &aFlags); + + ThreadError Set(const Timestamp &aTimestamp, const Message &aMessage, uint16_t aOffset, uint16_t aLength, + uint8_t &aFlags); + + Dataset mLocal; + Dataset mNetwork; + + Mle::Mle &mMle; + ThreadNetif &mNetif; + NetworkData::Leader &mNetworkDataLeader; + +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 HandleTimer(void *aContext); + void HandleTimer(void); + + ThreadError Register(void); + void SendSetResponse(const Coap::Header &aRequestHeader, const Ip6::MessageInfo &aMessageInfo); + + Coap::Resource mResource; + Timer mTimer; + + Ip6::UdpSocket mSocket; + uint8_t mCoapToken[2]; + uint16_t mCoapMessageId; + + const char *mUri; + + Coap::Server &mCoapServer; +}; + +class ActiveDataset: public DatasetManager +{ +public: + ActiveDataset(ThreadNetif &aThreadNetif); + + ThreadError Set(const Dataset &aDataset); + + ThreadError Set(const Timestamp &aTimestamp, const Message &aMessage, uint16_t aOffset, uint16_t aLength); + + ThreadError ApplyConfiguration(void); +}; + +class PendingDataset: public DatasetManager +{ +public: + PendingDataset(ThreadNetif &aThreadNetif); + + ThreadError Set(const Dataset &aDataset); + + ThreadError Set(const Timestamp &aTimestamp, const Message &aMessage, uint16_t aOffset, uint16_t aLength); + + void ApplyLocalToNetwork(void); + + void UpdateDelayTimer(void); + +private: + static void HandleTimer(void *aContext); + void HandleTimer(void); + + void ResetDelayTimer(uint8_t aFlags); + void UpdateDelayTimer(Dataset &aDataset, uint32_t &aStartTime); + + Timer mTimer; + uint32_t mLocalTime; + uint32_t mNetworkTime; +}; + +} // namespace MeshCoP +} // namespace Thread + +#endif // MESHCOP_DATASET_MANAGER_HPP_ diff --git a/src/core/thread/meshcop_tlvs.cpp b/src/core/thread/meshcop_tlvs.cpp new file mode 100644 index 000000000..2b4ab8370 --- /dev/null +++ b/src/core/thread/meshcop_tlvs.cpp @@ -0,0 +1,73 @@ +/* + * Copyright (c) 2016, Nest Labs, Inc. + * 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. + */ + +/** + * @file + * This file implements common MeshCoP TLV processing. + */ + +#include +#include + +namespace Thread { +namespace MeshCoP { + +int Timestamp::Compare(const Timestamp &aCompare) const +{ + uint64_t thisSeconds = GetSeconds(); + uint64_t compareSeconds = aCompare.GetSeconds(); + uint16_t thisTicks = GetTicks(); + uint16_t compareTicks = aCompare.GetTicks(); + int rval; + + if (compareSeconds > thisSeconds) + { + rval = 1; + } + else if (compareSeconds < thisSeconds) + { + rval = -1; + } + else if (compareTicks > thisTicks) + { + rval = 1; + } + else if (compareTicks < thisTicks) + { + rval = -1; + } + else + { + rval = 0; + } + + return rval; +} + +} // namespace MeshCoP +} // namespace Thread diff --git a/src/core/thread/meshcop_tlvs.hpp b/src/core/thread/meshcop_tlvs.hpp index c77852f5e..9c0c38147 100644 --- a/src/core/thread/meshcop_tlvs.hpp +++ b/src/core/thread/meshcop_tlvs.hpp @@ -41,6 +41,7 @@ #include using Thread::Encoding::BigEndian::HostSwap16; +using Thread::Encoding::BigEndian::HostSwap32; namespace Thread { namespace MeshCoP { @@ -59,8 +60,18 @@ public: */ enum Type { - kExtendedPanId = 2, ///< Extended PAN ID TLV - kNetworkName = 3, ///< Newtork Name TLV + kChannel = 0, ///< Channel TLV + kPanId = 1, ///< PAN ID TLV + kExtendedPanId = 2, ///< Extended PAN ID TLV + kNetworkName = 3, ///< Newtork Name TLV + kPSKc = 4, ///< PSKc TLV + kNetworkMasterKey = 5, ///< Network Master Key TLV + kMeshLocalPrefix = 7, ///< Mesh Local Prefix TLV + kSecurityPolicy = 12, ///< Security Policy TLV + kActiveTimestamp = 14, ///< Active Timestamp TLV + kPendingTimestamp = 51, ///< Pending Timestamp TLV + kDelayTimer = 52, ///< Delay Timer TLV + kChannelMask = 53, ///< Channel Mask TLV kDiscoveryRequest = 128, ///< Discovery Request TLV kDiscoveryResponse = 129, ///< Discovery Response TLV }; @@ -113,11 +124,118 @@ public: return reinterpret_cast(reinterpret_cast(this) + sizeof(*this) + mLength); } + const Tlv *GetNext() const { + return reinterpret_cast(reinterpret_cast(this) + sizeof(*this) + mLength); + } + private: uint8_t mType; uint8_t mLength; } OT_TOOL_PACKED_END; +/** + * This class implements Channel TLV generation and parsing. + * + */ +OT_TOOL_PACKED_BEGIN +class ChannelTlv: public Tlv +{ +public: + /** + * This method initializes the TLV. + * + */ + void Init(void) { SetType(kChannel); SetLength(sizeof(*this) - sizeof(Tlv)); } + + /** + * This method indicates whether or not the TLV appears to be well-formed. + * + * @retval TRUE If the TLV appears to be well-formed. + * @retval FALSE If the TLV does not appear to be well-formed. + * + */ + bool IsValid(void) const { return GetLength() == sizeof(*this) - sizeof(Tlv); } + + /** + * This method returns the ChannelPage value. + * + * @returns The ChannelPage value. + * + */ + uint8_t GetChannelPage(void) const { return mChannelPage; } + + /** + * This method sets the ChannelPage value. + * + * @param[in] aChannelPage The ChannelPage value. + * + */ + void SetChannelPage(uint8_t aChannelPage) { mChannelPage = aChannelPage; } + + /** + * This method returns the Channel value. + * + * @returns The Channel value. + * + */ + uint16_t GetChannel(void) const { return HostSwap16(mChannel); } + + /** + * This method sets the Channel value. + * + * @param[in] aChannel The Channel value. + * + */ + void SetChannel(uint16_t aChannel) { mChannel = HostSwap16(aChannel); } + +private: + uint8_t mChannelPage; + uint16_t mChannel; +} OT_TOOL_PACKED_END; + +/** + * This class implements PAN ID TLV generation and parsing. + * + */ +OT_TOOL_PACKED_BEGIN +class PanIdTlv: public Tlv +{ +public: + /** + * This method initializes the TLV. + * + */ + void Init(void) { SetType(kPanId); SetLength(sizeof(*this) - sizeof(Tlv)); } + + /** + * This method indicates whether or not the TLV appears to be well-formed. + * + * @retval TRUE If the TLV appears to be well-formed. + * @retval FALSE If the TLV does not appear to be well-formed. + * + */ + bool IsValid(void) const { return GetLength() == sizeof(*this) - sizeof(Tlv); } + + /** + * This method returns the PAN ID value. + * + * @returns The PAN ID value. + * + */ + uint16_t GetPanId(void) const { return HostSwap16(mPanId); } + + /** + * This method sets the PAN ID value. + * + * @param[in] aPanId The PAN ID value. + * + */ + void SetPanId(uint16_t aPanId) { mPanId = HostSwap16(aPanId); } + +private: + uint16_t mPanId; +} OT_TOOL_PACKED_END; + /** * This class implements Extended PAN ID TLV generation and parsing. * @@ -203,13 +321,510 @@ public: void SetNetworkName(const char *aNetworkName) { int length = strnlen(aNetworkName, sizeof(mNetworkName)); memcpy(mNetworkName, aNetworkName, length); - SetLength(length); } private: char mNetworkName[OT_NETWORK_NAME_SIZE]; } OT_TOOL_PACKED_END; +/** + * This class implements PSKc TLV generation and parsing. + * + */ +OT_TOOL_PACKED_BEGIN +class PSKcTlv: public Tlv +{ +public: + /** + * This method initializes the TLV. + * + */ + void Init(void) { SetType(kPSKc); SetLength(sizeof(*this) - sizeof(Tlv)); } + + /** + * This method indicates whether or not the TLV appears to be well-formed. + * + * @retval TRUE If the TLV appears to be well-formed. + * @retval FALSE If the TLV does not appear to be well-formed. + * + */ + bool IsValid(void) const { return GetLength() == sizeof(*this) - sizeof(Tlv); } + + /** + * This method returns the PSKc value. + * + * @returns The PSKc value. + * + */ + const uint8_t *GetPSKc(void) const { return mPSKc; } + + /** + * This method sets the PSKc value. + * + * @param[in] aPSKc A pointer to the PSKc value. + * + */ + void SetPSKc(const uint8_t *aPSKc) { + memcpy(mPSKc, aPSKc, sizeof(mPSKc)); + } + +private: + uint8_t mPSKc[16]; +} OT_TOOL_PACKED_END; + +/** + * This class implements Network Master Key TLV generation and parsing. + * + */ +OT_TOOL_PACKED_BEGIN +class NetworkMasterKeyTlv: public Tlv +{ +public: + /** + * This method initializes the TLV. + * + */ + void Init(void) { SetType(kNetworkMasterKey); SetLength(sizeof(*this) - sizeof(Tlv)); } + + /** + * This method indicates whether or not the TLV appears to be well-formed. + * + * @retval TRUE If the TLV appears to be well-formed. + * @retval FALSE If the TLV does not appear to be well-formed. + * + */ + bool IsValid(void) const { return GetLength() == sizeof(*this) - sizeof(Tlv); } + + /** + * This method returns the Network Master Key value. + * + * @returns The Network Master Key value. + * + */ + const uint8_t *GetNetworkMasterKey(void) const { return mNetworkMasterKey; } + + /** + * This method sets the Network Master Key value. + * + * @param[in] aNetworkMasterKey A pointer to the Network Master Key value. + * + */ + void SetNetworkMasterKey(const uint8_t *aNetworkMasterKey) { + memcpy(mNetworkMasterKey, aNetworkMasterKey, sizeof(mNetworkMasterKey)); + } + +private: + uint8_t mNetworkMasterKey[16]; +} OT_TOOL_PACKED_END; + +/** + * This class implements Mesh Local Prefix TLV generation and parsing. + * + */ +OT_TOOL_PACKED_BEGIN +class MeshLocalPrefixTlv: public Tlv +{ +public: + /** + * This method initializes the TLV. + * + */ + void Init(void) { SetType(kMeshLocalPrefix); SetLength(sizeof(*this) - sizeof(Tlv)); } + + /** + * This method indicates whether or not the TLV appears to be well-formed. + * + * @retval TRUE If the TLV appears to be well-formed. + * @retval FALSE If the TLV does not appear to be well-formed. + * + */ + bool IsValid(void) const { return GetLength() == sizeof(*this) - sizeof(Tlv); } + + /** + * This method returns the Mesh Local Prefix value. + * + * @returns The Mesh Local Prefix value. + * + */ + const uint8_t *GetMeshLocalPrefix(void) const { return mMeshLocalPrefix; } + + /** + * This method sets the Mesh Local Prefix value. + * + * @param[in] aMeshLocalPrefix A pointer to the Mesh Local Prefix value. + * + */ + void SetMeshLocalPrefix(const uint8_t *aMeshLocalPrefix) { + memcpy(mMeshLocalPrefix, aMeshLocalPrefix, sizeof(mMeshLocalPrefix)); + } + +private: + uint8_t mMeshLocalPrefix[8]; +} OT_TOOL_PACKED_END; + +/** + * This class implements Security Policy TLV generation and parsing. + * + */ +OT_TOOL_PACKED_BEGIN +class SecurityPolicyTlv: public Tlv +{ +public: + /** + * This method initializes the TLV. + * + */ + void Init(void) { SetType(kSecurityPolicy); SetLength(sizeof(*this) - sizeof(Tlv)); } + + /** + * This method indicates whether or not the TLV appears to be well-formed. + * + * @retval TRUE If the TLV appears to be well-formed. + * @retval FALSE If the TLV does not appear to be well-formed. + * + */ + bool IsValid(void) const { return GetLength() == sizeof(*this) - sizeof(Tlv); } + + /** + * This method returns the Rotation Time value. + * + * @returns The Rotation Time value. + * + */ + uint16_t GetRotationTime(void) const { return HostSwap16(mRotationTime); } + + /** + * This method sets the Rotation Time value. + * + * @param[in] aRotationTime The Rotation Time value. + * + */ + void SetRotationTime(uint16_t aRotationTime) { mRotationTime = HostSwap16(aRotationTime); } + + enum + { + kObtainMasterKeyFlag = 1 << 7, ///< Obtaining the Master Key + kNativeCommissioningFlag = 1 << 6, ///< Native Commissioning + kRoutersFlag = 1 << 5, ///< Routers enabled + kExternalCommissionerFlag = 1 << 4, ///< External Commissioner allowed + kBeaconsFlag = 1 << 3, ///< Beacons enabled + }; + + /** + * This method returns the Flags value. + * + * @returns The Flags value. + * + */ + uint8_t GetFlags(void) const { return mFlags; } + + /** + * This method sets the Flags value. + * + * @param[in] aFlags The Flags value. + * + */ + void SetFlags(uint8_t aFlags) { mFlags = aFlags; } + +private: + uint16_t mRotationTime; + uint8_t mFlags; +} OT_TOOL_PACKED_END; + +/** + * This class implements Timestamp generation and parsing. + * + */ +OT_TOOL_PACKED_BEGIN +class Timestamp +{ +public: + /** + * This method initializes the Timestamp + * + */ + void Init(void) { memset(mSeconds, 0, sizeof(mSeconds)); mTicks = 0; } + + /** + * This method compares this timestamp to another. + * + * @param[in] aCompare A reference to the timestamp to compare. + * + * @retval -1 if @p aCompare is less than this timestamp. + * @retval 0 if @p aCompare is equal to this timestamp. + * @retval 1 if @p aCompare is greater than this timestamp. + * + */ + int Compare(const Timestamp &aCompare) const; + + /** + * This method returns the Seconds value. + * + * @returns The Seconds value. + * + */ + uint64_t GetSeconds(void) const { + uint64_t seconds = 0; + + for (size_t i = 0; i < sizeof(mSeconds); i++) { + seconds = (seconds << 8) | mSeconds[i]; + } + + return seconds; + } + + /** + * This method sets the Seconds value. + * + * @param[in] aSeconds The Seconds value. + * + */ + void SetSeconds(uint64_t aSeconds) { + for (size_t i = 0; i < sizeof(mSeconds); i++, aSeconds >>= 8) { + mSeconds[sizeof(mSeconds) - 1 - i] = aSeconds; + } + } + + /** + * This method returns the Ticks value. + * + * @returns The Ticks value. + * + */ + uint16_t GetTicks(void) const { return mTicks >> kTicksOffset; } + + /** + * This method sets the Ticks value. + * + * @param[in] aTicks The Ticks value. + * + */ + void SetTicks(uint16_t aTicks) { mTicks = (mTicks & ~kTicksMask) | (aTicks << kTicksOffset); } + + /** + * This method returns the Authoritative value. + * + * @returns The Authoritative value. + * + */ + bool GetAuthoritative(void) const { return (mTicks & kAuthoritativeMask) != 0; } + + /** + * This method sets the Authoritative value. + * + * @param[in] aAuthoritative The Authoritative value. + * + */ + void SetAuthoritative(bool aAuthoritative) { + mTicks = (mTicks & kTicksMask) | (aAuthoritative << kAuthoritativeOffset); + } + +private: + uint8_t mSeconds[6]; + + enum + { + kTicksOffset = 1, + kTicksMask = 0x7fff << kTicksOffset, + kAuthoritativeOffset = 0, + kAuthoritativeMask = 1 << kAuthoritativeOffset, + }; + uint16_t mTicks; +} OT_TOOL_PACKED_END; + +/** + * This class implements Active Timestamp TLV generation and parsing. + * + */ +OT_TOOL_PACKED_BEGIN +class ActiveTimestampTlv : public Tlv, public Timestamp +{ +public: + /** + * This method initializes the TLV. + * + */ + void Init(void) { SetType(kActiveTimestamp); SetLength(sizeof(*this) - sizeof(Tlv)); } + + /** + * This method indicates whether or not the TLV appears to be well-formed. + * + * @retval TRUE If the TLV appears to be well-formed. + * @retval FALSE If the TLV does not appear to be well-formed. + * + */ + bool IsValid(void) const { return GetLength() == sizeof(*this) - sizeof(Tlv); } +} OT_TOOL_PACKED_END; + +/** + * This class implements Pending Timestamp TLV generation and parsing. + * + */ +OT_TOOL_PACKED_BEGIN +class PendingTimestampTlv : public Tlv, public Timestamp +{ +public: + /** + * This method initializes the TLV. + * + */ + void Init(void) { SetType(kPendingTimestamp); SetLength(sizeof(*this) - sizeof(Tlv)); } + + /** + * This method indicates whether or not the TLV appears to be well-formed. + * + * @retval TRUE If the TLV appears to be well-formed. + * @retval FALSE If the TLV does not appear to be well-formed. + * + */ + bool IsValid(void) const { return GetLength() == sizeof(*this) - sizeof(Tlv); } +} OT_TOOL_PACKED_END; + +/** + * This class implements Delay Timer TLV generation and parsing. + * + */ +OT_TOOL_PACKED_BEGIN +class DelayTimerTlv: public Tlv +{ +public: + /** + * This method initializes the TLV. + * + */ + void Init(void) { SetType(kDelayTimer); SetLength(sizeof(*this) - sizeof(Tlv)); } + + /** + * This method indicates whether or not the TLV appears to be well-formed. + * + * @retval TRUE If the TLV appears to be well-formed. + * @retval FALSE If the TLV does not appear to be well-formed. + * + */ + bool IsValid(void) const { return GetLength() == sizeof(*this) - sizeof(Tlv); } + + /** + * This method returns the Delay Timer value. + * + * @returns The Delay Timer value. + * + */ + uint16_t GetDelayTimer(void) const { return HostSwap32(mDelayTimer); } + + /** + * This method sets the Delay Timer value. + * + * @param[in] aDelayTimer The Delay Timer value. + * + */ + void SetDelayTimer(uint32_t aDelayTimer) { mDelayTimer = HostSwap32(aDelayTimer); } + +private: + uint32_t mDelayTimer; +} OT_TOOL_PACKED_END; + +/** + * This class implements Channel Mask Entry generation and parsing. + * + */ +OT_TOOL_PACKED_BEGIN +class ChannelMaskEntry +{ +public: + /** + * This method gets the ChannelPage value. + * + * @returns The ChannelPage value. + * + */ + uint8_t GetChannelPage(void) const { return mChannelPage; } + + /** + * This method sets the ChannelPage value. + * + * @param[in] aChannelPage The ChannelPage value. + * + */ + void SetChannelPage(uint8_t aChannelPage) { mChannelPage = aChannelPage; } + + /** + * This method gets the MaskLength value. + * + * @returns The MaskLength value. + * + */ + uint8_t GetMaskLength(void) const { return mMaskLength; } + + /** + * This method sets the MaskLength value. + * + * @param[in] aMaskLength The MaskLength value. + * + */ + void SetMaskLength(uint8_t aMaskLength) { mMaskLength = aMaskLength; } + + /** + * This method clears the bit corresponding to @p aChannel in ChannelMask. + * + * @param[in] aChannel The channel in ChannelMask to clear. + * + */ + void ClearChannel(uint8_t aChannel) { + uint8_t *mask = reinterpret_cast(this) + sizeof(*this); + mask[aChannel / 8] &= ~(1 << (aChannel % 8)); + } + + /** + * This method sets the bit corresponding to @p aChannel in ChannelMask. + * + * @param[in] aChannel The channel in ChannelMask to set. + * + */ + void SetChannel(uint8_t aChannel) { + uint8_t *mask = reinterpret_cast(this) + sizeof(*this); + mask[aChannel / 8] |= 1 << (aChannel % 8); + } + + /** + * This method indicates whether or not the bit corresponding to @p aChannel in ChannelMask is set. + * + * @param[in] aChannel The channel in ChannelMask to get. + * + */ + bool IsChannelSet(uint8_t aChannel) const { + const uint8_t *mask = reinterpret_cast(this) + sizeof(*this); + return (aChannel < (mMaskLength * 8)) ? mask[aChannel / 8] & (1 << (aChannel % 8)) : false; + } + +private: + uint8_t mChannelPage; + uint8_t mMaskLength; +} OT_TOOL_PACKED_END; + +/** + * This class implements Channel Mask TLV generation and parsing. + * + */ +OT_TOOL_PACKED_BEGIN +class ChannelMaskTlv: public Tlv +{ +public: + /** + * This method initializes the TLV. + * + */ + void Init(void) { SetType(kChannelMask); SetLength(sizeof(*this) - sizeof(Tlv)); } + + /** + * This method indicates whether or not the TLV appears to be well-formed. + * + * @retval TRUE If the TLV appears to be well-formed. + * @retval FALSE If the TLV does not appear to be well-formed. + * + */ + bool IsValid(void) const { return true; } +} OT_TOOL_PACKED_END; + /** * This class implements Discovery Request TLV generation and parsing. * diff --git a/src/core/thread/mle.cpp b/src/core/thread/mle.cpp index fc43c0927..95eaebce7 100644 --- a/src/core/thread/mle.cpp +++ b/src/core/thread/mle.cpp @@ -301,6 +301,13 @@ exit: return error; } +bool Mle::IsAttached(void) const +{ + return (mDeviceState == kDeviceStateChild || + mDeviceState == kDeviceStateRouter || + mDeviceState == kDeviceStateLeader); +} + DeviceState Mle::GetDeviceState(void) const { return mDeviceState; @@ -830,6 +837,38 @@ exit: return error; } +ThreadError Mle::AppendActiveTimestamp(Message &aMessage) +{ + ThreadError error; + ActiveTimestampTlv timestampTlv; + const MeshCoP::Timestamp ×tamp(mNetif.GetActiveDataset().GetNetwork().GetTimestamp()); + + VerifyOrExit(timestamp.GetSeconds() != 0, error = kThreadError_None); + + timestampTlv.Init(); + *static_cast(×tampTlv) = timestamp; + error = aMessage.Append(×tampTlv, sizeof(timestampTlv)); + +exit: + return error; +} + +ThreadError Mle::AppendPendingTimestamp(Message &aMessage) +{ + ThreadError error; + PendingTimestampTlv timestampTlv; + const MeshCoP::Timestamp ×tamp(mNetif.GetPendingDataset().GetNetwork().GetTimestamp()); + + VerifyOrExit(timestamp.GetSeconds() != 0, error = kThreadError_None); + + timestampTlv.Init(); + *static_cast(×tampTlv) = timestamp; + error = aMessage.Append(×tampTlv, sizeof(timestampTlv)); + +exit: + return error; +} + void Mle::HandleNetifStateChanged(uint32_t aFlags, void *aContext) { Mle *obj = reinterpret_cast(aContext); @@ -1066,6 +1105,8 @@ ThreadError Mle::SendChildIdRequest(void) } SuccessOrExit(error = AppendTlvRequest(*message, tlvs, sizeof(tlvs))); + SuccessOrExit(error = AppendActiveTimestamp(*message)); + SuccessOrExit(error = AppendPendingTimestamp(*message)); memset(&destination, 0, sizeof(destination)); destination.mFields.m16[0] = HostSwap16(0xfe80); @@ -1098,6 +1139,8 @@ ThreadError Mle::SendDataRequest(const Ip6::Address &aDestination, const uint8_t message->SetLinkSecurityEnabled(false); SuccessOrExit(error = AppendHeader(*message, Header::kCommandDataRequest)); SuccessOrExit(error = AppendTlvRequest(*message, aTlvs, aTlvsLength)); + SuccessOrExit(error = AppendActiveTimestamp(*message)); + SuccessOrExit(error = AppendPendingTimestamp(*message)); SuccessOrExit(error = SendMessage(*message, aDestination)); @@ -1113,46 +1156,6 @@ exit: return error; } -ThreadError Mle::SendDataResponse(const Ip6::Address &aDestination, const uint8_t *aTlvs, uint8_t aTlvsLength) -{ - ThreadError error = kThreadError_None; - Message *message; - Neighbor *neighbor; - bool stableOnly; - - VerifyOrExit((message = Ip6::Udp::NewMessage(0)) != NULL, ;); - message->SetLinkSecurityEnabled(false); - SuccessOrExit(error = AppendHeader(*message, Header::kCommandDataResponse)); - SuccessOrExit(error = AppendSourceAddress(*message)); - SuccessOrExit(error = AppendLeaderData(*message)); - - neighbor = mMleRouter.GetNeighbor(aDestination); - - for (int i = 0; i < aTlvsLength; i++) - { - switch (aTlvs[i]) - { - case Tlv::kNetworkData: - stableOnly = neighbor != NULL ? (neighbor->mMode & ModeTlv::kModeFullNetworkData) == 0 : false; - SuccessOrExit(error = AppendNetworkData(*message, stableOnly)); - break; - } - } - - SuccessOrExit(error = SendMessage(*message, aDestination)); - - otLogInfoMle("Sent Data Response\n"); - -exit: - - if (error != kThreadError_None && message != NULL) - { - Message::Free(*message); - } - - return error; -} - ThreadError Mle::SendChildUpdateRequest(void) { ThreadError error = kThreadError_None; @@ -1486,7 +1489,7 @@ void Mle::HandleUdpReceive(Message &aMessage, const Ip6::MessageInfo &aMessageIn break; case Header::kCommandDataRequest: - HandleDataRequest(aMessage, aMessageInfo); + mMleRouter.HandleDataRequest(aMessage, aMessageInfo); break; case Header::kCommandDataResponse: @@ -1599,33 +1602,20 @@ exit: return error; } -ThreadError Mle::HandleDataRequest(const Message &aMessage, const Ip6::MessageInfo &aMessageInfo) -{ - ThreadError error = kThreadError_None; - TlvRequestTlv tlvRequest; - - otLogInfoMle("Received Data Request\n"); - - // TLV Request - SuccessOrExit(error = Tlv::GetTlv(aMessage, Tlv::kTlvRequest, sizeof(tlvRequest), tlvRequest)); - VerifyOrExit(tlvRequest.IsValid(), error = kThreadError_Parse); - - SendDataResponse(aMessageInfo.GetPeerAddr(), tlvRequest.GetTlvs(), tlvRequest.GetLength()); - -exit: - return error; -} - ThreadError Mle::HandleDataResponse(const Message &aMessage, const Ip6::MessageInfo &aMessageInfo) { ThreadError error = kThreadError_None; LeaderDataTlv leaderData; NetworkDataTlv networkData; + ActiveTimestampTlv activeTimestamp; + PendingTimestampTlv pendingTimestamp; + Tlv tlv; + uint16_t offset; int8_t diff; otLogInfoMle("Received Data Response\n"); - SuccessOrExit(error = Tlv::GetTlv(aMessage, Tlv::kNetworkData, sizeof(networkData), networkData)); + // Leader Data SuccessOrExit(error = Tlv::GetTlv(aMessage, Tlv::kLeaderData, sizeof(leaderData), leaderData)); VerifyOrExit(leaderData.IsValid(), error = kThreadError_Parse); @@ -1651,6 +1641,37 @@ ThreadError Mle::HandleDataResponse(const Message &aMessage, const Ip6::MessageI VerifyOrExit(diff > 0, ;); } + // Network Data + SuccessOrExit(error = Tlv::GetTlv(aMessage, Tlv::kNetworkData, sizeof(networkData), networkData)); + VerifyOrExit(networkData.IsValid(), error = kThreadError_Parse); + + // Active Timestamp + if (Tlv::GetTlv(aMessage, Tlv::kActiveTimestamp, sizeof(activeTimestamp), activeTimestamp) == kThreadError_None) + { + VerifyOrExit(activeTimestamp.IsValid(), error = kThreadError_Parse); + + // Active Dataset + if (Tlv::GetOffset(aMessage, Tlv::kActiveDataset, offset) == kThreadError_None) + { + aMessage.Read(offset, sizeof(tlv), &tlv); + mNetif.GetActiveDataset().Set(activeTimestamp, aMessage, offset + sizeof(tlv), tlv.GetLength()); + } + } + + // Pending Timestamp + if (Tlv::GetTlv(aMessage, Tlv::kPendingTimestamp, sizeof(pendingTimestamp), pendingTimestamp) == kThreadError_None) + { + VerifyOrExit(pendingTimestamp.IsValid(), error = kThreadError_Parse); + + // Pending Dataset + if (Tlv::GetOffset(aMessage, Tlv::kPendingDataset, offset) == kThreadError_None) + { + aMessage.Read(offset, sizeof(tlv), &tlv); + mNetif.GetPendingDataset().Set(activeTimestamp, aMessage, offset + sizeof(tlv), tlv.GetLength()); + } + } + + // Network Data mNetworkData.SetNetworkData(leaderData.GetDataVersion(), leaderData.GetStableDataVersion(), (mDeviceMode & ModeTlv::kModeFullNetworkData) == 0, networkData.GetNetworkData(), networkData.GetLength()); @@ -1833,6 +1854,10 @@ ThreadError Mle::HandleChildIdResponse(const Message &aMessage, const Ip6::Messa Address16Tlv shortAddress; NetworkDataTlv networkData; RouteTlv route; + ActiveTimestampTlv activeTimestamp; + PendingTimestampTlv pendingTimestamp; + Tlv tlv; + uint16_t offset; uint8_t numRouters = 0; otLogInfoMle("Received Child ID Response\n"); @@ -1854,6 +1879,32 @@ ThreadError Mle::HandleChildIdResponse(const Message &aMessage, const Ip6::Messa // Network Data SuccessOrExit(error = Tlv::GetTlv(aMessage, Tlv::kNetworkData, sizeof(networkData), networkData)); + // Active Timestamp + if (Tlv::GetTlv(aMessage, Tlv::kActiveTimestamp, sizeof(activeTimestamp), activeTimestamp) == kThreadError_None) + { + VerifyOrExit(activeTimestamp.IsValid(), error = kThreadError_Parse); + + // Active Dataset + if (Tlv::GetOffset(aMessage, Tlv::kActiveDataset, offset) == kThreadError_None) + { + aMessage.Read(offset, sizeof(tlv), &tlv); + mNetif.GetActiveDataset().Set(activeTimestamp, aMessage, offset + sizeof(tlv), tlv.GetLength()); + } + } + + // Pending Timestamp + if (Tlv::GetTlv(aMessage, Tlv::kPendingTimestamp, sizeof(pendingTimestamp), pendingTimestamp) == kThreadError_None) + { + VerifyOrExit(pendingTimestamp.IsValid(), error = kThreadError_Parse); + + // Pending Dataset + if (Tlv::GetOffset(aMessage, Tlv::kPendingDataset, offset) == kThreadError_None) + { + aMessage.Read(offset, sizeof(tlv), &tlv); + mNetif.GetPendingDataset().Set(pendingTimestamp, aMessage, offset + sizeof(tlv), tlv.GetLength()); + } + } + // Parent Attach Success mParentRequestTimer.Stop(); @@ -1876,6 +1927,8 @@ ThreadError Mle::HandleChildIdResponse(const Message &aMessage, const Ip6::Messa (mDeviceMode & ModeTlv::kModeFullNetworkData) == 0, networkData.GetNetworkData(), networkData.GetLength()); + mNetif.GetActiveDataset().ApplyConfiguration(); + // Route if ((Tlv::GetTlv(aMessage, Tlv::kRoute, sizeof(route), route) == kThreadError_None) && (mDeviceMode & ModeTlv::kModeFFD)) diff --git a/src/core/thread/mle.hpp b/src/core/thread/mle.hpp index 49799c99e..fdd338a5c 100644 --- a/src/core/thread/mle.hpp +++ b/src/core/thread/mle.hpp @@ -446,6 +446,15 @@ public: */ ThreadError BecomeChild(otMleAttachFilter aFilter); + /** + * This method indicates whether or not the Thread device is attached to a Thread network. + * + * @retval TRUE Attached to a Thread network. + * @retval FALSE Not attached to a Thread network. + * + */ + bool IsAttached(void) const; + /** * This method returns the current Thread interface state. * @@ -857,6 +866,28 @@ protected: */ ThreadError AppendAddressRegistration(Message &aMessage); + /** + * This method appends a Active Timestamp TLV to a message. + * + * @param[in] aMessage A reference to the message. + * + * @retval kThreadError_None Successfully appended the Active Timestamp TLV. + * @retval kThreadError_NoBufs Insufficient buffers available to append the Active Timestamp TLV. + * + */ + ThreadError AppendActiveTimestamp(Message &aMessage); + + /** + * This method appends a Pending Timestamp TLV to a message. + * + * @param[in] aMessage A reference to the message. + * + * @retval kThreadError_None Successfully appended the Pending Timestamp TLV. + * @retval kThreadError_NoBufs Insufficient buffers available to append the Pending Timestamp TLV. + * + */ + ThreadError AppendPendingTimestamp(Message &aMessage); + /** * This method appends a Thread Discovery TLV to a message. * @@ -944,19 +975,6 @@ protected: */ ThreadError SendDataRequest(const Ip6::Address &aDestination, const uint8_t *aTlvs, uint8_t aTlvsLength); - /** - * This method generates an MLE Data Response message. - * - * @param[in] aDestination A reference to the IPv6 address of the destination. - * @param[in] aTlvs A pointer to TLV types that should be included. - * @param[in] aTlvsLength The number of TLV types in @p aTlvs. - * - * @retval kThreadError_None Successfully generated an MLE Data Response message. - * @retval kThreadError_NoBufs Insufficient buffers to generate the MLE Data Response message. - * - */ - ThreadError SendDataResponse(const Ip6::Address &aDestination, const uint8_t *aTlvs, uint8_t aTlvsLength); - /** * This method generates an MLE Child Update Request message. * @@ -1065,7 +1083,6 @@ private: ThreadError HandleAdvertisement(const Message &aMessage, const Ip6::MessageInfo &aMessageInfo); ThreadError HandleChildIdResponse(const Message &aMessage, const Ip6::MessageInfo &aMessageInfo); ThreadError HandleChildUpdateResponse(const Message &aMessage, const Ip6::MessageInfo &aMessageInfo); - ThreadError HandleDataRequest(const Message &aMessage, const Ip6::MessageInfo &aMessageInfo); ThreadError HandleDataResponse(const Message &aMessage, const Ip6::MessageInfo &aMessageInfo); ThreadError HandleParentResponse(const Message &aMessage, const Ip6::MessageInfo &aMessageInfo, uint32_t aKeySequence); diff --git a/src/core/thread/mle_router.cpp b/src/core/thread/mle_router.cpp index 476a8d256..e0a04deab 100644 --- a/src/core/thread/mle_router.cpp +++ b/src/core/thread/mle_router.cpp @@ -359,6 +359,8 @@ ThreadError MleRouter::SetStateLeader(uint16_t aRloc16) mRouters[mRouterId].mLastHeard = Timer::GetNow(); mNetworkData.Start(); + mNetif.GetActiveDataset().ApplyLocalToNetwork(); + mNetif.GetPendingDataset().ApplyLocalToNetwork(); mCoapServer.AddResource(mAddressSolicit); mCoapServer.AddResource(mAddressRelease); @@ -1734,7 +1736,10 @@ ThreadError MleRouter::HandleChildIdRequest(const Message &aMessage, const Ip6:: TimeoutTlv timeout; AddressRegistrationTlv address; TlvRequestTlv tlvRequest; + ActiveTimestampTlv activeTimestamp; + PendingTimestampTlv pendingTimestamp; Child *child; + uint8_t numTlvs; otLogInfoMle("Received Child ID Request\n"); @@ -1783,7 +1788,24 @@ ThreadError MleRouter::HandleChildIdRequest(const Message &aMessage, const Ip6:: // TLV Request SuccessOrExit(error = Tlv::GetTlv(aMessage, Tlv::kTlvRequest, sizeof(tlvRequest), tlvRequest)); - VerifyOrExit(tlvRequest.IsValid(), error = kThreadError_Parse); + VerifyOrExit(tlvRequest.IsValid() && tlvRequest.GetLength() <= sizeof(child->mRequestTlvs), + error = kThreadError_Parse); + + // Active Timestamp + activeTimestamp.SetLength(0); + + if (Tlv::GetTlv(aMessage, Tlv::kActiveTimestamp, sizeof(activeTimestamp), activeTimestamp) == kThreadError_None) + { + VerifyOrExit(activeTimestamp.IsValid(), error = kThreadError_Parse); + } + + // Pending Timestamp + pendingTimestamp.SetLength(0); + + if (Tlv::GetTlv(aMessage, Tlv::kPendingTimestamp, sizeof(pendingTimestamp), pendingTimestamp) == kThreadError_None) + { + VerifyOrExit(pendingTimestamp.IsValid(), error = kThreadError_Parse); + } // Remove from router table for (int i = 0; i < kMaxRouterId; i++) @@ -1816,14 +1838,20 @@ ThreadError MleRouter::HandleChildIdRequest(const Message &aMessage, const Ip6:: UpdateChildAddresses(address, *child); - for (uint8_t i = 0; i < tlvRequest.GetLength(); i++) + memset(child->mRequestTlvs, Tlv::kInvalid, sizeof(child->mRequestTlvs)); + memcpy(child->mRequestTlvs, tlvRequest.GetTlvs(), tlvRequest.GetLength()); + numTlvs = tlvRequest.GetLength(); + + if (activeTimestamp.GetLength() == 0 || + mNetif.GetActiveDataset().GetNetwork().GetTimestamp().Compare(activeTimestamp) != 0) { - child->mRequestTlvs[i] = tlvRequest.GetTlvs()[i]; + child->mRequestTlvs[numTlvs++] = Tlv::kActiveDataset; } - for (uint8_t i = tlvRequest.GetLength(); i < sizeof(child->mRequestTlvs); i++) + if (pendingTimestamp.GetLength() == 0 || + mNetif.GetPendingDataset().GetNetwork().GetTimestamp().Compare(pendingTimestamp) != 0) { - child->mRequestTlvs[i] = Tlv::kInvalid; + child->mRequestTlvs[numTlvs++] = Tlv::kPendingDataset; } switch (GetDeviceState()) @@ -1929,6 +1957,54 @@ exit: return error; } +ThreadError MleRouter::HandleDataRequest(const Message &aMessage, const Ip6::MessageInfo &aMessageInfo) +{ + ThreadError error = kThreadError_None; + TlvRequestTlv tlvRequest; + ActiveTimestampTlv activeTimestamp; + PendingTimestampTlv pendingTimestamp; + uint8_t tlvs[4]; + uint8_t numTlvs; + + otLogInfoMle("Received Data Request\n"); + + // TLV Request + SuccessOrExit(error = Tlv::GetTlv(aMessage, Tlv::kTlvRequest, sizeof(tlvRequest), tlvRequest)); + VerifyOrExit(tlvRequest.IsValid() && tlvRequest.GetLength() <= sizeof(tlvs), error = kThreadError_Parse); + + // Active Timestamp + SuccessOrExit(error = Tlv::GetTlv(aMessage, Tlv::kActiveTimestamp, sizeof(activeTimestamp), activeTimestamp)); + VerifyOrExit(activeTimestamp.IsValid(), error = kThreadError_Parse); + + // Pending Timestamp + pendingTimestamp.SetLength(0); + + if (Tlv::GetTlv(aMessage, Tlv::kPendingTimestamp, sizeof(pendingTimestamp), pendingTimestamp) == kThreadError_None) + { + VerifyOrExit(pendingTimestamp.IsValid(), error = kThreadError_Parse); + } + + memset(tlvs, Tlv::kInvalid, sizeof(tlvs)); + memcpy(tlvs, tlvRequest.GetTlvs(), tlvRequest.GetLength()); + numTlvs = tlvRequest.GetLength(); + + if (mNetif.GetActiveDataset().GetNetwork().GetTimestamp().Compare(activeTimestamp) != 0) + { + tlvs[numTlvs++] = Tlv::kActiveDataset; + } + + if (pendingTimestamp.GetLength() == 0 || + mNetif.GetPendingDataset().GetNetwork().GetTimestamp().Compare(pendingTimestamp) != 0) + { + tlvs[numTlvs++] = Tlv::kPendingDataset; + } + + SendDataResponse(aMessageInfo.GetPeerAddr(), tlvs, numTlvs); + +exit: + return error; +} + ThreadError MleRouter::HandleNetworkDataUpdateRouter(void) { static const uint8_t tlvs[] = {Tlv::kLeaderData, Tlv::kNetworkData}; @@ -1986,6 +2062,14 @@ ThreadError MleRouter::SendChildIdResponse(Child *aChild) case Tlv::kRoute: SuccessOrExit(error = AppendRoute(*message)); break; + + case Tlv::kActiveDataset: + SuccessOrExit(error = AppendActiveDataset(*message)); + break; + + case Tlv::kPendingDataset: + SuccessOrExit(error = AppendPendingDataset(*message)); + break; } } @@ -2073,6 +2157,55 @@ exit: return kThreadError_None; } +ThreadError MleRouter::SendDataResponse(const Ip6::Address &aDestination, const uint8_t *aTlvs, uint8_t aTlvsLength) +{ + ThreadError error = kThreadError_None; + Message *message; + Neighbor *neighbor; + bool stableOnly; + + VerifyOrExit((message = Ip6::Udp::NewMessage(0)) != NULL, ;); + message->SetLinkSecurityEnabled(false); + SuccessOrExit(error = AppendHeader(*message, Header::kCommandDataResponse)); + SuccessOrExit(error = AppendSourceAddress(*message)); + SuccessOrExit(error = AppendLeaderData(*message)); + SuccessOrExit(error = AppendActiveTimestamp(*message)); + SuccessOrExit(error = AppendPendingTimestamp(*message)); + + for (int i = 0; i < aTlvsLength; i++) + { + switch (aTlvs[i]) + { + case Tlv::kNetworkData: + neighbor = mMleRouter.GetNeighbor(aDestination); + stableOnly = neighbor != NULL ? (neighbor->mMode & ModeTlv::kModeFullNetworkData) == 0 : false; + SuccessOrExit(error = AppendNetworkData(*message, stableOnly)); + break; + + case Tlv::kActiveDataset: + SuccessOrExit(error = AppendActiveDataset(*message)); + break; + + case Tlv::kPendingDataset: + SuccessOrExit(error = AppendPendingDataset(*message)); + break; + } + } + + SuccessOrExit(error = SendMessage(*message, aDestination)); + + otLogInfoMle("Sent Data Response\n"); + +exit: + + if (error != kThreadError_None && message != NULL) + { + Message::Free(*message); + } + + return error; +} + Child *MleRouter::GetChild(uint16_t aAddress) { for (int i = 0; i < kMaxChildren; i++) @@ -3177,5 +3310,38 @@ exit: return error; } +ThreadError MleRouter::AppendActiveDataset(Message &aMessage) +{ + ThreadError error = kThreadError_None; + Tlv tlv; + + SuccessOrExit(error = AppendActiveTimestamp(aMessage)); + + tlv.SetType(Tlv::kActiveDataset); + tlv.SetLength(mNetif.GetActiveDataset().GetNetwork().GetSize()); + SuccessOrExit(error = aMessage.Append(&tlv, sizeof(tlv))); + SuccessOrExit(error = aMessage.Append(mNetif.GetActiveDataset().GetNetwork().GetBytes(), tlv.GetLength())); + +exit: + return error; +} + +ThreadError MleRouter::AppendPendingDataset(Message &aMessage) +{ + ThreadError error = kThreadError_None; + Tlv tlv; + + SuccessOrExit(error = AppendPendingTimestamp(aMessage)); + + tlv.SetType(Tlv::kPendingDataset); + tlv.SetLength(mNetif.GetPendingDataset().GetNetwork().GetSize()); + SuccessOrExit(error = aMessage.Append(&tlv, sizeof(tlv))); + mNetif.GetPendingDataset().UpdateDelayTimer(); + SuccessOrExit(error = aMessage.Append(mNetif.GetPendingDataset().GetNetwork().GetBytes(), tlv.GetLength())); + +exit: + return error; +} + } // namespace Mle } // namespace Thread diff --git a/src/core/thread/mle_router.hpp b/src/core/thread/mle_router.hpp index 9f909f87f..ae427b814 100644 --- a/src/core/thread/mle_router.hpp +++ b/src/core/thread/mle_router.hpp @@ -404,6 +404,8 @@ private: ThreadError AppendConnectivity(Message &aMessage); ThreadError AppendChildAddresses(Message &aMessage, Child &aChild); ThreadError AppendRoute(Message &aMessage); + ThreadError AppendActiveDataset(Message &aMessage); + ThreadError AppendPendingDataset(Message &aMessage); uint8_t GetLinkCost(uint8_t aRouterId); void GetChildInfo(Child &aChild, otChildInfo &aChildInfo); ThreadError HandleDetachStart(void); @@ -420,6 +422,7 @@ private: ThreadError HandleChildIdRequest(const Message &aMessage, const Ip6::MessageInfo &aMessageInfo, uint32_t aKeySequence); ThreadError HandleChildUpdateRequest(const Message &aMessage, const Ip6::MessageInfo &aMessageInfo); + ThreadError HandleDataRequest(const Message &aMessage, const Ip6::MessageInfo &aMessageInfo); ThreadError HandleNetworkDataUpdateRouter(void); bool IsSingleton(void); @@ -437,6 +440,8 @@ private: ThreadError SendChildIdResponse(Child *aChild); ThreadError SendChildUpdateResponse(Child *aChild, const Ip6::MessageInfo &aMessageInfo, const uint8_t *aTlvs, uint8_t aTlvsLength, const ChallengeTlv *challenge); + ThreadError SendDataResponse(const Ip6::Address &aDestination, const uint8_t *aTlvs, uint8_t aTlvsLength); + ThreadError SetStateRouter(uint16_t aRloc16); ThreadError SetStateLeader(uint16_t aRloc16); ThreadError UpdateChildAddresses(const AddressRegistrationTlv &aTlv, Child &aChild); diff --git a/src/core/thread/mle_tlvs.cpp b/src/core/thread/mle_tlvs.cpp index 62772b767..851018db3 100644 --- a/src/core/thread/mle_tlvs.cpp +++ b/src/core/thread/mle_tlvs.cpp @@ -39,28 +39,42 @@ namespace Thread { namespace Mle { ThreadError Tlv::GetTlv(const Message &aMessage, Type aType, uint16_t aMaxLength, Tlv &aTlv) +{ + ThreadError error = kThreadError_Parse; + uint16_t offset; + + SuccessOrExit(error = GetOffset(aMessage, aType, offset)); + aMessage.Read(offset, sizeof(Tlv), &aTlv); + + if (aMaxLength > sizeof(aTlv) + aTlv.GetLength()) + { + aMaxLength = sizeof(aTlv) + aTlv.GetLength(); + } + + aMessage.Read(offset, aMaxLength, &aTlv); + +exit: + return error; +} + +ThreadError Tlv::GetOffset(const Message &aMessage, Type aType, uint16_t &aOffset) { ThreadError error = kThreadError_Parse; uint16_t offset = aMessage.GetOffset(); uint16_t end = aMessage.GetLength(); + Tlv tlv; while (offset < end) { - aMessage.Read(offset, sizeof(Tlv), &aTlv); + aMessage.Read(offset, sizeof(Tlv), &tlv); - if (aTlv.GetType() == aType && (offset + sizeof(aTlv) + aTlv.GetLength()) <= end) + if (tlv.GetType() == aType && (offset + sizeof(tlv) + tlv.GetLength()) <= end) { - if (aMaxLength > sizeof(aTlv) + aTlv.GetLength()) - { - aMaxLength = sizeof(aTlv) + aTlv.GetLength(); - } - - aMessage.Read(offset, aMaxLength, &aTlv); - + aOffset = offset; ExitNow(error = kThreadError_None); } - offset += sizeof(aTlv) + aTlv.GetLength(); + offset += sizeof(tlv) + tlv.GetLength(); } exit: diff --git a/src/core/thread/mle_tlvs.hpp b/src/core/thread/mle_tlvs.hpp index 0e5d03889..b1ddd60b3 100644 --- a/src/core/thread/mle_tlvs.hpp +++ b/src/core/thread/mle_tlvs.hpp @@ -40,6 +40,7 @@ #include #include #include +#include #include using Thread::Encoding::BigEndian::HostSwap16; @@ -93,6 +94,10 @@ public: kStatus = 17, ///< Status TLV kVersion = 18, ///< Version TLV kAddressRegistration = 19, ///< Address Registration TLV + kActiveTimestamp = 22, ///< Active Timestamp TLV + kPendingTimestamp = 23, ///< Pending Timestamp TLV + kActiveDataset = 24, ///< Active Operational Dataset TLV + kPendingDataset = 25, ///< Pending Operational Dataset TLV kDiscovery = 26, ///< Thread Discovery TLV kInvalid = 255, }; @@ -141,6 +146,19 @@ public: */ static ThreadError GetTlv(const Message &aMessage, Type aType, uint16_t aMaxLength, Tlv &aTlv); + /** + * This static method obtains the offset of a TLV within @p aMessage. + * + * @param[in] aMessage A reference to the message. + * @param[in] aType The Type value to search for. + * @param[out] aOffset A reference to the offset of the TLV. + * + * @retval kThreadError_None Successfully copied the TLV. + * @retval kThreadError_NotFound Could not find the TLV with Type @p aType. + * + */ + static ThreadError GetOffset(const Message &aMessage, Type aType, uint16_t &aOffset); + private: uint8_t mType; uint8_t mLength; @@ -1429,6 +1447,54 @@ private: AddressRegistrationEntry mAddresses[4]; } OT_TOOL_PACKED_END; +/** + * This class implements Active Timestamp TLV generation and parsing. + * + */ +OT_TOOL_PACKED_BEGIN +class ActiveTimestampTlv : public Tlv, public MeshCoP::Timestamp +{ +public: + /** + * This method initializes the TLV. + * + */ + void Init(void) { SetType(Mle::Tlv::kActiveTimestamp); SetLength(sizeof(*this) - sizeof(Tlv)); } + + /** + * This method indicates whether or not the TLV appears to be well-formed. + * + * @retval TRUE If the TLV appears to be well-formed. + * @retval FALSE If the TLV does not appear to be well-formed. + * + */ + bool IsValid(void) const { return GetLength() == sizeof(*this) - sizeof(Tlv); } +} OT_TOOL_PACKED_END; + +/** + * This class implements Pending Timestamp TLV generation and parsing. + * + */ +OT_TOOL_PACKED_BEGIN +class PendingTimestampTlv : public Tlv, public MeshCoP::Timestamp +{ +public: + /** + * This method initializes the TLV. + * + */ + void Init(void) { SetType(Mle::Tlv::kPendingTimestamp); SetLength(sizeof(*this) - sizeof(Tlv)); } + + /** + * This method indicates whether or not the TLV appears to be well-formed. + * + * @retval TRUE If the TLV appears to be well-formed. + * @retval FALSE If the TLV does not appear to be well-formed. + * + */ + bool IsValid(void) const { return GetLength() == sizeof(*this) - sizeof(Tlv); } +} OT_TOOL_PACKED_END; + /** * @} * diff --git a/src/core/thread/network_data_leader.cpp b/src/core/thread/network_data_leader.cpp index 3869f316f..8004c12c0 100644 --- a/src/core/thread/network_data_leader.cpp +++ b/src/core/thread/network_data_leader.cpp @@ -86,11 +86,27 @@ uint8_t Leader::GetVersion(void) const return mVersion; } +void Leader::IncrementVersion(void) +{ + if (mMle.GetDeviceState() == Mle::kDeviceStateLeader) + { + mVersion++; + } +} + uint8_t Leader::GetStableVersion(void) const { return mStableVersion; } +void Leader::IncrementStableVersion(void) +{ + if (mMle.GetDeviceState() == Mle::kDeviceStateLeader) + { + mStableVersion++; + } +} + uint32_t Leader::GetContextIdReuseDelay(void) const { return mContextIdReuseDelay; diff --git a/src/core/thread/network_data_leader.hpp b/src/core/thread/network_data_leader.hpp index f630b9847..b2787f05f 100644 --- a/src/core/thread/network_data_leader.hpp +++ b/src/core/thread/network_data_leader.hpp @@ -99,6 +99,12 @@ public: */ uint8_t GetVersion(void) const; + /** + * This method increments the Thread Network Data version. + * + */ + void IncrementVersion(void); + /** * This method returns the Thread Network Data stable version. * @@ -107,6 +113,12 @@ public: */ uint8_t GetStableVersion(void) const; + /** + * This method increments the Thread Network Data stable version. + * + */ + void IncrementStableVersion(void); + /** * This method returns CONTEXT_ID_RESUSE_DELAY value. * @@ -258,7 +270,7 @@ private: uint8_t mVersion; Coap::Server &mCoapServer; - Ip6::Netif &mNetif; + ThreadNetif &mNetif; Mle::MleRouter &mMle; }; diff --git a/src/core/thread/thread_netif.cpp b/src/core/thread/thread_netif.cpp index 73e9c183c..d1fdaa875 100644 --- a/src/core/thread/thread_netif.cpp +++ b/src/core/thread/thread_netif.cpp @@ -40,6 +40,7 @@ #include #include #include +#include using Thread::Encoding::BigEndian::HostSwap16; @@ -56,6 +57,8 @@ static const char name[] = "thread"; ThreadNetif::ThreadNetif(void): mCoapServer(kCoapUdpPort), mAddressResolver(*this), + mActiveDataset(*this), + mPendingDataset(*this), mKeyManager(*this), mLowpan(*this), mMac(*this), diff --git a/src/core/thread/thread_netif.hpp b/src/core/thread/thread_netif.hpp index 493fe2409..dab355b76 100644 --- a/src/core/thread/thread_netif.hpp +++ b/src/core/thread/thread_netif.hpp @@ -40,6 +40,7 @@ #include #include #include +#include #include #include #include @@ -205,9 +206,15 @@ public: */ NetworkData::Leader &GetNetworkDataLeader(void) { return mNetworkDataLeader; } + MeshCoP::ActiveDataset &GetActiveDataset(void) { return mActiveDataset; } + + MeshCoP::PendingDataset &GetPendingDataset(void) { return mPendingDataset; } + private: Coap::Server mCoapServer; AddressResolver mAddressResolver; + MeshCoP::ActiveDataset mActiveDataset; + MeshCoP::PendingDataset mPendingDataset; Ip6::Filter mIp6Filter; KeyManager mKeyManager; Lowpan::Lowpan mLowpan; diff --git a/src/core/thread/thread_uris.hpp b/src/core/thread/thread_uris.hpp index 8f9010df8..c3169cd5a 100644 --- a/src/core/thread/thread_uris.hpp +++ b/src/core/thread/thread_uris.hpp @@ -74,6 +74,22 @@ namespace Thread { */ #define OPENTHREAD_URI_ADDRESS_SOLICIT "a/as" +/** + * @def OPENTHREAD_URI_ACTIVE_SET + * + * The URI Path for MGMT_ACTIVE_SET + * + */ +#define OPENTHREAD_URI_ACTIVE_SET "c/as" + +/** + * @def OPENTHREAD_URI_PENDING_SET + * + * The URI Path for MGMT_PENDING_SET + * + */ +#define OPENTHREAD_URI_PENDING_SET "c/ps" + /** * @def OPENTHREAD_URI_SERVER_DATA * diff --git a/src/core/thread/topology.hpp b/src/core/thread/topology.hpp index 01dfa6865..ce3354886 100644 --- a/src/core/thread/topology.hpp +++ b/src/core/thread/topology.hpp @@ -101,7 +101,7 @@ public: Ip6::Address mIp6Address[kMaxIp6AddressPerChild]; ///< Registered IPv6 addresses uint32_t mTimeout; ///< Child timeout uint16_t mFragmentOffset; ///< 6LoWPAN fragment offset - uint8_t mRequestTlvs[4]; ///< Requested MLE TLVs + uint8_t mRequestTlvs[5]; ///< Requested MLE TLVs uint8_t mNetworkDataVersion; ///< Current Network Data version };