diff --git a/etc/visual-studio/libopenthread.vcxproj b/etc/visual-studio/libopenthread.vcxproj
index da4665943..155c6d504 100644
--- a/etc/visual-studio/libopenthread.vcxproj
+++ b/etc/visual-studio/libopenthread.vcxproj
@@ -35,6 +35,7 @@
+
@@ -52,6 +53,7 @@
+
@@ -94,6 +96,7 @@
+
@@ -113,6 +116,7 @@
+
diff --git a/etc/visual-studio/libopenthread_k.vcxproj b/etc/visual-studio/libopenthread_k.vcxproj
index ae41c0a2f..72e5e9232 100644
--- a/etc/visual-studio/libopenthread_k.vcxproj
+++ b/etc/visual-studio/libopenthread_k.vcxproj
@@ -37,6 +37,7 @@
+
@@ -52,6 +53,7 @@
+
@@ -94,6 +96,7 @@
+
@@ -113,6 +116,7 @@
+
diff --git a/include/commissioning/commissioner.h b/include/commissioning/commissioner.h
index db2b547c3..a919ad3c9 100644
--- a/include/commissioning/commissioner.h
+++ b/include/commissioning/commissioner.h
@@ -104,6 +104,22 @@ ThreadError otCommissionerRemoveJoiner(otInstance *aIntsance, const otExtAddress
*/
ThreadError otCommissionerSetProvisioningUrl(otInstance *aInstance, const char *aProvisioningUrl);
+/**
+ * This function sends an Announce Begin message.
+ *
+ * @param[in] aInstance A pointer to an OpenThread instance.
+ * @param[in] aChannelMask The channel mask value.
+ * @param[in] aCount The number of energy measurements per channel.
+ * @param[in] aPeriod The time between energy measurements (milliseconds).
+ * @param[in] aAddress A pointer to the IPv6 destination.
+ *
+ * @retval kThreadError_None Successfully enqueued the Announce Begin message.
+ * @retval kThreadError_NoBufs Insufficient buffers to generate an Announce Begin message.
+ *
+ */
+ThreadError otCommissionerAnnounceBegin(otInstance *aInstance, uint32_t aChannelMask, uint8_t aCount, uint16_t aPeriod,
+ const otIp6Address *aAddress);
+
/**
* This function pointer is called when the Commissioner receives an Energy Report.
*
diff --git a/src/cli/cli.cpp b/src/cli/cli.cpp
index 43236ab49..0f9d964e3 100644
--- a/src/cli/cli.cpp
+++ b/src/cli/cli.cpp
@@ -2029,6 +2029,33 @@ void Interpreter::ProcessCommissioner(int argc, char *argv[])
{
SuccessOrExit(error = otCommissionerSetProvisioningUrl(mInstance, (argc > 1) ? argv[1] : NULL));
}
+ else if (strcmp(argv[0], "announce") == 0)
+ {
+ long mask;
+ long count;
+ long period;
+ otIp6Address address;
+
+ VerifyOrExit(argc > 4, error = kThreadError_Parse);
+
+ // mask
+ SuccessOrExit(error = ParseLong(argv[1], mask));
+
+ // count
+ SuccessOrExit(error = ParseLong(argv[2], count));
+
+ // period
+ SuccessOrExit(error = ParseLong(argv[3], period));
+
+ // destination
+ SuccessOrExit(error = otIp6AddressFromString(argv[4], &address));
+
+ SuccessOrExit(error = otCommissionerAnnounceBegin(mInstance,
+ static_cast(mask),
+ static_cast(count),
+ static_cast(period),
+ &address));
+ }
else if (strcmp(argv[0], "energy") == 0)
{
long mask;
diff --git a/src/core/Makefile.am b/src/core/Makefile.am
index 6ee5aeda4..655a61c8d 100644
--- a/src/core/Makefile.am
+++ b/src/core/Makefile.am
@@ -64,6 +64,7 @@ libopenthread_a_SOURCES = \
net/netif.cpp \
net/udp6.cpp \
thread/address_resolver.cpp \
+ thread/announce_begin_server.cpp \
thread/energy_scan_server.cpp \
thread/key_manager.cpp \
thread/link_quality.cpp \
@@ -88,6 +89,7 @@ libopenthread_a_SOURCES = \
if OPENTHREAD_ENABLE_COMMISSIONER
libopenthread_a_SOURCES += \
+ meshcop/announce_begin_client.cpp \
meshcop/commissioner.cpp \
meshcop/energy_scan_client.cpp \
meshcop/panid_query_client.cpp \
@@ -129,6 +131,7 @@ noinst_HEADERS = \
mac/mac_frame.hpp \
mac/mac_whitelist.hpp \
mac/mac_blacklist.hpp \
+ meshcop/announce_begin_client.hpp \
meshcop/commissioner.hpp \
meshcop/dtls.hpp \
meshcop/energy_scan_client.hpp \
@@ -148,6 +151,7 @@ noinst_HEADERS = \
net/udp6.hpp \
net/tcp.hpp \
thread/address_resolver.hpp \
+ thread/announce_begin_server.hpp \
thread/energy_scan_server.hpp \
thread/key_manager.hpp \
thread/link_quality.hpp \
diff --git a/src/core/common/message.cpp b/src/core/common/message.cpp
index e76a66e32..02b8f0caf 100644
--- a/src/core/common/message.cpp
+++ b/src/core/common/message.cpp
@@ -238,6 +238,16 @@ void Message::SetType(uint8_t aType)
mInfo.mType = aType;
}
+uint8_t Message::GetSubType(void) const
+{
+ return mInfo.mSubType;
+}
+
+void Message::SetSubType(uint8_t aSubType)
+{
+ mInfo.mSubType = aSubType;
+}
+
ThreadError Message::Append(const void *aBuf, uint16_t aLength)
{
ThreadError error = kThreadError_None;
@@ -498,6 +508,16 @@ void Message::SetPanId(uint16_t aPanId)
mInfo.mPanId = aPanId;
}
+uint8_t Message::GetChannel(void) const
+{
+ return mInfo.mChannel;
+}
+
+void Message::SetChannel(uint8_t aChannel)
+{
+ mInfo.mChannel = aChannel;
+}
+
uint8_t Message::GetTimeout(void) const
{
return mInfo.mTimeout;
@@ -543,36 +563,6 @@ void Message::SetLinkSecurityEnabled(bool aLinkSecurityEnabled)
mInfo.mLinkSecurity = aLinkSecurityEnabled;
}
-bool Message::IsMleDiscoverRequest(void) const
-{
- return mInfo.mMleDiscoverRequest;
-}
-
-void Message::SetMleDiscoverRequest(bool aMleDiscoverRequest)
-{
- mInfo.mMleDiscoverRequest = aMleDiscoverRequest;
-}
-
-bool Message::IsMleDiscoverResponse(void) const
-{
- return mInfo.mMleDiscoverResponse;
-}
-
-void Message::SetMleDiscoverResponse(bool aMleDiscoverResponse)
-{
- mInfo.mMleDiscoverResponse = aMleDiscoverResponse;
-}
-
-bool Message::IsJoinerEntrust(void) const
-{
- return mInfo.mJoinerEntrust;
-}
-
-void Message::SetJoinerEntrust(bool aJoinerEntrust)
-{
- mInfo.mJoinerEntrust = aJoinerEntrust;
-}
-
uint16_t Message::UpdateChecksum(uint16_t aChecksum, uint16_t aOffset, uint16_t aLength) const
{
Buffer *curBuffer;
diff --git a/src/core/common/message.hpp b/src/core/common/message.hpp
index 37cfa528f..be5476922 100644
--- a/src/core/common/message.hpp
+++ b/src/core/common/message.hpp
@@ -118,16 +118,18 @@ struct MessageInfo
uint16_t mDatagramTag; ///< The datagram tag used for 6LoWPAN fragmentation.
uint8_t mChildMask[8]; ///< A bit-vector to indicate which sleepy children need to receive this.
- uint16_t mPanId; ///< The Destination PAN ID.
uint8_t mTimeout; ///< Seconds remaining before dropping the message.
int8_t mInterfaceId; ///< The interface ID.
+ union
+ {
+ uint16_t mPanId; ///< Used for MLE Discover Request and Response messages.
+ uint8_t mChannel; ///< Used for MLE Announce.
+ };
uint8_t mType : 2; ///< Identifies the type of message.
+ uint8_t mSubType : 3; ///< Identifies the message sub type.
bool mDirectTx : 1; ///< Used to indicate whether a direct transmission is required.
bool mLinkSecurity : 1; ///< Indicates whether or not link security is enabled.
- bool mMleDiscoverRequest : 1; ///< Identifies MLE Discover Request.
- bool mMleDiscoverResponse : 1; ///< Identifies MLE Discover Response.
- bool mJoinerEntrust : 1; ///< Indicates whether or not this message is a Joiner Entrust.
};
/**
@@ -221,6 +223,15 @@ public:
kTypeMacDataPoll = 2, ///< A MAC data poll message
};
+ enum
+ {
+ kSubTypeNone = 0, ///< None
+ kSubTypeMleAnnounce = 1, ///< MLE Announce
+ kSubTypeMleDiscoverRequest = 2, ///< MLE Discover Request
+ kSubTypeMleDiscoverResponse = 3, ///< MLE Discover Response
+ kSubTypeJoinerEntrust = 4, ///< Joiner Entrust
+ };
+
/**
* This method frees this message buffer.
*
@@ -292,6 +303,22 @@ public:
*/
uint8_t GetType(void) const;
+ /**
+ * This method returns the sub type of the message.
+ *
+ * @returns The sub type of the message.
+ *
+ */
+ uint8_t GetSubType(void) const;
+
+ /**
+ * This method sets the message sub type.
+ *
+ * @param[in] aSubType The message sub type.
+ *
+ */
+ void SetSubType(uint8_t aSubType);
+
/**
* This method prepends bytes to the front of the message.
*
@@ -412,6 +439,8 @@ public:
/**
* This method returns the IEEE 802.15.4 Destination PAN ID.
*
+ * @note Only use this when sending MLE Discover Request or Response messages.
+ *
* @returns The IEEE 802.15.4 Destination PAN ID.
*
*/
@@ -420,11 +449,33 @@ public:
/**
* This method sets the IEEE 802.15.4 Destination PAN ID.
*
+ * @note Only use this when sending MLE Discover Request or Response messages.
+ *
* @param[in] aPanId The IEEE 802.15.4 Destination PAN ID.
*
*/
void SetPanId(uint16_t aPanId);
+ /**
+ * This method returns the IEEE 802.15.4 Channel to use for transmission.
+ *
+ * @note Only use this when sending MLE Announce messages.
+ *
+ * @returns The IEEE 802.15.4 Channel to use for transmission.
+ *
+ */
+ uint8_t GetChannel(void) const;
+
+ /**
+ * This method sets the IEEE 802.15.4 Channel to use for transmission.
+ *
+ * @note Only use this when sending MLE Announce messages.
+ *
+ * @param[in] aChannel The IEEE 802.15.4 Channel to use for transmission.
+ *
+ */
+ void SetChannel(uint8_t aChannel);
+
/**
* This method returns the timeout used for 6LoWPAN reassembly.
*
@@ -495,57 +546,6 @@ public:
*/
void SetLinkSecurityEnabled(bool aLinkSecurityEnabled);
- /**
- * This method indicates whether or not this message is an MLE Discovery Request.
- *
- * @retval TRUE If this message is an MLE Discovery Request.
- * @retval FALSE If this message is not an MLE Discovery Request.
- *
- */
- bool IsMleDiscoverRequest(void) const;
-
- /**
- * This method sets whether or not this message is an MLE Discovery Request.
- *
- * @param[in] aLinkSecurityEnabled TRUE if this message is an MLE Discovery Request, FALSE otherwise.
- *
- */
- void SetMleDiscoverRequest(bool aMleDiscoverRequest);
-
- /**
- * This method indicates whether or not this message is an MLE Discovery Response.
- *
- * @retval TRUE If this message is an MLE Discovery Response.
- * @retval FALSE If this message is not an MLE Discovery Response.
- *
- */
- bool IsMleDiscoverResponse(void) const;
-
- /**
- * This method sets whether or not this message is an MLE Discovery Response.
- *
- * @param[in] aLinkSecurityEnabled TRUE if this message is an MLE Discovery Response, FALSE otherwise.
- *
- */
- void SetMleDiscoverResponse(bool aMleDiscoverResponse);
-
- /**
- * This method indicates whether or not this message is an Joiner Entrust.
- *
- * @retval TRUE If this message is an Joiner Entrust.
- * @retval FALSE If this message is not an Joiner Entrust.
- *
- */
- bool IsJoinerEntrust(void) const;
-
- /**
- * This method sets whether or not this message is an Joiner Entrust.
- *
- * @param[in] aLinkSecurityEnabled TRUE if this message is an Joiner Entrust, FALSE otherwise.
- *
- */
- void SetJoinerEntrust(bool aJoinerEntrust);
-
/**
* This method is used to update a checksum value.
*
diff --git a/src/core/mac/mac.cpp b/src/core/mac/mac.cpp
index 8d4183a76..6aae0751a 100644
--- a/src/core/mac/mac.cpp
+++ b/src/core/mac/mac.cpp
@@ -55,6 +55,16 @@
namespace Thread {
namespace Mac {
+static const uint8_t sMode2Key[] =
+{
+ 0x78, 0x58, 0x16, 0x86, 0xfd, 0xb4, 0x58, 0x0f, 0xb0, 0x92, 0x54, 0x6a, 0xec, 0xbd, 0x15, 0x66
+};
+
+static const otExtAddress sMode2ExtAddress =
+{
+ { 0x35, 0x06, 0xfe, 0xb8, 0x23, 0xd4, 0x87, 0x12 },
+};
+
static const uint8_t sExtendedPanidInit[] = {0xde, 0xad, 0x00, 0xbe, 0xef, 0x00, 0xca, 0xfe};
static const char sNetworkNameInit[] = "OpenThread";
static Mac *sMac;
@@ -561,6 +571,7 @@ void Mac::ProcessTransmitSecurity(Frame &aFrame)
uint8_t tagLength;
Crypto::AesCcm aesCcm;
const uint8_t *key = NULL;
+ const ExtAddress *extAddress = NULL;
if (aFrame.GetSecurityEnabled() == false)
{
@@ -574,6 +585,7 @@ void Mac::ProcessTransmitSecurity(Frame &aFrame)
case Frame::kKeyIdMode0:
key = mKeyManager.GetKek();
frameCounter = mKeyManager.GetKekFrameCounter();
+ extAddress = &mExtAddress;
break;
case Frame::kKeyIdMode1:
@@ -581,6 +593,14 @@ void Mac::ProcessTransmitSecurity(Frame &aFrame)
frameCounter = mKeyManager.GetMacFrameCounter();
mKeyManager.IncrementMacFrameCounter();
aFrame.SetKeyId((mKeyManager.GetCurrentKeySequence() & 0x7f) + 1);
+ extAddress = &mExtAddress;
+ break;
+
+ case Frame::kKeyIdMode2:
+ key = sMode2Key;
+ frameCounter = 0xffffffff;
+ aFrame.SetKeyId(0xff);
+ extAddress = static_cast(&sMode2ExtAddress);
break;
default:
@@ -591,7 +611,7 @@ void Mac::ProcessTransmitSecurity(Frame &aFrame)
aFrame.GetSecurityLevel(securityLevel);
aFrame.SetFrameCounter(frameCounter);
- GenerateNonce(mExtAddress, frameCounter, securityLevel, nonce);
+ GenerateNonce(*extAddress, frameCounter, securityLevel, nonce);
aesCcm.SetKey(key, 16);
tagLength = aFrame.GetFooterLength() - Frame::kFcsSize;
@@ -915,6 +935,7 @@ ThreadError Mac::ProcessReceiveSecurity(Frame &aFrame, const Address &aSrcAddr,
uint8_t keyid;
uint32_t keySequence = 0;
const uint8_t *macKey;
+ const ExtAddress *extAddress;
Crypto::AesCcm aesCcm;
aFrame.SetSecurityValid(false);
@@ -934,6 +955,7 @@ ThreadError Mac::ProcessReceiveSecurity(Frame &aFrame, const Address &aSrcAddr,
{
case Frame::kKeyIdMode0:
VerifyOrExit((macKey = mKeyManager.GetKek()) != NULL, error = kThreadError_Security);
+ extAddress = &aSrcAddr.mExtAddress;
break;
case Frame::kKeyIdMode1:
@@ -970,6 +992,13 @@ ThreadError Mac::ProcessReceiveSecurity(Frame &aFrame, const Address &aSrcAddr,
(frameCounter >= aNeighbor->mValid.mLinkFrameCounter)),
error = kThreadError_Security);
+ extAddress = &aSrcAddr.mExtAddress;
+
+ break;
+
+ case Frame::kKeyIdMode2:
+ macKey = sMode2Key;
+ extAddress = static_cast(&sMode2ExtAddress);
break;
default:
@@ -977,7 +1006,7 @@ ThreadError Mac::ProcessReceiveSecurity(Frame &aFrame, const Address &aSrcAddr,
break;
}
- GenerateNonce(aSrcAddr.mExtAddress, frameCounter, securityLevel, nonce);
+ GenerateNonce(*extAddress, frameCounter, securityLevel, nonce);
tagLength = aFrame.GetFooterLength() - Frame::kFcsSize;
aesCcm.SetKey(macKey, 16);
diff --git a/src/core/meshcop/announce_begin_client.cpp b/src/core/meshcop/announce_begin_client.cpp
new file mode 100644
index 000000000..807148d3b
--- /dev/null
+++ b/src/core/meshcop/announce_begin_client.cpp
@@ -0,0 +1,144 @@
+/*
+ * Copyright (c) 2016, The OpenThread Authors.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. Neither the name of the copyright holder nor the
+ * names of its contributors may be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ */
+
+/**
+ * @file
+ * This file implements the Announce Begin Client.
+ */
+
+#define WPP_NAME "announce_begin_client.tmh"
+
+#ifdef OPENTHREAD_CONFIG_FILE
+#include OPENTHREAD_CONFIG_FILE
+#else
+#include
+#endif
+
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+
+namespace Thread {
+
+AnnounceBeginClient::AnnounceBeginClient(ThreadNetif &aThreadNetif) :
+ mSocket(aThreadNetif.GetIp6().mUdp),
+ mNetif(aThreadNetif)
+{
+ mSocket.Open(HandleUdpReceive, this);
+ mCoapMessageId = static_cast(otPlatRandomGet());
+}
+
+ThreadError AnnounceBeginClient::SendRequest(uint32_t aChannelMask, uint8_t aCount, uint16_t aPeriod,
+ const Ip6::Address &aAddress)
+{
+ ThreadError error = kThreadError_None;
+ Coap::Header header;
+ MeshCoP::CommissionerSessionIdTlv sessionId;
+
+ OT_TOOL_PACKED_BEGIN
+ struct
+ {
+ MeshCoP::ChannelMaskTlv tlv;
+ MeshCoP::ChannelMaskEntry header;
+ uint32_t mask;
+ } OT_TOOL_PACKED_END channelMask;
+
+ MeshCoP::CountTlv count;
+ MeshCoP::PeriodTlv period;
+
+ Ip6::MessageInfo messageInfo;
+ Message *message;
+
+ for (size_t i = 0; i < sizeof(mCoapToken); i++)
+ {
+ mCoapToken[i] = static_cast(otPlatRandomGet());
+ }
+
+ header.Init();
+ header.SetType(aAddress.IsMulticast() ? Coap::Header::kTypeNonConfirmable : Coap::Header::kTypeConfirmable);
+ header.SetCode(Coap::Header::kCodePost);
+ header.SetMessageId(++mCoapMessageId);
+ header.SetToken(mCoapToken, sizeof(mCoapToken));
+ header.AppendUriPathOptions(OPENTHREAD_URI_ANNOUNCE_BEGIN);
+ header.Finalize();
+
+ VerifyOrExit((message = mSocket.NewMessage(0)) != NULL, error = kThreadError_NoBufs);
+ SuccessOrExit(error = message->Append(header.GetBytes(), header.GetLength()));
+
+ sessionId.Init();
+ sessionId.SetCommissionerSessionId(mNetif.GetCommissioner().GetSessionId());
+ SuccessOrExit(error = message->Append(&sessionId, sizeof(sessionId)));
+
+ channelMask.tlv.Init();
+ channelMask.tlv.SetLength(sizeof(channelMask.header) + sizeof(channelMask.mask));
+ channelMask.header.SetChannelPage(0);
+ channelMask.header.SetMaskLength(sizeof(channelMask.mask));
+ channelMask.mask = HostSwap32(aChannelMask);
+ SuccessOrExit(error = message->Append(&channelMask, sizeof(channelMask)));
+
+ count.Init();
+ count.SetCount(aCount);
+ SuccessOrExit(error = message->Append(&count, sizeof(count)));
+
+ period.Init();
+ period.SetPeriod(aPeriod);
+ SuccessOrExit(error = message->Append(&period, sizeof(period)));
+
+ memset(&messageInfo, 0, sizeof(messageInfo));
+ messageInfo.GetPeerAddr() = aAddress;
+ messageInfo.mPeerPort = kCoapUdpPort;
+ messageInfo.mInterfaceId = mNetif.GetInterfaceId();
+ SuccessOrExit(error = mSocket.SendTo(*message, messageInfo));
+
+ otLogInfoMeshCoP("sent announce begin query\r\n");
+
+exit:
+
+ if (error != kThreadError_None && message != NULL)
+ {
+ message->Free();
+ }
+
+ return error;
+}
+
+void AnnounceBeginClient::HandleUdpReceive(void *aContext, otMessage aMessage, const otMessageInfo *aMessageInfo)
+{
+ otLogInfoMeshCoP("received announce begin response\r\n");
+ (void)aContext;
+ (void)aMessage;
+ (void)aMessageInfo;
+}
+
+} // namespace Thread
diff --git a/src/core/meshcop/announce_begin_client.hpp b/src/core/meshcop/announce_begin_client.hpp
new file mode 100644
index 000000000..a53868962
--- /dev/null
+++ b/src/core/meshcop/announce_begin_client.hpp
@@ -0,0 +1,89 @@
+/*
+ * Copyright (c) 2016, The OpenThread Authors.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. Neither the name of the copyright holder nor the
+ * names of its contributors may be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ */
+
+/**
+ * @file
+ * This file includes definitions for responding to Announce Requests.
+ */
+
+#ifndef ANNOUNCE_BEGIN_CLIENT_HPP_
+#define ANNOUNCE_BEGIN_CLIENT_HPP_
+
+#include
+#include
+#include
+#include
+#include
+#include
+
+namespace Thread {
+
+class ThreadNetif;
+
+/**
+ * This class implements handling Announce Begin Requests.
+ *
+ */
+class AnnounceBeginClient
+{
+public:
+ /**
+ * This constructor initializes the object.
+ *
+ */
+ AnnounceBeginClient(ThreadNetif &aThreadNetif);
+
+ /**
+ * This method sends a Announce Begin message.
+ *
+ * @param[in] aChannelMask The channel mask value.
+ * @param[in] aCount The number of energy measurements per channel.
+ * @param[in] aPeriod The time between energy measurements (milliseconds).
+ *
+ * @retval kThreadError_None Successfully enqueued the Announce Begin message.
+ * @retval kThreadError_NoBufs Insufficient buffers to generate a Announce Begin message.
+ *
+ */
+ ThreadError SendRequest(uint32_t aChannelMask, uint8_t aCount, uint16_t mPeriod, const Ip6::Address &aAddress);
+
+private:
+ static void HandleUdpReceive(void *aContext, otMessage aMessage, const otMessageInfo *aMessageInfo);
+
+ Ip6::UdpSocket mSocket;
+ uint8_t mCoapToken[2];
+ uint16_t mCoapMessageId;
+ ThreadNetif &mNetif;
+};
+
+/**
+ * @}
+ */
+
+} // namespace Thread
+
+#endif // ANNOUNCE_BEGIN_CLIENT_HPP_
diff --git a/src/core/meshcop/commissioner.cpp b/src/core/meshcop/commissioner.cpp
index 9ef384fdc..47b5305e8 100644
--- a/src/core/meshcop/commissioner.cpp
+++ b/src/core/meshcop/commissioner.cpp
@@ -56,6 +56,7 @@ namespace Thread {
namespace MeshCoP {
Commissioner::Commissioner(ThreadNetif &aThreadNetif):
+ mAnnounceBegin(aThreadNetif),
mEnergyScan(aThreadNetif),
mPanIdQuery(aThreadNetif),
mTimer(aThreadNetif.GetIp6().mTimerScheduler, HandleTimer, this),
diff --git a/src/core/meshcop/commissioner.hpp b/src/core/meshcop/commissioner.hpp
index d527d9496..c0add804a 100644
--- a/src/core/meshcop/commissioner.hpp
+++ b/src/core/meshcop/commissioner.hpp
@@ -39,6 +39,7 @@
#include
#include
#include
+#include
#include
#include
#include
@@ -120,9 +121,6 @@ public:
*/
uint16_t GetSessionId(void) const;
- EnergyScanClient mEnergyScan;
- PanIdQueryClient mPanIdQuery;
-
/**
* This method sends MGMT_COMMISSIONER_GET.
*
@@ -149,6 +147,10 @@ public:
ThreadError SendMgmtCommissionerSetRequest(const otCommissioningDataset &aDataset,
const uint8_t *aTlvs, uint8_t aLength);
+ AnnounceBeginClient mAnnounceBegin;
+ EnergyScanClient mEnergyScan;
+ PanIdQueryClient mPanIdQuery;
+
private:
static void HandleTimer(void *aContext);
void HandleTimer(void);
diff --git a/src/core/meshcop/joiner_router.cpp b/src/core/meshcop/joiner_router.cpp
index 86d192df6..c64a6c39d 100644
--- a/src/core/meshcop/joiner_router.cpp
+++ b/src/core/meshcop/joiner_router.cpp
@@ -340,7 +340,7 @@ ThreadError JoinerRouter::SendJoinerEntrust(const Ip6::MessageInfo &aMessageInfo
Tlv *tlv;
VerifyOrExit((message = mSocket.NewMessage(0)) != NULL, error = kThreadError_NoBufs);
- message->SetJoinerEntrust(true);
+ message->SetSubType(Message::kSubTypeJoinerEntrust);
header.Init();
header.SetType(Coap::Header::kTypeConfirmable);
diff --git a/src/core/openthread.cpp b/src/core/openthread.cpp
index 6574379bc..9dc05f5e3 100644
--- a/src/core/openthread.cpp
+++ b/src/core/openthread.cpp
@@ -1445,6 +1445,13 @@ ThreadError otCommissionerSetProvisioningUrl(otInstance *, const char *aProvisio
return sThreadNetif->GetCommissioner().SetProvisioningUrl(aProvisioningUrl);
}
+ThreadError otCommissionerAnnounceBegin(otInstance *, uint32_t aChannelMask, uint8_t aCount, uint16_t aPeriod,
+ const otIp6Address *aAddress)
+{
+ return sThreadNetif->GetCommissioner().mAnnounceBegin.SendRequest(aChannelMask, aCount, aPeriod,
+ *static_cast(aAddress));
+}
+
ThreadError otCommissionerEnergyScan(otInstance *, uint32_t aChannelMask, uint8_t aCount, uint16_t aPeriod,
uint16_t aScanDuration, const otIp6Address *aAddress,
otCommissionerEnergyReportCallback aCallback, void *aContext)
diff --git a/src/core/thread/announce_begin_server.cpp b/src/core/thread/announce_begin_server.cpp
new file mode 100644
index 000000000..c9c897a87
--- /dev/null
+++ b/src/core/thread/announce_begin_server.cpp
@@ -0,0 +1,193 @@
+/*
+ * Copyright (c) 2016, The OpenThread Authors.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. Neither the name of the copyright holder nor the
+ * names of its contributors may be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ */
+
+/**
+ * @file
+ * This file implements the Announce Begin Server.
+ */
+
+#define WPP_NAME "announce_begin_server.tmh"
+
+#ifdef OPENTHREAD_CONFIG_FILE
+#include OPENTHREAD_CONFIG_FILE
+#else
+#include
+#endif
+
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+
+using Thread::Encoding::BigEndian::HostSwap32;
+
+namespace Thread {
+
+AnnounceBeginServer::AnnounceBeginServer(ThreadNetif &aThreadNetif) :
+ mTimer(aThreadNetif.GetIp6().mTimerScheduler, &AnnounceBeginServer::HandleTimer, this),
+ mAnnounceBegin(OPENTHREAD_URI_ANNOUNCE_BEGIN, &AnnounceBeginServer::HandleRequest, this),
+ mCoapServer(aThreadNetif.GetCoapServer()),
+ mNetif(aThreadNetif)
+{
+ mCoapServer.AddResource(mAnnounceBegin);
+}
+
+ThreadError AnnounceBeginServer::SendAnnounce(uint32_t aChannelMask)
+{
+ return SendAnnounce(aChannelMask, kDefaultCount, kDefaultPeriod);
+}
+
+ThreadError AnnounceBeginServer::SendAnnounce(uint32_t aChannelMask, uint8_t aCount, uint16_t aPeriod)
+{
+ ThreadError error = kThreadError_None;
+
+ mChannelMask = aChannelMask;
+ mCount = aCount;
+ mPeriod = aPeriod;
+ mChannel = kPhyMinChannel;
+
+ while ((mChannelMask & (1 << mChannel)) == 0)
+ {
+ mChannel++;
+ VerifyOrExit(mChannel <= kPhyMaxChannel,);
+ }
+
+ mTimer.Start(mPeriod);
+
+exit:
+ return error;
+}
+
+void AnnounceBeginServer::HandleRequest(void *aContext, Coap::Header &aHeader, Message &aMessage,
+ const Ip6::MessageInfo &aMessageInfo)
+{
+ static_cast(aContext)->HandleRequest(aHeader, aMessage, aMessageInfo);
+}
+
+void AnnounceBeginServer::HandleRequest(Coap::Header &aHeader, Message &aMessage, const Ip6::MessageInfo &aMessageInfo)
+{
+ OT_TOOL_PACKED_BEGIN
+ struct
+ {
+ MeshCoP::ChannelMaskTlv tlv;
+ MeshCoP::ChannelMaskEntry entry;
+ uint32_t mask;
+ } OT_TOOL_PACKED_END channelMask;
+
+ MeshCoP::CountTlv count;
+ MeshCoP::PeriodTlv period;
+
+ VerifyOrExit(aHeader.GetCode() == Coap::Header::kCodePost, ;);
+
+ SuccessOrExit(MeshCoP::Tlv::GetTlv(aMessage, MeshCoP::Tlv::kChannelMask, sizeof(channelMask), channelMask.tlv));
+ VerifyOrExit(channelMask.tlv.IsValid() &&
+ channelMask.entry.GetChannelPage() == 0 &&
+ channelMask.entry.GetMaskLength() == sizeof(uint32_t), ;);
+
+ SuccessOrExit(MeshCoP::Tlv::GetTlv(aMessage, MeshCoP::Tlv::kCount, sizeof(count), count));
+ VerifyOrExit(count.IsValid(), ;);
+
+ SuccessOrExit(MeshCoP::Tlv::GetTlv(aMessage, MeshCoP::Tlv::kPeriod, sizeof(period), period));
+ VerifyOrExit(period.IsValid(), ;);
+
+ SendAnnounce(HostSwap32(channelMask.mask), count.GetCount(), period.GetPeriod());
+
+ SendResponse(aHeader, aMessageInfo);
+
+exit:
+ return;
+}
+
+ThreadError AnnounceBeginServer::SendResponse(const Coap::Header &aRequestHeader, const Ip6::MessageInfo &aRequestInfo)
+{
+ ThreadError error = kThreadError_None;
+ Message *message = NULL;
+ Coap::Header responseHeader;
+ Ip6::MessageInfo responseInfo;
+
+ VerifyOrExit(aRequestHeader.GetType() == Coap::Header::kTypeConfirmable, ;);
+
+ VerifyOrExit((message = mCoapServer.NewMessage(0)) != NULL, error = kThreadError_NoBufs);
+
+ responseHeader.Init();
+ responseHeader.SetType(Coap::Header::kTypeAcknowledgment);
+ responseHeader.SetCode(Coap::Header::kCodeChanged);
+ responseHeader.SetMessageId(aRequestHeader.GetMessageId());
+ responseHeader.SetToken(aRequestHeader.GetToken(), aRequestHeader.GetTokenLength());
+ responseHeader.Finalize();
+ SuccessOrExit(error = message->Append(responseHeader.GetBytes(), responseHeader.GetLength()));
+
+ memcpy(&responseInfo, &aRequestInfo, sizeof(responseInfo));
+ memset(&responseInfo.mSockAddr, 0, sizeof(responseInfo.mSockAddr));
+ SuccessOrExit(error = mCoapServer.SendMessage(*message, responseInfo));
+
+ otLogInfoMeshCoP("sent announce begin response\r\n");
+
+exit:
+
+ if (error != kThreadError_None && message != NULL)
+ {
+ message->Free();
+ }
+
+ return error;
+}
+
+void AnnounceBeginServer::HandleTimer(void *aContext)
+{
+ static_cast(aContext)->HandleTimer();
+}
+
+void AnnounceBeginServer::HandleTimer(void)
+{
+ mNetif.GetMle().SendAnnounce(mChannel++);
+
+ while (mCount > 0)
+ {
+ if (mChannelMask & (1 << mChannel))
+ {
+ mTimer.Start(mPeriod);
+ break;
+ }
+
+ mChannel++;
+
+ if (mChannel > kPhyMaxChannel)
+ {
+ mChannel = kPhyMinChannel;
+ mCount--;
+ }
+ }
+}
+
+} // namespace Thread
diff --git a/src/core/thread/announce_begin_server.hpp b/src/core/thread/announce_begin_server.hpp
new file mode 100644
index 000000000..9716d3209
--- /dev/null
+++ b/src/core/thread/announce_begin_server.hpp
@@ -0,0 +1,116 @@
+/*
+ * Copyright (c) 2016, The OpenThread Authors.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. Neither the name of the copyright holder nor the
+ * names of its contributors may be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ */
+
+/**
+ * @file
+ * This file includes definitions for responding to Announce Requests.
+ */
+
+#ifndef ANNOUNCE_BEGIN_SERVER_HPP_
+#define ANNOUNCE_BEGIN_SERVER_HPP_
+
+#include
+#include
+#include
+#include
+#include
+
+namespace Thread {
+
+class ThreadNetif;
+
+/**
+ * This class implements handling Announce Begin Requests.
+ *
+ */
+class AnnounceBeginServer
+{
+public:
+ /**
+ * This constructor initializes the object.
+ *
+ */
+ AnnounceBeginServer(ThreadNetif &aThreadNetif);
+
+ /**
+ * This method begins the MLE Announce transmission process using Count=3 and Period=1s.
+ *
+ * @param[in] aChannelMask The channels to use for transmission.
+ *
+ * @retval kThreadError_None Successfully started the transmission process.
+ *
+ */
+ ThreadError SendAnnounce(uint32_t aChannelMask);
+
+ /**
+ * This method begins the MLE Announce transmission process.
+ *
+ * @param[in] aChannelMask The channels to use for transmission.
+ * @param[in] aCount The number of transmissions per channel.
+ * @param[in] aPeriod The time between transmissions (milliseconds).
+ *
+ * @retval kThreadError_None Successfully started the transmission process.
+ *
+ */
+ ThreadError SendAnnounce(uint32_t aChannelMask, uint8_t aCount, uint16_t aPeriod);
+
+private:
+ enum
+ {
+ kDefaultCount = 3,
+ kDefaultPeriod = 1000,
+ };
+
+ static void HandleRequest(void *aContext, Coap::Header &aHeader, Message &aMessage,
+ const Ip6::MessageInfo &aMessageInfo);
+ void HandleRequest(Coap::Header &aHeader, Message &aMessage, const Ip6::MessageInfo &aMessageInfo);
+
+ ThreadError SendResponse(const Coap::Header &aRequestHeader, const Ip6::MessageInfo &aRequestMessageInfo);
+
+ static void HandleTimer(void *aContext);
+ void HandleTimer(void);
+
+ uint32_t mChannelMask;
+ uint16_t mPeriod;
+ uint8_t mCount;
+ uint8_t mChannel;
+
+ Timer mTimer;
+
+ Coap::Resource mAnnounceBegin;
+ Coap::Server &mCoapServer;
+ ThreadNetif &mNetif;
+};
+
+/**
+ * @}
+ */
+
+} // namespace Thread
+
+#endif // ANNOUNCE_BEGIN_SERVER_HPP_
diff --git a/src/core/thread/mesh_forwarder.cpp b/src/core/thread/mesh_forwarder.cpp
index 897775d67..7e35596af 100644
--- a/src/core/thread/mesh_forwarder.cpp
+++ b/src/core/thread/mesh_forwarder.cpp
@@ -613,7 +613,16 @@ ThreadError MeshForwarder::UpdateIp6Route(Message &aMessage)
if (aMessage.IsLinkSecurityEnabled())
{
mMacDest.mLength = sizeof(mMacDest.mShortAddress);
- mMacDest.mShortAddress = mMle.GetNextHop(Mac::kShortAddrBroadcast);
+
+ if (ip6Header.GetDestination().IsLinkLocalMulticast())
+ {
+ mMacDest.mShortAddress = Mac::kShortAddrBroadcast;
+ }
+ else
+ {
+ mMacDest.mShortAddress = mMle.GetNextHop(Mac::kShortAddrBroadcast);
+ }
+
GetMacSourceAddress(ip6Header.GetSource(), mMacSource);
}
else if (ip6Header.GetDestination().IsLinkLocal() || ip6Header.GetDestination().IsLinkLocalMulticast())
@@ -824,7 +833,7 @@ ThreadError MeshForwarder::HandleFrameRequest(Mac::Frame &aFrame)
switch (mSendMessage->GetType())
{
case Message::kTypeIp6:
- if (mSendMessage->IsMleDiscoverRequest())
+ if (mSendMessage->GetSubType() == Message::kSubTypeMleDiscoverRequest)
{
if (!mScanning)
{
@@ -995,17 +1004,41 @@ ThreadError MeshForwarder::SendFragment(Message &aMessage, Mac::Frame &aFrame)
if (aMessage.IsLinkSecurityEnabled())
{
fcf |= Mac::Frame::kFcfSecurityEnabled;
- secCtl = static_cast(aMessage.IsJoinerEntrust() ? Mac::Frame::kKeyIdMode0 : Mac::Frame::kKeyIdMode1);
+
+ switch (aMessage.GetSubType())
+ {
+ case Message::kSubTypeJoinerEntrust:
+ secCtl = static_cast(Mac::Frame::kKeyIdMode0);
+ break;
+
+ case Message::kSubTypeMleAnnounce:
+ secCtl = static_cast(Mac::Frame::kKeyIdMode2);
+ break;
+
+ default:
+ secCtl = static_cast(Mac::Frame::kKeyIdMode1);
+ break;
+ }
+
secCtl |= Mac::Frame::kSecEncMic32;
}
- if (aMessage.IsMleDiscoverRequest() || aMessage.IsMleDiscoverResponse())
+ dstpan = mMac.GetPanId();
+
+ switch (aMessage.GetSubType())
{
+ case Message::kSubTypeMleAnnounce:
+ aFrame.SetChannel(aMessage.GetChannel());
+ dstpan = Mac::kPanIdBroadcast;
+ break;
+
+ case Message::kSubTypeMleDiscoverRequest:
+ case Message::kSubTypeMleDiscoverResponse:
dstpan = aMessage.GetPanId();
- }
- else
- {
- dstpan = mMac.GetPanId();
+ break;
+
+ default:
+ break;
}
if (dstpan == mMac.GetPanId())
@@ -1219,7 +1252,7 @@ void MeshForwarder::HandleSentFrame(Mac::Frame &aFrame, ThreadError aError)
mSendMessage->SetOffset(0);
}
- if (mSendMessage->IsMleDiscoverRequest())
+ if (mSendMessage->GetSubType() == Message::kSubTypeMleDiscoverRequest)
{
mSendBusy = true;
mDiscoverTimer.Start(mScanDuration);
diff --git a/src/core/thread/mle.cpp b/src/core/thread/mle.cpp
index 59efd1840..78587015f 100644
--- a/src/core/thread/mle.cpp
+++ b/src/core/thread/mle.cpp
@@ -161,6 +161,8 @@ Mle::Mle(ThreadNetif &aThreadNetif) :
mRouterSelectionJitterTimeout = 0;
mRouterSelectionJitter = kRouterSelectionJitter;
+
+ mPreviousPanId = Mac::kPanIdBroadcast;
}
ThreadError Mle::Enable(void)
@@ -245,7 +247,7 @@ ThreadError Mle::Discover(uint32_t aScanChannels, uint16_t aScanDuration, uint16
VerifyOrExit((message = mSocket.NewMessage(0)) != NULL, ;);
message->SetLinkSecurityEnabled(false);
- message->SetMleDiscoverRequest(true);
+ message->SetSubType(Message::kSubTypeMleDiscoverRequest);
message->SetPanId(aPanId);
SuccessOrExit(error = AppendHeader(*message, Header::kCommandDiscoveryRequest));
@@ -384,6 +386,12 @@ ThreadError Mle::SetStateChild(uint16_t aRloc16)
mNetif.GetNetworkDataLocal().ClearResubmitDelayTimer();
mNetif.GetIp6().SetForwardingEnabled(false);
+ if (mPreviousPanId != Mac::kPanIdBroadcast)
+ {
+ mPreviousPanId = Mac::kPanIdBroadcast;
+ mNetif.GetAnnounceBeginServer().SendAnnounce(1 << mPreviousChannel);
+ }
+
otLogInfoMle("Mode -> Child\n");
return kThreadError_None;
}
@@ -1064,7 +1072,14 @@ void Mle::HandleParentRequestTimer(void)
switch (mParentRequestMode)
{
case kMleAttachAnyPartition:
- if (mMleRouter.BecomeLeader() != kThreadError_None)
+ if (mPreviousPanId != Mac::kPanIdBroadcast)
+ {
+ mMac.SetChannel(mPreviousChannel);
+ mMac.SetPanId(mPreviousPanId);
+ mPreviousPanId = Mac::kPanIdBroadcast;
+ BecomeDetached();
+ }
+ else if (mMleRouter.BecomeLeader() != kThreadError_None)
{
mParentRequestState = kParentIdle;
BecomeDetached();
@@ -1326,6 +1341,48 @@ exit:
return error;
}
+ThreadError Mle::SendAnnounce(uint8_t aChannel)
+{
+ ThreadError error = kThreadError_None;
+ ChannelTlv channel;
+ PanIdTlv panid;
+ Ip6::Address destination;
+ Message *message;
+ (void)aChannel;
+
+ VerifyOrExit((message = mSocket.NewMessage(0)) != NULL, ;);
+ message->SetSubType(Message::kSubTypeMleAnnounce);
+ message->SetChannel(aChannel);
+ SuccessOrExit(error = AppendHeader(*message, Header::kCommandAnnounce));
+
+ channel.Init();
+ channel.SetChannelPage(0);
+ channel.SetChannel(mMac.GetChannel());
+ SuccessOrExit(error = message->Append(&channel, sizeof(channel)));
+
+ SuccessOrExit(error = AppendActiveTimestamp(*message));
+
+ panid.Init();
+ panid.SetPanId(mMac.GetPanId());
+ SuccessOrExit(error = message->Append(&panid, sizeof(panid)));
+
+ memset(&destination, 0, sizeof(destination));
+ destination.mFields.m16[0] = HostSwap16(0xff02);
+ destination.mFields.m16[7] = HostSwap16(0x0001);
+ SuccessOrExit(error = SendMessage(*message, destination));
+
+ otLogInfoMle("sent announce on channel %d\n", aChannel);
+
+exit:
+
+ if (error != kThreadError_None && message != NULL)
+ {
+ message->Free();
+ }
+
+ return error;
+}
+
ThreadError Mle::SendMessage(Message &aMessage, const Ip6::Address &aDestination)
{
ThreadError error = kThreadError_None;
@@ -1554,7 +1611,8 @@ void Mle::HandleUdpReceive(Message &aMessage, const Ip6::MessageInfo &aMessageIn
command == Header::kCommandParentRequest ||
command == Header::kCommandParentResponse ||
command == Header::kCommandChildIdRequest ||
- command == Header::kCommandChildUpdateRequest))
+ command == Header::kCommandChildUpdateRequest ||
+ command == Header::kCommandAnnounce))
{
otLogDebgMle("mle sequence unknown! %d\n", command);
ExitNow();
@@ -1614,6 +1672,10 @@ void Mle::HandleUdpReceive(Message &aMessage, const Ip6::MessageInfo &aMessageIn
case Header::kCommandChildUpdateResponse:
HandleChildUpdateResponse(aMessage, aMessageInfo);
break;
+
+ case Header::kCommandAnnounce:
+ HandleAnnounce(aMessage, aMessageInfo);
+ break;
}
exit:
@@ -2211,6 +2273,41 @@ exit:
return error;
}
+ThreadError Mle::HandleAnnounce(const Message &aMessage, const Ip6::MessageInfo &aMessageInfo)
+{
+ ThreadError error = kThreadError_None;
+ ChannelTlv channel;
+ ActiveTimestampTlv timestamp;
+ const MeshCoP::Timestamp *localTimestamp;
+ PanIdTlv panid;
+
+ otLogInfoMle("Received announce\n");
+
+ SuccessOrExit(Tlv::GetTlv(aMessage, Tlv::kChannel, sizeof(channel), channel));
+ VerifyOrExit(channel.IsValid(),);
+
+ SuccessOrExit(Tlv::GetTlv(aMessage, Tlv::kActiveTimestamp, sizeof(timestamp), timestamp));
+ VerifyOrExit(timestamp.IsValid(),);
+
+ SuccessOrExit(Tlv::GetTlv(aMessage, Tlv::kPanId, sizeof(panid), panid));
+ VerifyOrExit(panid.IsValid(),);
+
+ localTimestamp = mNetif.GetActiveDataset().GetNetwork().GetTimestamp();
+
+ VerifyOrExit(localTimestamp == NULL || localTimestamp->Compare(timestamp) > 0,);
+
+ Stop();
+ mPreviousChannel = mMac.GetChannel();
+ mPreviousPanId = mMac.GetPanId();
+ mMac.SetChannel(static_cast(channel.GetChannel()));
+ mMac.SetPanId(panid.GetPanId());
+ Start();
+
+exit:
+ (void)aMessageInfo;
+ return error;
+}
+
ThreadError Mle::HandleDiscoveryRequest(const Message &aMessage, const Ip6::MessageInfo &aMessageInfo)
{
ThreadError error = kThreadError_None;
@@ -2291,7 +2388,7 @@ ThreadError Mle::SendDiscoveryResponse(const Ip6::Address &aDestination, uint16_
VerifyOrExit((message = mSocket.NewMessage(0)) != NULL, ;);
message->SetLinkSecurityEnabled(false);
- message->SetMleDiscoverResponse(true);
+ message->SetSubType(Message::kSubTypeMleDiscoverResponse);
message->SetPanId(aPanId);
SuccessOrExit(error = AppendHeader(*message, Header::kCommandDiscoveryResponse));
diff --git a/src/core/thread/mle.hpp b/src/core/thread/mle.hpp
index 2b5970a26..115b7e5ef 100644
--- a/src/core/thread/mle.hpp
+++ b/src/core/thread/mle.hpp
@@ -277,6 +277,7 @@ public:
kCommandChildIdResponse = 12, ///< Child ID Response
kCommandChildUpdateRequest = 13, ///< Child Update Request
kCommandChildUpdateResponse = 14, ///< Child Update Response
+ kCommandAnnounce = 15, ///< Announce
kCommandDiscoveryRequest = 16, ///< Discovery Request
kCommandDiscoveryResponse = 17, ///< Discovery Response
};
@@ -410,6 +411,17 @@ public:
*/
void HandleDiscoverComplete(void);
+ /**
+ * This method generates an MLE Announce message.
+ *
+ * @param[in] aChannel The channel to use when transmitting.
+ *
+ * @retval kThreadError_None Successfully generated an MLE Announce message.
+ * @retval kThreadError_NoBufs Insufficient buffers to generate the MLE Announce message.
+ *
+ */
+ ThreadError SendAnnounce(uint8_t aChannel);
+
/**
* This method causes the Thread interface to detach from the Thread network.
*
@@ -1002,7 +1014,7 @@ protected:
/**
* This method generates an MLE Child Update Request message.
*
- * @retval kThreadError_None Successfully generated an MLE Child Update Request message..
+ * @retval kThreadError_None Successfully generated an MLE Child Update Request message.
* @retval kThreadError_NoBufs Insufficient buffers to generate the MLE Child Update Request message.
*
*/
@@ -1121,6 +1133,7 @@ private:
ThreadError HandleDataResponse(const Message &aMessage, const Ip6::MessageInfo &aMessageInfo);
ThreadError HandleParentResponse(const Message &aMessage, const Ip6::MessageInfo &aMessageInfo,
uint32_t aKeySequence);
+ ThreadError HandleAnnounce(const Message &aMessage, const Ip6::MessageInfo &aMessageInfo);
ThreadError HandleDiscoveryRequest(const Message &aMessage, const Ip6::MessageInfo &aMessageInfo);
ThreadError HandleDiscoveryResponse(const Message &aMessage, const Ip6::MessageInfo &aMessageInfo);
@@ -1159,6 +1172,9 @@ private:
void *mDiscoverContext;
bool mIsDiscoverInProgress;
+ uint8_t mPreviousChannel;
+ uint16_t mPreviousPanId;
+
Ip6::NetifUnicastAddress mLinkLocal16;
Ip6::NetifUnicastAddress mLinkLocal64;
Ip6::NetifUnicastAddress mMeshLocal64;
diff --git a/src/core/thread/mle_tlvs.hpp b/src/core/thread/mle_tlvs.hpp
index d51466463..d5c0e2c46 100644
--- a/src/core/thread/mle_tlvs.hpp
+++ b/src/core/thread/mle_tlvs.hpp
@@ -94,6 +94,8 @@ public:
kStatus = 17, ///< Status TLV
kVersion = 18, ///< Version TLV
kAddressRegistration = 19, ///< Address Registration TLV
+ kChannel = 20, ///< Channel TLV
+ kPanId = 21, ///< PAN ID TLV
kActiveTimestamp = 22, ///< Active Timestamp TLV
kPendingTimestamp = 23, ///< Pending Timestamp TLV
kActiveDataset = 24, ///< Active Operational Dataset TLV
@@ -1450,6 +1452,109 @@ private:
AddressRegistrationEntry mAddresses[4];
} 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 Channel Page value.
+ *
+ * @returns The Channel Page value.
+ *
+ */
+ uint8_t GetChannelPage(void) const { return mChannelPage; }
+
+ /**
+ * This method sets the Channel Page value.
+ *
+ * @param[in] aChannelPage The Channel Page 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 Active Timestamp TLV generation and parsing.
*
diff --git a/src/core/thread/thread_netif.cpp b/src/core/thread/thread_netif.cpp
index 60760c542..01b114340 100644
--- a/src/core/thread/thread_netif.cpp
+++ b/src/core/thread/thread_netif.cpp
@@ -79,6 +79,7 @@ ThreadNetif::ThreadNetif(Ip6::Ip6 &aIp6):
#endif // OPENTHREAD_ENABLE_JOINER
mJoinerRouter(*this),
mLeader(*this),
+ mAnnounceBegin(*this),
mPanIdQuery(*this),
mEnergyScan(*this)
{
diff --git a/src/core/thread/thread_netif.hpp b/src/core/thread/thread_netif.hpp
index 074757174..419d695b6 100644
--- a/src/core/thread/thread_netif.hpp
+++ b/src/core/thread/thread_netif.hpp
@@ -48,6 +48,7 @@
#include
#include
#include
+#include
#include
#include
#include
@@ -246,6 +247,8 @@ public:
MeshCoP::JoinerRouter &GetJoinerRouter(void) { return mJoinerRouter; }
+ AnnounceBeginServer &GetAnnounceBeginServer(void) { return mAnnounceBegin; }
+
#if OPENTHREAD_ENABLE_COMMISSIONER
MeshCoP::Commissioner &GetCommissioner(void) { return mCommissioner; }
#endif // OPENTHREAD_ENABLE_COMMISSIONER
@@ -288,6 +291,7 @@ private:
MeshCoP::JoinerRouter mJoinerRouter;
MeshCoP::Leader mLeader;
+ AnnounceBeginServer mAnnounceBegin;
PanIdQueryServer mPanIdQuery;
EnergyScanServer mEnergyScan;
};
diff --git a/src/core/thread/thread_uris.hpp b/src/core/thread/thread_uris.hpp
index 083f25729..2af5ab7e6 100644
--- a/src/core/thread/thread_uris.hpp
+++ b/src/core/thread/thread_uris.hpp
@@ -130,6 +130,14 @@ namespace Thread {
*/
#define OPENTHREAD_URI_SERVER_DATA "a/sd"
+/**
+ * @def OPENTHREAD_URI_ANNOUNCE_BEGIN
+ *
+ * The URI Path for Announce Begin.
+ *
+ */
+#define OPENTHREAD_URI_ANNOUNCE_BEGIN "c/ab"
+
/**
* @def OPENTHREAD_URI_RELAY_RX
*
diff --git a/tests/scripts/Makefile.am b/tests/scripts/Makefile.am
index 21d21389f..ac7476b28 100644
--- a/tests/scripts/Makefile.am
+++ b/tests/scripts/Makefile.am
@@ -104,6 +104,7 @@ EXTRA_DIST = \
thread-cert/Cert_7_1_05_BorderRouterAsRouter.py \
thread-cert/Cert_8_1_01_Commissioning.py \
thread-cert/Cert_8_1_02_Commissioning.py \
+ thread-cert/Cert_9_2_12_Announce.py \
thread-cert/Cert_9_2_13_EnergyScan.py \
thread-cert/Cert_9_2_14_PanIdQuery.py \
thread-cert/node.py \
@@ -222,6 +223,7 @@ check_SCRIPTS += \
thread-cert/Cert_7_1_05_BorderRouterAsRouter.py \
thread-cert/Cert_8_1_01_Commissioning.py \
thread-cert/Cert_8_1_02_Commissioning.py \
+ thread-cert/Cert_9_2_12_Announce.py \
thread-cert/Cert_9_2_13_EnergyScan.py \
thread-cert/Cert_9_2_14_PanIdQuery.py \
$(NULL)
@@ -242,6 +244,7 @@ TESTS = \
XFAIL_NCP_TESTS = \
thread-cert/Cert_8_1_01_Commissioning.py \
thread-cert/Cert_8_1_02_Commissioning.py \
+ thread-cert/Cert_9_2_12_Announce.py \
thread-cert/Cert_9_2_13_EnergyScan.py \
thread-cert/Cert_9_2_14_PanIdQuery.py \
$(NULL)
diff --git a/tests/scripts/thread-cert/Cert_9_2_12_Announce.py b/tests/scripts/thread-cert/Cert_9_2_12_Announce.py
new file mode 100755
index 000000000..1203962ca
--- /dev/null
+++ b/tests/scripts/thread-cert/Cert_9_2_12_Announce.py
@@ -0,0 +1,129 @@
+#!/usr/bin/python
+#
+# Copyright (c) 2016, The OpenThread Authors.
+# All rights reserved.
+#
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted provided that the following conditions are met:
+# 1. Redistributions of source code must retain the above copyright
+# notice, this list of conditions and the following disclaimer.
+# 2. Redistributions in binary form must reproduce the above copyright
+# notice, this list of conditions and the following disclaimer in the
+# documentation and/or other materials provided with the distribution.
+# 3. Neither the name of the copyright holder nor the
+# names of its contributors may be used to endorse or promote products
+# derived from this software without specific prior written permission.
+#
+# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+# AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+# ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
+# LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+# CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+# SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+# INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+# CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+# ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+# POSSIBILITY OF SUCH DAMAGE.
+#
+
+import time
+import unittest
+
+import node
+
+LEADER1 = 1
+ROUTER1 = 2
+LEADER2 = 3
+ROUTER2 = 4
+MED = 5
+
+DATASET1_TIMESTAMP = 20
+DATASET1_CHANNEL = 11
+DATASET1_PANID = 0xface
+
+DATASET2_TIMESTAMP = 10
+DATASET2_CHANNEL = 12
+DATASET2_PANID = 0xafce
+
+class Cert_9_2_12_Announce(unittest.TestCase):
+ def setUp(self):
+ self.nodes = {}
+ for i in range(1,6):
+ self.nodes[i] = node.Node(i)
+
+ self.nodes[LEADER1].set_active_dataset(DATASET1_TIMESTAMP, channel=DATASET1_CHANNEL, panid=DATASET1_PANID)
+ self.nodes[LEADER1].set_mode('rsdn')
+ self.nodes[LEADER1].add_whitelist(self.nodes[ROUTER1].get_addr64())
+ self.nodes[LEADER1].enable_whitelist()
+
+ self.nodes[ROUTER1].set_active_dataset(DATASET1_TIMESTAMP, channel=DATASET1_CHANNEL, panid=DATASET1_PANID)
+ self.nodes[ROUTER1].set_mode('rsdn')
+ self.nodes[ROUTER1].add_whitelist(self.nodes[LEADER1].get_addr64())
+ self.nodes[ROUTER1].add_whitelist(self.nodes[LEADER2].get_addr64())
+ self.nodes[ROUTER1].enable_whitelist()
+ self.nodes[ROUTER1].set_router_selection_jitter(1)
+
+ self.nodes[LEADER2].set_active_dataset(DATASET2_TIMESTAMP, channel=DATASET2_CHANNEL, panid=DATASET1_PANID)
+ self.nodes[LEADER2].set_mode('rsdn')
+ self.nodes[LEADER2].add_whitelist(self.nodes[ROUTER1].get_addr64())
+ self.nodes[LEADER2].add_whitelist(self.nodes[ROUTER2].get_addr64())
+ self.nodes[LEADER2].enable_whitelist()
+ self.nodes[LEADER2].set_router_selection_jitter(1)
+
+ self.nodes[ROUTER2].set_active_dataset(DATASET2_TIMESTAMP, channel=DATASET2_CHANNEL, panid=DATASET1_PANID)
+ self.nodes[ROUTER2].set_mode('rsdn')
+ self.nodes[ROUTER2].add_whitelist(self.nodes[LEADER2].get_addr64())
+ self.nodes[ROUTER2].add_whitelist(self.nodes[MED].get_addr64())
+ self.nodes[ROUTER2].enable_whitelist()
+ self.nodes[ROUTER2].set_router_selection_jitter(1)
+
+ self.nodes[MED].set_active_dataset(DATASET2_TIMESTAMP, channel=DATASET2_CHANNEL, panid=DATASET1_PANID)
+ self.nodes[MED].set_mode('rsn')
+ self.nodes[MED].add_whitelist(self.nodes[ROUTER2].get_addr64())
+ self.nodes[MED].enable_whitelist()
+
+ def tearDown(self):
+ for node in list(self.nodes.values()):
+ node.stop()
+ del self.nodes
+
+ def test(self):
+ self.nodes[LEADER1].start()
+ self.nodes[LEADER1].set_state('leader')
+ self.assertEqual(self.nodes[LEADER1].get_state(), 'leader')
+
+ self.nodes[ROUTER1].start()
+ time.sleep(5)
+ self.assertEqual(self.nodes[ROUTER1].get_state(), 'router')
+
+ self.nodes[LEADER2].start()
+ self.nodes[LEADER2].set_state('leader')
+ self.assertEqual(self.nodes[LEADER2].get_state(), 'leader')
+
+ self.nodes[ROUTER2].start()
+ time.sleep(5)
+ self.assertEqual(self.nodes[ROUTER2].get_state(), 'router')
+
+ self.nodes[MED].start()
+ time.sleep(5)
+ self.assertEqual(self.nodes[MED].get_state(), 'child')
+
+ ipaddrs = self.nodes[ROUTER1].get_addrs()
+ for ipaddr in ipaddrs:
+ if ipaddr[0:4] != 'fe80':
+ break
+
+ self.nodes[LEADER1].announce_begin(0x1000, 1, 1000, ipaddr)
+ time.sleep(30)
+ self.assertEqual(self.nodes[LEADER2].get_state(), 'router')
+ self.assertEqual(self.nodes[ROUTER2].get_state(), 'router')
+ self.assertEqual(self.nodes[MED].get_state(), 'child')
+
+ ipaddrs = self.nodes[MED].get_addrs()
+ for ipaddr in ipaddrs:
+ if ipaddr[0:4] != 'fe80':
+ self.assertTrue(self.nodes[LEADER1].ping(ipaddr))
+
+if __name__ == '__main__':
+ unittest.main()
diff --git a/tests/scripts/thread-cert/node.py b/tests/scripts/thread-cert/node.py
index 8492e2c1f..b7f5e2ee0 100755
--- a/tests/scripts/thread-cert/node.py
+++ b/tests/scripts/thread-cert/node.py
@@ -425,5 +425,31 @@ class Node:
self.send_command(cmd)
self.pexpect.expect('Done')
+ def set_active_dataset(self, timestamp, panid=None, channel=None):
+ self.send_command('dataset clear')
+ self.pexpect.expect('Done')
+
+ cmd = 'dataset activetimestamp %d' % timestamp
+ self.send_command(cmd)
+ self.pexpect.expect('Done')
+
+ if panid != None:
+ cmd = 'dataset panid %d' % panid
+ self.send_command(cmd)
+ self.pexpect.expect('Done')
+
+ if channel != None:
+ cmd = 'dataset channel %d' % channel
+ self.send_command(cmd)
+ self.pexpect.expect('Done')
+
+ self.send_command('dataset commit active')
+ self.pexpect.expect('Done')
+
+ def announce_begin(self, mask, count, period, ipaddr):
+ cmd = 'commissioner announce ' + str(mask) + ' ' + str(count) + ' ' + str(period) + ' ' + ipaddr
+ self.send_command(cmd)
+ self.pexpect.expect('Done')
+
if __name__ == '__main__':
unittest.main()