mirror of
https://github.com/espressif/openthread.git
synced 2026-07-31 08:07:47 +00:00
Support meshcop datasets and propagation. (#330)
This commit is contained in:
@@ -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);
|
||||
|
||||
@@ -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;
|
||||
|
||||
/**
|
||||
|
||||
@@ -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 \
|
||||
|
||||
@@ -39,6 +39,7 @@
|
||||
#include <openthread-config.h>
|
||||
|
||||
#include "cli.hpp"
|
||||
#include "cli_dataset.hpp"
|
||||
#include <common/encoding.hpp>
|
||||
#include <platform/uart.h>
|
||||
|
||||
@@ -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;
|
||||
|
||||
+4
-2
@@ -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;
|
||||
|
||||
@@ -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 <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
#include <openthread.h>
|
||||
#include <openthread-config.h>
|
||||
|
||||
#include "cli.hpp"
|
||||
#include "cli_dataset.hpp"
|
||||
#include <common/encoding.hpp>
|
||||
#include <thread/meshcop_dataset.hpp>
|
||||
#include <thread/thread_netif.hpp>
|
||||
|
||||
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<MeshCoP::ActiveTimestampTlv *>(tlv);
|
||||
sServer->OutputFormat("Active Timestamp: %d\r\n", tmp->GetSeconds());
|
||||
}
|
||||
|
||||
if ((tlv = aDataset.Get(MeshCoP::Tlv::kChannel)) != NULL)
|
||||
{
|
||||
MeshCoP::ChannelTlv *tmp = static_cast<MeshCoP::ChannelTlv *>(tlv);
|
||||
sServer->OutputFormat("Channel: %d\r\n", tmp->GetChannel());
|
||||
}
|
||||
|
||||
if ((tlv = aDataset.Get(MeshCoP::Tlv::kDelayTimer)) != NULL)
|
||||
{
|
||||
MeshCoP::DelayTimerTlv *tmp = static_cast<MeshCoP::DelayTimerTlv *>(tlv);
|
||||
sServer->OutputFormat("Delay: %d\r\n", tmp->GetDelayTimer());
|
||||
}
|
||||
|
||||
if ((tlv = aDataset.Get(MeshCoP::Tlv::kExtendedPanId)) != NULL)
|
||||
{
|
||||
MeshCoP::ExtendedPanIdTlv *tmp = static_cast<MeshCoP::ExtendedPanIdTlv *>(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<MeshCoP::MeshLocalPrefixTlv *>(tlv);
|
||||
const uint8_t *prefix = tmp->GetMeshLocalPrefix();
|
||||
sServer->OutputFormat("Mesh Local Prefix: %x:%x:%x:%x/64\r\n",
|
||||
(static_cast<uint16_t>(prefix[0]) << 16) | prefix[1],
|
||||
(static_cast<uint16_t>(prefix[2]) << 16) | prefix[3],
|
||||
(static_cast<uint16_t>(prefix[4]) << 16) | prefix[5],
|
||||
(static_cast<uint16_t>(prefix[6]) << 16) | prefix[7]);
|
||||
}
|
||||
|
||||
if ((tlv = aDataset.Get(MeshCoP::Tlv::kNetworkMasterKey)) != NULL)
|
||||
{
|
||||
MeshCoP::NetworkMasterKeyTlv *tmp = static_cast<MeshCoP::NetworkMasterKeyTlv *>(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<MeshCoP::NetworkNameTlv *>(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<MeshCoP::PanIdTlv *>(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
|
||||
@@ -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 <stdarg.h>
|
||||
|
||||
#include <cli/cli_server.hpp>
|
||||
#include <thread/meshcop_dataset.hpp>
|
||||
|
||||
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_
|
||||
@@ -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 \
|
||||
|
||||
@@ -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
|
||||
*
|
||||
|
||||
@@ -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 <stdio.h>
|
||||
|
||||
#include <common/code_utils.hpp>
|
||||
#include <thread/meshcop_tlvs.hpp>
|
||||
#include <thread/meshcop_dataset.hpp>
|
||||
|
||||
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<Tlv *>(mTlvs);
|
||||
Tlv *end = reinterpret_cast<Tlv *>(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<const Tlv *>(mTlvs);
|
||||
const Tlv *end = reinterpret_cast<const Tlv *>(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<uint8_t *>(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<uint8_t *>(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
|
||||
@@ -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 <common/message.hpp>
|
||||
#include <thread/meshcop_tlvs.hpp>
|
||||
|
||||
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_
|
||||
@@ -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 <stdio.h>
|
||||
|
||||
#include <coap/coap_header.hpp>
|
||||
#include <common/code_utils.hpp>
|
||||
#include <common/logging.hpp>
|
||||
#include <common/timer.hpp>
|
||||
#include <platform/random.h>
|
||||
#include <thread/meshcop_dataset.hpp>
|
||||
#include <thread/meshcop_dataset_manager.hpp>
|
||||
#include <thread/thread_netif.hpp>
|
||||
#include <thread/thread_tlvs.hpp>
|
||||
#include <thread/thread_uris.hpp>
|
||||
|
||||
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<DatasetManager *>(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<Timestamp *>(×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<DatasetManager *>(aContext);
|
||||
obj->HandleUdpReceive(*static_cast<Message *>(aMessage), *static_cast<const Ip6::MessageInfo *>(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<DatasetManager *>(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<const Tlv *>(dataset->GetBytes());
|
||||
end = reinterpret_cast<const Tlv *>(dataset->GetBytes() + dataset->GetSize());
|
||||
|
||||
while (cur < end)
|
||||
{
|
||||
switch (cur->GetType())
|
||||
{
|
||||
case Tlv::kChannel:
|
||||
{
|
||||
const ChannelTlv *channel = static_cast<const ChannelTlv *>(cur);
|
||||
mNetif.GetMac().SetChannel(channel->GetChannel());
|
||||
break;
|
||||
}
|
||||
|
||||
case Tlv::kPanId:
|
||||
{
|
||||
const PanIdTlv *panid = static_cast<const PanIdTlv *>(cur);
|
||||
mNetif.GetMac().SetPanId(panid->GetPanId());
|
||||
break;
|
||||
}
|
||||
|
||||
case Tlv::kExtendedPanId:
|
||||
{
|
||||
const ExtendedPanIdTlv *extpanid = static_cast<const ExtendedPanIdTlv *>(cur);
|
||||
mNetif.GetMac().SetExtendedPanId(extpanid->GetExtendedPanId());
|
||||
break;
|
||||
}
|
||||
|
||||
case Tlv::kNetworkName:
|
||||
{
|
||||
const NetworkNameTlv *extpanid = static_cast<const NetworkNameTlv *>(cur);
|
||||
mNetif.GetMac().SetNetworkName(extpanid->GetNetworkName());
|
||||
break;
|
||||
}
|
||||
|
||||
case Tlv::kNetworkMasterKey:
|
||||
{
|
||||
const NetworkMasterKeyTlv *key = static_cast<const NetworkMasterKeyTlv *>(cur);
|
||||
mNetif.GetKeyManager().SetMasterKey(key->GetNetworkMasterKey(), key->GetLength());
|
||||
break;
|
||||
}
|
||||
|
||||
case Tlv::kMeshLocalPrefix:
|
||||
{
|
||||
const MeshLocalPrefixTlv *prefix = static_cast<const MeshLocalPrefixTlv *>(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<DelayTimerTlv *>(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<DelayTimerTlv *>(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<DelayTimerTlv *>(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<PendingDataset *>(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<ActiveTimestampTlv *>(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
|
||||
@@ -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 <coap/coap_server.hpp>
|
||||
#include <common/timer.hpp>
|
||||
#include <net/udp6.hpp>
|
||||
#include <thread/meshcop_dataset.hpp>
|
||||
#include <thread/mle.hpp>
|
||||
#include <thread/network_data_leader.hpp>
|
||||
|
||||
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_
|
||||
@@ -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 <common/code_utils.hpp>
|
||||
#include <thread/meshcop_tlvs.hpp>
|
||||
|
||||
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
|
||||
@@ -41,6 +41,7 @@
|
||||
#include <common/encoding.hpp>
|
||||
|
||||
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<Tlv *>(reinterpret_cast<uint8_t *>(this) + sizeof(*this) + mLength);
|
||||
}
|
||||
|
||||
const Tlv *GetNext() const {
|
||||
return reinterpret_cast<const Tlv *>(reinterpret_cast<const uint8_t *>(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<uint8_t *>(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<uint8_t *>(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<const uint8_t *>(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.
|
||||
*
|
||||
|
||||
+112
-59
@@ -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<MeshCoP::Timestamp *>(×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<MeshCoP::Timestamp *>(×tampTlv) = timestamp;
|
||||
error = aMessage.Append(×tampTlv, sizeof(timestampTlv));
|
||||
|
||||
exit:
|
||||
return error;
|
||||
}
|
||||
|
||||
void Mle::HandleNetifStateChanged(uint32_t aFlags, void *aContext)
|
||||
{
|
||||
Mle *obj = reinterpret_cast<Mle *>(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))
|
||||
|
||||
+31
-14
@@ -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);
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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:
|
||||
|
||||
@@ -40,6 +40,7 @@
|
||||
#include <common/encoding.hpp>
|
||||
#include <common/message.hpp>
|
||||
#include <net/ip6_address.hpp>
|
||||
#include <thread/meshcop_tlvs.hpp>
|
||||
#include <thread/mle_constants.hpp>
|
||||
|
||||
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;
|
||||
|
||||
/**
|
||||
* @}
|
||||
*
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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;
|
||||
};
|
||||
|
||||
|
||||
@@ -40,6 +40,7 @@
|
||||
#include <thread/mle.hpp>
|
||||
#include <thread/thread_netif.hpp>
|
||||
#include <thread/thread_tlvs.hpp>
|
||||
#include <thread/thread_uris.hpp>
|
||||
|
||||
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),
|
||||
|
||||
@@ -40,6 +40,7 @@
|
||||
#include <net/netif.hpp>
|
||||
#include <thread/address_resolver.hpp>
|
||||
#include <thread/key_manager.hpp>
|
||||
#include <thread/meshcop_dataset_manager.hpp>
|
||||
#include <thread/mesh_forwarder.hpp>
|
||||
#include <thread/mle.hpp>
|
||||
#include <thread/mle_router.hpp>
|
||||
@@ -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;
|
||||
|
||||
@@ -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
|
||||
*
|
||||
|
||||
@@ -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
|
||||
};
|
||||
|
||||
|
||||
Reference in New Issue
Block a user