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()