From b326f01821c072edf03ac3d488f72a202760fcab Mon Sep 17 00:00:00 2001 From: Jonathan Hui Date: Wed, 3 Aug 2016 13:16:35 -0700 Subject: [PATCH] Implement MLE Discovery Request and Response. (#322) --- include/openthread-types.h | 2 + include/openthread.h | 24 +- src/cli/README.md | 21 +- src/cli/cli.cpp | 28 +- src/cli/cli.hpp | 1 + src/core/Makefile.am | 1 + src/core/common/message.cpp | 50 +++- src/core/common/message.hpp | 87 +++++-- src/core/mac/mac_frame.cpp | 7 +- src/core/openthread.cpp | 14 + src/core/thread/mesh_forwarder.cpp | 92 ++++++- src/core/thread/mesh_forwarder.hpp | 18 ++ src/core/thread/meshcop_tlvs.hpp | 367 ++++++++++++++++++++++++++ src/core/thread/mle.cpp | 402 ++++++++++++++++++++++++++--- src/core/thread/mle.hpp | 118 ++++++++- src/core/thread/mle_tlvs.hpp | 1 + src/core/thread/thread_netif.cpp | 3 +- src/core/thread/thread_netif.hpp | 8 +- 18 files changed, 1149 insertions(+), 95 deletions(-) create mode 100644 src/core/thread/meshcop_tlvs.hpp diff --git a/include/openthread-types.h b/include/openthread-types.h index ffaead717..505ff6f00 100644 --- a/include/openthread-types.h +++ b/include/openthread-types.h @@ -160,6 +160,8 @@ typedef struct otExtAddress * */ +#define OT_PANID_BROADCAST 0xffff ///< IEEE 802.15.4 Broadcast PAN ID + #define OT_CHANNEL_11_MASK (1 << 11) ///< Channel 11 #define OT_CHANNEL_12_MASK (1 << 12) ///< Channel 12 #define OT_CHANNEL_13_MASK (1 << 13) ///< Channel 13 diff --git a/include/openthread.h b/include/openthread.h index a494623d4..f014d82cf 100644 --- a/include/openthread.h +++ b/include/openthread.h @@ -232,7 +232,7 @@ typedef void (*otHandleActiveScanResult)(otActiveScanResult *aResult); * * @param[in] aScanChannels A bit vector indicating which channels to scan (e.g. OT_CHANNEL_11_MASK). * @param[in] aScanDuration The time in milliseconds to spend scanning each channel. - * @param[in] aCallback A pointer to a function that is called when a beacon is received or the scan completes. + * @param[in] aCallback A pointer to a function called on receiving a beacon or scan completes. * * @retval kThreadError_None Accepted the Active Scan request. * @retval kThreadError_Busy Already performing an Active Scan. @@ -247,6 +247,28 @@ ThreadError otActiveScan(uint32_t aScanChannels, uint16_t aScanDuration, otHandl */ bool otActiveScanInProgress(void); +/** + * This function starts a Thread Discovery scan. + * + * @param[in] aScanChannels A bit vector indicating which channels to scan (e.g. OT_CHANNEL_11_MASK). + * @param[in] aScanDuration The time in milliseconds to spend scanning each channel. + * @param[in] aPanId The PAN ID filter (set to Broadcast PAN to disable filter). + * @param[in] aCallback A pointer to a function called on receiving an MLE Discovery Response or scan completes. + * + * @retval kThreadError_None Accepted the Thread Discovery request. + * @retval kThreadError_Busy Already performing an Thread Discovery. + * + */ +ThreadError otDiscover(uint32_t aScanChannels, uint16_t aScanDuration, uint16_t aPanid, + otHandleActiveScanResult aCallback); + +/** + * This function determines if an MLE Thread Discovery is currently in progress. + * + * @returns true if an active scan is in progress. + */ +bool otDiscoverInProgress(void); + /** * @} * diff --git a/src/cli/README.md b/src/cli/README.md index 97c5a12c6..786e19c68 100644 --- a/src/cli/README.md +++ b/src/cli/README.md @@ -12,6 +12,7 @@ OpenThread test scripts use the CLI to execute test cases. * [childtimeout](#childtimeout) * [contextreusedelay](#contextreusedelay) * [counter](#counter) +* [discover](#discover) * [eidcache](#eidcache) * [extaddr](#extaddr) * [extpanid](#extpanid) @@ -168,6 +169,20 @@ RxTotal: 11 RxErrOther: 0 ``` +### discover \[channel\] + +Perform an MLE Discovery operation. + +* channel: The channel to discover on. If no channel is provided, the discovery will cover all valid channels. + +```bash +> discover +| J | Network Name | Extended PAN | PAN | MAC Address | Ch | dBm | LQI | ++---+------------------+------------------+------+------------------+----+-----+-----+ +| 0 | OpenThread | dead00beef00cafe | ffff | f1d92a82c8d8fe43 | 11 | -20 | 0 | +Done +``` + ### eidcache Print the EID-to-RLOC cache entries. @@ -574,9 +589,9 @@ Perform an IEEE 802.15.4 Active Scan. ```bash > scan -| J | Network Name | Extended PAN | PAN | MAC Address | Ch | dBm | -+---+------------------+------------------+------+------------------+----+-----+ -| 0 | OpenThread | dead00beef00cafe | ffff | f1d92a82c8d8fe43 | 11 | -20 | +| J | Network Name | Extended PAN | PAN | MAC Address | Ch | dBm | LQI | ++---+------------------+------------------+------+------------------+----+-----+-----+ +| 0 | OpenThread | dead00beef00cafe | ffff | f1d92a82c8d8fe43 | 11 | -20 | 0 | Done ``` diff --git a/src/cli/cli.cpp b/src/cli/cli.cpp index 9b21fb918..53d13b739 100644 --- a/src/cli/cli.cpp +++ b/src/cli/cli.cpp @@ -56,6 +56,7 @@ const struct Command Interpreter::sCommands[] = { "childtimeout", &ProcessChildTimeout }, { "contextreusedelay", &ProcessContextIdReuseDelay }, { "counter", &ProcessCounters }, + { "discover", &ProcessDiscover }, { "eidcache", &ProcessEidCache }, { "extaddr", &ProcessExtAddress }, { "extpanid", &ProcessExtPanId }, @@ -353,6 +354,28 @@ void Interpreter::ProcessCounters(int argc, char *argv[]) } } +void Interpreter::ProcessDiscover(int argc, char *argv[]) +{ + ThreadError error = kThreadError_None; + uint32_t scanChannels = 0; + long value; + + if (argc > 0) + { + SuccessOrExit(error = ParseLong(argv[0], value)); + scanChannels = 1 << value; + } + + SuccessOrExit(error = otDiscover(scanChannels, 0, OT_PANID_BROADCAST, &HandleActiveScanResult)); + sServer->OutputFormat("| J | Network Name | Extended PAN | PAN | MAC Address | Ch | dBm | LQI |\r\n"); + sServer->OutputFormat("+---+------------------+------------------+------+------------------+----+-----+-----+\r\n"); + + return; + +exit: + AppendResult(error); +} + void Interpreter::ProcessEidCache(int argc, char *argv[]) { otEidCacheEntry entry; @@ -1246,7 +1269,9 @@ void Interpreter::HandleActiveScanResult(otActiveScanResult *aResult) if (aResult->mExtPanId != NULL) { + sServer->OutputFormat("| "); OutputBytes(aResult->mExtPanId, OT_EXT_PAN_ID_SIZE); + sServer->OutputFormat(" "); } else { @@ -1254,9 +1279,8 @@ void Interpreter::HandleActiveScanResult(otActiveScanResult *aResult) } sServer->OutputFormat("| %04x | ", aResult->mPanId); - OutputBytes(aResult->mExtAddress.m8, OT_EXT_ADDRESS_SIZE); - sServer->OutputFormat("| %2d ", aResult->mChannel); + sServer->OutputFormat(" | %2d ", aResult->mChannel); sServer->OutputFormat("| %3d ", aResult->mRssi); sServer->OutputFormat("| %3d |\r\n", aResult->mLqi); diff --git a/src/cli/cli.hpp b/src/cli/cli.hpp index fd84af514..fafe10e70 100644 --- a/src/cli/cli.hpp +++ b/src/cli/cli.hpp @@ -93,6 +93,7 @@ private: static void ProcessChildTimeout(int argc, char *argv[]); static void ProcessContextIdReuseDelay(int argc, char *argv[]); static void ProcessCounters(int argc, char *argv[]); + static void ProcessDiscover(int argc, char *argv[]); static void ProcessEidCache(int argc, char *argv[]); static void ProcessExtAddress(int argc, char *argv[]); static void ProcessExtPanId(int argc, char *argv[]); diff --git a/src/core/Makefile.am b/src/core/Makefile.am index b6cff6f1f..40dde3326 100644 --- a/src/core/Makefile.am +++ b/src/core/Makefile.am @@ -100,6 +100,7 @@ noinst_HEADERS = \ thread/key_manager.hpp \ thread/link_quality.hpp \ thread/lowpan.hpp \ + thread/meshcop_tlvs.hpp \ thread/mesh_forwarder.hpp \ thread/mle.hpp \ thread/mle_constants.hpp \ diff --git a/src/core/common/message.cpp b/src/core/common/message.cpp index d524b0ce2..dcb452ad8 100644 --- a/src/core/common/message.cpp +++ b/src/core/common/message.cpp @@ -440,16 +440,6 @@ void Message::SetDatagramTag(uint16_t aTag) mInfo.mDatagramTag = aTag; } -uint8_t Message::GetTimeout(void) const -{ - return mInfo.mTimeout; -} - -void Message::SetTimeout(uint8_t aTimeout) -{ - mInfo.mTimeout = aTimeout; -} - bool Message::GetChildMask(uint8_t aChildIndex) const { assert(aChildIndex < sizeof(mInfo.mChildMask) * 8); @@ -484,6 +474,26 @@ exit: return rval; } +uint16_t Message::GetPanId(void) const +{ + return mInfo.mPanId; +} + +void Message::SetPanId(uint16_t aPanId) +{ + mInfo.mPanId = aPanId; +} + +uint8_t Message::GetTimeout(void) const +{ + return mInfo.mTimeout; +} + +void Message::SetTimeout(uint8_t aTimeout) +{ + mInfo.mTimeout = aTimeout; +} + bool Message::GetDirectTransmission(void) const { return mInfo.mDirectTx; @@ -509,6 +519,26 @@ 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; +} + 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 d728128a2..7bb7191f8 100644 --- a/src/core/common/message.hpp +++ b/src/core/common/message.hpp @@ -108,13 +108,16 @@ struct MessageInfo uint16_t mLength; ///< Number of bytes within the message. uint16_t mOffset; ///< A byte offset within the message. uint16_t mDatagramTag; ///< The datagram tag used for 6LoWPAN fragmentation. - uint8_t mTimeout; ///< Seconds remaining before dropping the message. - uint8_t mChildMask[8]; ///< A bit-vector to indicate which sleepy children need to receive this message. + 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. uint8_t mType : 2; ///< Identifies the type of message. 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. }; /** @@ -353,20 +356,6 @@ public: */ void SetDatagramTag(uint16_t aTag); - /** - * This method returns the timeout used for 6LoWPAN reassembly. - * - * @returns The time remaining in seconds. - * - */ - uint8_t GetTimeout(void) const; - - /** - * This method sets the timeout used for 6LoWPAN reassembly. - * - */ - void SetTimeout(uint8_t aTimeout); - /** * This method returns whether or not the message forwarding is scheduled for the child. * @@ -403,6 +392,38 @@ public: */ bool IsChildPending(void) const; + /** + * This method returns the IEEE 802.15.4 Destination PAN ID. + * + * @returns The IEEE 802.15.4 Destination PAN ID. + * + */ + uint16_t GetPanId(void) const; + + /** + * This method sets the IEEE 802.15.4 Destination PAN ID. + * + * @param[in] aPanId The IEEE 802.15.4 Destination PAN ID. + * + */ + void SetPanId(uint16_t aPanId); + + /** + * This method returns the timeout used for 6LoWPAN reassembly. + * + * @returns The time remaining in seconds. + * + */ + uint8_t GetTimeout(void) const; + + /** + * This method sets the timeout used for 6LoWPAN reassembly. + * + * @param[in] aTimeout The timeout value. + * + */ + void SetTimeout(uint8_t aTimeout); + /** * This method returns whether or not message forwarding is scheduled for direct transmission. * @@ -441,6 +462,40 @@ 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 is used to update a checksum value. * diff --git a/src/core/mac/mac_frame.cpp b/src/core/mac/mac_frame.cpp index 16b52711b..f893837dc 100644 --- a/src/core/mac/mac_frame.cpp +++ b/src/core/mac/mac_frame.cpp @@ -382,15 +382,16 @@ exit: ThreadError Frame::SetSrcPanId(PanId aPanId) { + ThreadError error = kThreadError_None; uint8_t *buf; - buf = FindSrcPanId(); - assert(buf != NULL); + VerifyOrExit((buf = FindSrcPanId()) != NULL, error = kThreadError_Parse); buf[0] = aPanId; buf[1] = aPanId >> 8; - return kThreadError_None; +exit: + return error; } uint8_t *Frame::FindSrcAddr(void) diff --git a/src/core/openthread.cpp b/src/core/openthread.cpp index 65d6510db..cb2020e5b 100644 --- a/src/core/openthread.cpp +++ b/src/core/openthread.cpp @@ -60,6 +60,7 @@ extern "C" { static otDEFINE_ALIGNED_VAR(sThreadNetifRaw, sizeof(ThreadNetif), uint64_t); static void HandleActiveScanResult(void *aContext, Mac::Frame *aFrame); +static void HandleMleDiscover(otActiveScanResult *aResult, void *aContext); void otProcessNextTasklet(void) { @@ -813,6 +814,19 @@ exit: return; } +ThreadError otDiscover(uint32_t aScanChannels, uint16_t aScanDuration, uint16_t aPanId, + otHandleActiveScanResult aCallback) +{ + return sThreadNetif->GetMle().Discover(aScanChannels, aScanDuration, aPanId, &HandleMleDiscover, + reinterpret_cast(aCallback)); +} + +void HandleMleDiscover(otActiveScanResult *aResult, void *aContext) +{ + otHandleActiveScanResult handler = reinterpret_cast(aContext); + handler(aResult); +} + void otSetReceiveIp6DatagramCallback(otReceiveIp6DatagramCallback aCallback) { Ip6::Ip6::SetReceiveDatagramCallback(aCallback); diff --git a/src/core/thread/mesh_forwarder.cpp b/src/core/thread/mesh_forwarder.cpp index a6eddeb79..c9a57a130 100644 --- a/src/core/thread/mesh_forwarder.cpp +++ b/src/core/thread/mesh_forwarder.cpp @@ -53,6 +53,7 @@ namespace Thread { MeshForwarder::MeshForwarder(ThreadNetif &aThreadNetif): mMacReceiver(&HandleReceivedFrame, this), mMacSender(&HandleFrameRequest, &HandleSentFrame, this), + mDiscoverTimer(&HandleDiscoverTimer, this), mPollTimer(&HandlePollTimer, this), mReassemblyTimer(&HandleReassemblyTimer, this), mScheduleTransmissionTask(ScheduleTransmissionTask, this), @@ -461,8 +462,6 @@ ThreadError MeshForwarder::UpdateIp6Route(Message &aMessage) switch (mMle.GetDeviceState()) { case Mle::kDeviceStateDisabled: - break; - case Mle::kDeviceStateDetached: if (ip6Header.GetDestination().IsLinkLocal() || ip6Header.GetDestination().IsLinkLocalMulticast()) { @@ -661,6 +660,25 @@ ThreadError MeshForwarder::HandleFrameRequest(Mac::Frame &aFrame) switch (mSendMessage->GetType()) { case Message::kTypeIp6: + if (mSendMessage->IsMleDiscoverRequest()) + { + if (!mScanning) + { + mScanChannel = kPhyMinChannel; + mRestoreChannel = mMac.GetChannel(); + mScanning = true; + } + + while ((mScanChannels & 1) == 0) + { + mScanChannels >>= 1; + mScanChannel++; + } + + mMac.SetChannel(mScanChannel); + aFrame.SetChannel(mScanChannel); + } + SendFragment(*mSendMessage, aFrame); assert(aFrame.GetLength() != 7); break; @@ -772,6 +790,7 @@ ThreadError MeshForwarder::SendFragment(Message &aMessage, Mac::Frame &aFrame) int payloadLength; int hcLength; uint16_t fragmentLength; + uint16_t dstpan; if (mAddMeshHeader) { @@ -787,7 +806,7 @@ ThreadError MeshForwarder::SendFragment(Message &aMessage, Mac::Frame &aFrame) } // initialize MAC header - fcf = Mac::Frame::kFcfFrameData | Mac::Frame::kFcfPanidCompression | Mac::Frame::kFcfFrameVersion2006; + fcf = Mac::Frame::kFcfFrameData | Mac::Frame::kFcfFrameVersion2006; fcf |= (mMacDest.mLength == 2) ? Mac::Frame::kFcfDstAddrShort : Mac::Frame::kFcfDstAddrExt; fcf |= (mMacSource.mLength == 2) ? Mac::Frame::kFcfSrcAddrShort : Mac::Frame::kFcfSrcAddrExt; @@ -802,8 +821,23 @@ ThreadError MeshForwarder::SendFragment(Message &aMessage, Mac::Frame &aFrame) fcf |= Mac::Frame::kFcfSecurityEnabled; } + if (aMessage.IsMleDiscoverRequest() || aMessage.IsMleDiscoverResponse()) + { + dstpan = aMessage.GetPanId(); + } + else + { + dstpan = mMac.GetPanId(); + } + + if (dstpan == mMac.GetPanId()) + { + fcf |= Mac::Frame::kFcfPanidCompression; + } + aFrame.InitMacHeader(fcf, Mac::Frame::kKeyIdMode1 | Mac::Frame::kSecEncMic32); - aFrame.SetDstPanId(mMac.GetPanId()); + aFrame.SetDstPanId(dstpan); + aFrame.SetSrcPanId(mMac.GetPanId()); if (mMacDest.mLength == 2) { @@ -961,6 +995,13 @@ void MeshForwarder::HandleSentFrame(Mac::Frame &aFrame) mSendMessage->ClearDirectTransmission(); mSendMessage->SetOffset(0); } + + if (mSendMessage->IsMleDiscoverRequest()) + { + mSendBusy = true; + mDiscoverTimer.Start(mScanDuration); + ExitNow(); + } } if (mSendMessage->GetDirectTransmission() == false && mSendMessage->IsChildPending() == false) @@ -975,6 +1016,44 @@ exit: {} } +void MeshForwarder::SetDiscoverParameters(uint32_t aScanChannels, uint16_t aScanDuration) +{ + mScanChannels = (aScanChannels == 0) ? static_cast(Mac::kScanChannelsAll) : aScanChannels; + mScanDuration = (aScanDuration == 0) ? static_cast(Mac::kScanDurationDefault) : aScanDuration; +} + +void MeshForwarder::HandleDiscoverTimer(void *aContext) +{ + MeshForwarder *obj = static_cast(aContext); + obj->HandleDiscoverTimer(); +} + +void MeshForwarder::HandleDiscoverTimer(void) +{ + do + { + mScanChannels >>= 1; + mScanChannel++; + + if (mScanChannel > kPhyMaxChannel) + { + mSendQueue.Dequeue(*mSendMessage); + Message::Free(*mSendMessage); + mMac.SetChannel(mRestoreChannel); + mScanning = false; + mMle.HandleDiscoverComplete(); + ExitNow(); + } + } + while ((mScanChannels & 1) == 0); + + mSendMessage->SetDirectTransmission(); + +exit: + mSendBusy = false; + mScheduleTransmissionTask.Post(); +} + void MeshForwarder::HandleReceivedFrame(void *aContext, Mac::Frame &aFrame, ThreadError aError) { MeshForwarder *obj = reinterpret_cast(aContext); @@ -1028,6 +1107,8 @@ void MeshForwarder::HandleReceivedFrame(Mac::Frame &aFrame, ThreadError aError) } SuccessOrExit(aFrame.GetDstAddr(macDest)); + aFrame.GetSrcPanId(messageInfo.mPanId); + messageInfo.mChannel = aFrame.GetChannel(); messageInfo.mRss = aFrame.GetPower(); messageInfo.mLqi = aFrame.GetLqi(); messageInfo.mLinkSecurity = aFrame.GetSecurityEnabled(); @@ -1117,6 +1198,7 @@ void MeshForwarder::HandleMesh(uint8_t *aFrame, uint8_t aFrameLength, const Thre SuccessOrExit(error = message->SetLength(aFrameLength)); message->Write(0, aFrameLength, aFrame); message->SetLinkSecurityEnabled(aMessageInfo.mLinkSecurity); + message->SetPanId(aMessageInfo.mPanId); SendMessage(*message); } @@ -1175,6 +1257,7 @@ void MeshForwarder::HandleFragment(uint8_t *aFrame, uint8_t aFrameLength, VerifyOrExit((message = Message::New(Message::kTypeIp6, 0)) != NULL, error = kThreadError_NoBufs); message->SetLinkSecurityEnabled(aMessageInfo.mLinkSecurity); + message->SetPanId(aMessageInfo.mPanId); headerLength = mLowpan.Decompress(*message, aMacSource, aMacDest, aFrame, aFrameLength, datagramLength); VerifyOrExit(headerLength > 0, error = kThreadError_NoBufs); @@ -1279,6 +1362,7 @@ void MeshForwarder::HandleLowpanHC(uint8_t *aFrame, uint8_t aFrameLength, VerifyOrExit((message = Message::New(Message::kTypeIp6, 0)) != NULL, ;); message->SetLinkSecurityEnabled(aMessageInfo.mLinkSecurity); + message->SetPanId(aMessageInfo.mPanId); headerLength = mLowpan.Decompress(*message, aMacSource, aMacDest, aFrame, aFrameLength, 0); VerifyOrExit(headerLength > 0, ;); diff --git a/src/core/thread/mesh_forwarder.hpp b/src/core/thread/mesh_forwarder.hpp index 19a9516aa..1c0e8dac0 100644 --- a/src/core/thread/mesh_forwarder.hpp +++ b/src/core/thread/mesh_forwarder.hpp @@ -140,6 +140,15 @@ public: */ void SetPollPeriod(uint32_t aPeriod); + /** + * This method sets the scan parameters for MLE Discovery Request messages. + * + * @param[in] aScanChannels A bit vector indicating which channels to scan. + * @param[in] aScanDuration The time in milliseconds to spend scanning each channel. + * + */ + void SetDiscoverParameters(uint32_t aScanChannels, uint16_t aScanDuration); + private: enum { @@ -178,6 +187,8 @@ private: static void HandleSentFrame(void *aContext, Mac::Frame &aFrame); void HandleSentFrame(Mac::Frame &aFrame); + static void HandleDiscoverTimer(void *aContext); + void HandleDiscoverTimer(void); static void HandleReassemblyTimer(void *aContext); void HandleReassemblyTimer(void); static void HandlePollTimer(void *aContext); @@ -188,6 +199,7 @@ private: Mac::Receiver mMacReceiver; Mac::Sender mMacSender; + Timer mDiscoverTimer; Timer mPollTimer; Timer mReassemblyTimer; @@ -210,6 +222,12 @@ private: Tasklet mScheduleTransmissionTask; bool mEnabled; + uint32_t mScanChannels; + uint16_t mScanDuration; + uint8_t mScanChannel; + uint8_t mRestoreChannel; + bool mScanning; + ThreadNetif &mNetif; AddressResolver &mAddressResolver; Lowpan::Lowpan &mLowpan; diff --git a/src/core/thread/meshcop_tlvs.hpp b/src/core/thread/meshcop_tlvs.hpp new file mode 100644 index 000000000..c77852f5e --- /dev/null +++ b/src/core/thread/meshcop_tlvs.hpp @@ -0,0 +1,367 @@ +/* + * Copyright (c) 2016, Nest Labs, Inc. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the copyright holder nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +/** + * @file + * This file includes definitions for generating and processing MeshCoP TLVs. + * + */ + +#ifndef MESHCOP_TLVS_HPP_ +#define MESHCOP_TLVS_HPP_ + +#include + +#include +#include + +using Thread::Encoding::BigEndian::HostSwap16; + +namespace Thread { +namespace MeshCoP { + +/** + * This class implements MeshCoP TLV generation and parsing. + * + */ +OT_TOOL_PACKED_BEGIN +class Tlv +{ +public: + /** + * MeshCoP TLV Types. + * + */ + enum Type + { + kExtendedPanId = 2, ///< Extended PAN ID TLV + kNetworkName = 3, ///< Newtork Name TLV + kDiscoveryRequest = 128, ///< Discovery Request TLV + kDiscoveryResponse = 129, ///< Discovery Response TLV + }; + + /** + * This method returns the Type value. + * + * @returns The Type value. + * + */ + Type GetType(void) const { return static_cast(mType); } + + /** + * This method sets the Type value. + * + * @param[in] aType The Type value. + * + */ + void SetType(Type aType) { mType = static_cast(aType); } + + /** + * This method returns the Length value. + * + */ + uint8_t GetLength(void) const { return mLength; } + + /** + * This method sets the Length value. + * + * @param[in] aLength The Length value. + * + */ + void SetLength(uint8_t aLength) { mLength = aLength; } + + /** + * This method returns a pointer to the Value. + * + * @returns A pointer to the value. + * + */ + uint8_t *GetValue() { return reinterpret_cast(this) + sizeof(Tlv); } + + /** + * This method returns a pointer to the next TLV. + * + * @returns A pointer to the next TLV. + * + */ + Tlv *GetNext() { + return reinterpret_cast(reinterpret_cast(this) + sizeof(*this) + mLength); + } + +private: + uint8_t mType; + uint8_t mLength; +} OT_TOOL_PACKED_END; + +/** + * This class implements Extended PAN ID TLV generation and parsing. + * + */ +OT_TOOL_PACKED_BEGIN +class ExtendedPanIdTlv: public Tlv +{ +public: + /** + * This method initializes the TLV. + * + */ + void Init(void) { SetType(kExtendedPanId); 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 Extended PAN ID value. + * + * @returns The Extended PAN ID value. + * + */ + const uint8_t *GetExtendedPanId(void) const { return mExtendedPanId; } + + /** + * This method sets the Extended PAN ID value. + * + * @param[in] aExtendedPanId A pointer to the Extended PAN ID value. + * + */ + void SetExtendedPanId(const uint8_t *aExtendedPanId) { + memcpy(mExtendedPanId, aExtendedPanId, OT_EXT_PAN_ID_SIZE); + } + +private: + uint8_t mExtendedPanId[OT_EXT_PAN_ID_SIZE]; +} OT_TOOL_PACKED_END; + +/** + * This class implements Network Name TLV generation and parsing. + * + */ +OT_TOOL_PACKED_BEGIN +class NetworkNameTlv: public Tlv +{ +public: + /** + * This method initializes the TLV. + * + */ + void Init(void) { SetType(kNetworkName); SetLength(sizeof(*this) - sizeof(Tlv)); } + + /** + * This method indicates whether or not the TLV appears to be well-formed. + * + * @retval TRUE If the TLV appears to be well-formed. + * @retval FALSE If the TLV does not appear to be well-formed. + * + */ + bool IsValid(void) const { return GetLength() <= sizeof(*this) - sizeof(Tlv); } + + /** + * This method returns the Network Name value. + * + * @returns The Network Name value. + * + */ + const char *GetNetworkName(void) const { return mNetworkName; } + + /** + * This method sets the Network Name value. + * + * @param[in] aNetworkName A pointer to the Network Name value. + * + */ + void SetNetworkName(const char *aNetworkName) { + int length = strnlen(aNetworkName, sizeof(mNetworkName)); + memcpy(mNetworkName, aNetworkName, length); + SetLength(length); + } + +private: + char mNetworkName[OT_NETWORK_NAME_SIZE]; +} OT_TOOL_PACKED_END; + +/** + * This class implements Discovery Request TLV generation and parsing. + * + */ +OT_TOOL_PACKED_BEGIN +class DiscoveryRequestTlv: public Tlv +{ +public: + /** + * This method initializes the TLV. + * + */ + void Init(void) { SetType(kDiscoveryRequest); SetLength(sizeof(*this) - sizeof(Tlv)); mReserved = 0; } + + /** + * 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 Version value. + * + * @returns The Version value. + * + */ + uint8_t GetVersion(void) const { return mFlags >> kVersionOffset; } + + /** + * This method sets the Version value. + * + * @param[in] aVersion The Version value. + * + */ + void SetVersion(uint8_t aVersion) { mFlags = (mFlags & ~kVersionMask) | (aVersion << kVersionOffset); } + + /** + * This method indicates whether or not the Joiner flag is set. + * + * @retval TRUE If the Joiner flag is set. + * @retval FALSE If the Joiner flag is not set. + * + */ + bool IsJoiner(void) { return mFlags & kJoinerMask; } + + /** + * This method sets the Joiner flag. + * + * @param[in] aJoiner TRUE if set, FALSE otherwise. + * + */ + void SetJoiner(bool aJoiner) { + if (aJoiner) { + mFlags |= kJoinerMask; + } + else { + mFlags &= ~kJoinerMask; + } + } + +private: + enum + { + kVersionOffset = 4, + kVersionMask = 0xf << kVersionOffset, + kJoinerOffset = 3, + kJoinerMask = 1 << kJoinerOffset, + }; + uint8_t mFlags; + uint8_t mReserved; +} OT_TOOL_PACKED_END; + +/** + * This class implements Discovery Response TLV generation and parsing. + * + */ +OT_TOOL_PACKED_BEGIN +class DiscoveryResponseTlv: public Tlv +{ +public: + /** + * This method initializes the TLV. + * + */ + void Init(void) { SetType(kDiscoveryResponse); SetLength(sizeof(*this) - sizeof(Tlv)); mReserved = 0; } + + /** + * 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 Version value. + * + * @returns The Version value. + * + */ + uint8_t GetVersion(void) const { return mFlags >> kVersionOffset; } + + /** + * This method sets the Version value. + * + * @param[in] aVersion The Version value. + * + */ + void SetVersion(uint8_t aVersion) { mFlags = (mFlags & ~kVersionMask) | (aVersion << kVersionOffset); } + + /** + * This method indicates whether or not the Native Commissioner flag is set. + * + * @retval TRUE If the Native Commissioner flag is set. + * @retval FALSE If the Native Commissioner flag is not set. + * + */ + bool IsNativeCommissioner(void) { return mFlags & kNativeMask; } + + /** + * This method sets the Native Commissioner flag. + * + * @param[in] aNativeCommissioner TRUE if set, FALSE otherwise. + * + */ + void SetNativeCommissioner(bool aNativeCommissioner) { + if (aNativeCommissioner) { + mFlags |= kNativeMask; + } + else { + mFlags &= ~kNativeMask; + } + } + +private: + enum + { + kVersionOffset = 4, + kVersionMask = 0xf << kVersionOffset, + kNativeOffset = 3, + kNativeMask = 1 << kNativeOffset, + }; + uint8_t mFlags; + uint8_t mReserved; +} OT_TOOL_PACKED_END; + +} // namespace MeshCoP + +} // namespace Thread + +#endif // MESHCOP_TLVS_HPP_ diff --git a/src/core/thread/mle.cpp b/src/core/thread/mle.cpp index a159f5476..fc43c0927 100644 --- a/src/core/thread/mle.cpp +++ b/src/core/thread/mle.cpp @@ -43,6 +43,7 @@ #include #include #include +#include #include #include #include @@ -144,20 +145,39 @@ Mle::Mle(ThreadNetif &aThreadNetif) : mNetif.RegisterCallback(mNetifCallback); } -ThreadError Mle::Start(void) +ThreadError Mle::Enable(void) { ThreadError error = kThreadError_None; Ip6::SockAddr sockaddr; - // cannot bring up the interface if IEEE 802.15.4 promiscuous mode is enabled - VerifyOrExit(otPlatRadioGetPromiscuous() == false, error = kThreadError_Busy); - VerifyOrExit(mNetif.IsUp(), error = kThreadError_InvalidState); - // memcpy(&sockaddr.mAddr, &mLinkLocal64.GetAddress(), sizeof(sockaddr.mAddr)); sockaddr.mPort = kUdpPort; SuccessOrExit(error = mSocket.Open(&HandleUdpReceive, this)); SuccessOrExit(error = mSocket.Bind(sockaddr)); +exit: + return error; +} + +ThreadError Mle::Disable(void) +{ + ThreadError error = kThreadError_None; + + SuccessOrExit(error = Stop()); + SuccessOrExit(error = mSocket.Close()); + +exit: + return error; +} + +ThreadError Mle::Start(void) +{ + ThreadError error = kThreadError_None; + + // cannot bring up the interface if IEEE 802.15.4 promiscuous mode is enabled + VerifyOrExit(otPlatRadioGetPromiscuous() == false, error = kThreadError_Busy); + VerifyOrExit(mNetif.IsUp(), error = kThreadError_InvalidState); + mDeviceState = kDeviceStateDetached; SetStateDetached(); @@ -183,13 +203,68 @@ exit: ThreadError Mle::Stop(void) { SetStateDetached(); - mSocket.Close(); mNetif.RemoveUnicastAddress(mLinkLocal16); mNetif.RemoveUnicastAddress(mMeshLocal16); mDeviceState = kDeviceStateDisabled; return kThreadError_None; } +ThreadError Mle::Discover(uint32_t aScanChannels, uint16_t aScanDuration, uint16_t aPanId, + DiscoverHandler aCallback, void *aContext) +{ + ThreadError error = kThreadError_None; + Message *message; + Ip6::Address destination; + Tlv tlv; + MeshCoP::DiscoveryRequestTlv discoveryRequest; + uint16_t startOffset; + + mDiscoverHandler = aCallback; + mDiscoverContext = aContext; + mMesh.SetDiscoverParameters(aScanChannels, aScanDuration); + + VerifyOrExit((message = Ip6::Udp::NewMessage(0)) != NULL, ;); + message->SetLinkSecurityEnabled(false); + message->SetMleDiscoverRequest(true); + message->SetPanId(aPanId); + SuccessOrExit(error = AppendHeader(*message, Header::kCommandDiscoveryRequest)); + + // Discovery TLV + tlv.SetType(Tlv::kDiscovery); + SuccessOrExit(error = message->Append(&tlv, sizeof(tlv))); + + startOffset = message->GetLength(); + + // Discovery Request TLV + discoveryRequest.Init(); + discoveryRequest.SetVersion(kVersion); + SuccessOrExit(error = message->Append(&discoveryRequest, sizeof(discoveryRequest))); + + tlv.SetLength(message->GetLength() - startOffset); + message->Write(startOffset - sizeof(tlv), sizeof(tlv), &tlv); + + memset(&destination, 0, sizeof(destination)); + destination.mFields.m16[0] = HostSwap16(0xff02); + destination.mFields.m16[7] = HostSwap16(0x0002); + SuccessOrExit(error = SendMessage(*message, destination)); + + otLogInfoMle("Sent discovery request\n"); + +exit: + + if (error != kThreadError_None && message != NULL) + { + Message::Free(*message); + } + + return error; +} + +void Mle::HandleDiscoverComplete(void) +{ + mDiscoverHandler(NULL, mDiscoverContext); +} + ThreadError Mle::BecomeDetached(void) { ThreadError error = kThreadError_None; @@ -514,17 +589,24 @@ ThreadError Mle::AppendHeader(Message &aMessage, Header::Command aCommand) header.Init(); - if (aCommand == Header::kCommandAdvertisement || - aCommand == Header::kCommandChildIdRequest || - aCommand == Header::kCommandLinkReject || - aCommand == Header::kCommandParentRequest || - aCommand == Header::kCommandParentResponse) + switch (aCommand) { + case Header::kCommandDiscoveryRequest: + case Header::kCommandDiscoveryResponse: + header.SetSecuritySuite(255); + break; + + case Header::kCommandAdvertisement: + case Header::kCommandChildIdRequest: + case Header::kCommandLinkReject: + case Header::kCommandParentRequest: + case Header::kCommandParentResponse: header.SetKeyIdMode2(); - } - else - { + break; + + default: header.SetKeyIdMode1(); + break; } header.SetCommand(aCommand); @@ -1149,36 +1231,42 @@ ThreadError Mle::SendMessage(Message &aMessage, const Ip6::Address &aDestination Ip6::MessageInfo messageInfo; aMessage.Read(0, sizeof(header), &header); - header.SetFrameCounter(mKeyManager.GetMleFrameCounter()); - keySequence = mKeyManager.GetCurrentKeySequence(); - header.SetKeyId(keySequence); - - aMessage.Write(0, header.GetLength(), &header); - - GenerateNonce(*mMac.GetExtAddress(), mKeyManager.GetMleFrameCounter(), Mac::Frame::kSecEncMic32, nonce); - - aesCcm.SetKey(mKeyManager.GetCurrentMleKey(), 16); - aesCcm.Init(16 + 16 + header.GetHeaderLength(), aMessage.GetLength() - (header.GetLength() - 1), - sizeof(tag), nonce, sizeof(nonce)); - - aesCcm.Header(&mLinkLocal64.GetAddress(), sizeof(mLinkLocal64.GetAddress())); - aesCcm.Header(&aDestination, sizeof(aDestination)); - aesCcm.Header(header.GetBytes() + 1, header.GetHeaderLength()); - - aMessage.SetOffset(header.GetLength() - 1); - - while (aMessage.GetOffset() < aMessage.GetLength()) + if (header.GetSecuritySuite() == 0) { - length = aMessage.Read(aMessage.GetOffset(), sizeof(buf), buf); - aesCcm.Payload(buf, buf, length, true); - aMessage.Write(aMessage.GetOffset(), length, buf); - aMessage.MoveOffset(length); - } + header.SetFrameCounter(mKeyManager.GetMleFrameCounter()); - tagLength = sizeof(tag); - aesCcm.Finalize(tag, &tagLength); - SuccessOrExit(aMessage.Append(tag, tagLength)); + keySequence = mKeyManager.GetCurrentKeySequence(); + header.SetKeyId(keySequence); + + aMessage.Write(0, header.GetLength(), &header); + + GenerateNonce(*mMac.GetExtAddress(), mKeyManager.GetMleFrameCounter(), Mac::Frame::kSecEncMic32, nonce); + + aesCcm.SetKey(mKeyManager.GetCurrentMleKey(), 16); + aesCcm.Init(16 + 16 + header.GetHeaderLength(), aMessage.GetLength() - (header.GetLength() - 1), + sizeof(tag), nonce, sizeof(nonce)); + + aesCcm.Header(&mLinkLocal64.GetAddress(), sizeof(mLinkLocal64.GetAddress())); + aesCcm.Header(&aDestination, sizeof(aDestination)); + aesCcm.Header(header.GetBytes() + 1, header.GetHeaderLength()); + + aMessage.SetOffset(header.GetLength() - 1); + + while (aMessage.GetOffset() < aMessage.GetLength()) + { + length = aMessage.Read(aMessage.GetOffset(), sizeof(buf), buf); + aesCcm.Payload(buf, buf, length, true); + aMessage.Write(aMessage.GetOffset(), length, buf); + aMessage.MoveOffset(length); + } + + tagLength = sizeof(tag); + aesCcm.Finalize(tag, &tagLength); + SuccessOrExit(aMessage.Append(tag, tagLength)); + + mKeyManager.IncrementMleFrameCounter(); + } memset(&messageInfo, 0, sizeof(messageInfo)); memcpy(&messageInfo.GetPeerAddr(), &aDestination, sizeof(messageInfo.GetPeerAddr())); @@ -1187,8 +1275,6 @@ ThreadError Mle::SendMessage(Message &aMessage, const Ip6::Address &aDestination messageInfo.mInterfaceId = mNetif.GetInterfaceId(); messageInfo.mHopLimit = 255; - mKeyManager.IncrementMleFrameCounter(); - SuccessOrExit(error = mSocket.SendTo(aMessage, messageInfo)); exit: @@ -1224,6 +1310,29 @@ void Mle::HandleUdpReceive(Message &aMessage, const Ip6::MessageInfo &aMessageIn aMessage.Read(aMessage.GetOffset(), sizeof(header), &header); VerifyOrExit(header.IsValid(),); + if (header.GetSecuritySuite() == 255) + { + aMessage.MoveOffset(header.GetLength()); + + switch (header.GetCommand()) + { + case Header::kCommandDiscoveryRequest: + HandleDiscoveryRequest(aMessage, aMessageInfo); + break; + + case Header::kCommandDiscoveryResponse: + HandleDiscoveryResponse(aMessage, aMessageInfo); + break; + + default: + break; + } + + ExitNow(); + } + + VerifyOrExit(mDeviceState != kDeviceStateDisabled && header.GetSecuritySuite() == 0, ;); + if (header.IsKeyIdMode1()) { keyid = header.GetKeyId(); @@ -1879,6 +1988,213 @@ exit: return error; } +ThreadError Mle::HandleDiscoveryRequest(const Message &aMessage, const Ip6::MessageInfo &aMessageInfo) +{ + ThreadError error = kThreadError_None; + Tlv tlv; + MeshCoP::Tlv meshcopTlv; + MeshCoP::DiscoveryRequestTlv discoveryRequest; + MeshCoP::ExtendedPanIdTlv extPanId; + uint16_t offset; + uint16_t end; + + otLogInfoMle("Received discovery request\n"); + + // only Routers and REEDs respond + VerifyOrExit((mDeviceMode & ModeTlv::kModeFFD) != 0, ;); + + offset = aMessage.GetOffset(); + end = aMessage.GetLength(); + + // find MLE Discovery TLV + while (offset < end) + { + aMessage.Read(offset, sizeof(tlv), &tlv); + + if (tlv.GetType() == Tlv::kDiscovery) + { + break; + } + + offset += sizeof(tlv) + tlv.GetLength(); + } + + VerifyOrExit(offset < end, error = kThreadError_Parse); + + offset += sizeof(tlv); + end = offset + sizeof(tlv) + tlv.GetLength(); + + while (offset < end) + { + aMessage.Read(offset, sizeof(meshcopTlv), &meshcopTlv); + + switch (meshcopTlv.GetType()) + { + case MeshCoP::Tlv::kDiscoveryRequest: + aMessage.Read(offset, sizeof(discoveryRequest), &discoveryRequest); + VerifyOrExit(discoveryRequest.IsValid(), error = kThreadError_Parse); + break; + + case MeshCoP::Tlv::kExtendedPanId: + aMessage.Read(offset, sizeof(extPanId), &extPanId); + VerifyOrExit(extPanId.IsValid(), error = kThreadError_Parse); + VerifyOrExit(memcmp(mMac.GetExtendedPanId(), extPanId.GetExtendedPanId(), OT_EXT_PAN_ID_SIZE), + error = kThreadError_Drop); + break; + + default: + break; + } + + offset += sizeof(meshcopTlv) + meshcopTlv.GetLength(); + } + + error = SendDiscoveryResponse(aMessageInfo.GetPeerAddr(), aMessage.GetPanId()); + +exit: + return error; +} + +ThreadError Mle::SendDiscoveryResponse(const Ip6::Address &aDestination, uint16_t aPanId) +{ + ThreadError error = kThreadError_None; + Message *message; + uint16_t startOffset; + Tlv tlv; + MeshCoP::DiscoveryResponseTlv discoveryResponse; + MeshCoP::ExtendedPanIdTlv extPanId; + MeshCoP::NetworkNameTlv networkName; + + VerifyOrExit((message = Ip6::Udp::NewMessage(0)) != NULL, ;); + message->SetLinkSecurityEnabled(false); + message->SetMleDiscoverResponse(true); + message->SetPanId(aPanId); + SuccessOrExit(error = AppendHeader(*message, Header::kCommandDiscoveryResponse)); + + // Discovery TLV + tlv.SetType(Tlv::kDiscovery); + SuccessOrExit(error = message->Append(&tlv, sizeof(tlv))); + + startOffset = message->GetLength(); + + // Discovery Response TLV + discoveryResponse.Init(); + discoveryResponse.SetVersion(kVersion); + SuccessOrExit(error = message->Append(&discoveryResponse, sizeof(discoveryResponse))); + + // Extended PAN ID TLV + extPanId.Init(); + extPanId.SetExtendedPanId(mMac.GetExtendedPanId()); + SuccessOrExit(error = message->Append(&extPanId, sizeof(extPanId))); + + // Network Name TLV + networkName.Init(); + networkName.SetNetworkName(mMac.GetNetworkName()); + SuccessOrExit(error = message->Append(&networkName, sizeof(tlv) + networkName.GetLength())); + + tlv.SetLength(message->GetLength() - startOffset); + message->Write(startOffset - sizeof(tlv), sizeof(tlv), &tlv); + + SuccessOrExit(error = SendMessage(*message, aDestination)); + + otLogInfoMle("Sent discovery response\n"); + +exit: + + if (error != kThreadError_None && message != NULL) + { + Message::Free(*message); + } + + return error; +} + +ThreadError Mle::HandleDiscoveryResponse(const Message &aMessage, const Ip6::MessageInfo &aMessageInfo) +{ + ThreadError error = kThreadError_None; + const ThreadMessageInfo *threadMessageInfo = reinterpret_cast(aMessageInfo.mLinkInfo); + Tlv tlv; + MeshCoP::Tlv meshcopTlv; + MeshCoP::DiscoveryResponseTlv discoveryResponse; + MeshCoP::ExtendedPanIdTlv extPanId; + MeshCoP::NetworkNameTlv networkName; + otActiveScanResult result; + uint16_t offset; + uint16_t end; + char networkNameBuf[OT_NETWORK_NAME_SIZE]; + + otLogInfoMle("Handle discovery response\n"); + + offset = aMessage.GetOffset(); + end = aMessage.GetLength(); + + // find MLE Discovery TLV + while (offset < end) + { + aMessage.Read(offset, sizeof(tlv), &tlv); + + if (tlv.GetType() == Tlv::kDiscovery) + { + break; + } + + offset += sizeof(tlv) + tlv.GetLength(); + } + + VerifyOrExit(offset < end, error = kThreadError_Parse); + + offset += sizeof(tlv); + end = offset + sizeof(tlv) + tlv.GetLength(); + + memset(&result, 0, sizeof(result)); + result.mPanId = threadMessageInfo->mPanId; + result.mChannel = threadMessageInfo->mChannel; + result.mRssi = threadMessageInfo->mRss; + result.mLqi = threadMessageInfo->mLqi; + static_cast(&result.mExtAddress)->Set(aMessageInfo.GetPeerAddr()); + + // process MeshCoP TLVs + while (offset < end) + { + aMessage.Read(offset, sizeof(meshcopTlv), &meshcopTlv); + + switch (meshcopTlv.GetType()) + { + case MeshCoP::Tlv::kDiscoveryResponse: + aMessage.Read(offset, sizeof(discoveryResponse), &discoveryResponse); + VerifyOrExit(discoveryResponse.IsValid(), error = kThreadError_Parse); + result.mVersion = discoveryResponse.GetVersion(); + result.mIsNative = discoveryResponse.IsNativeCommissioner(); + break; + + case MeshCoP::Tlv::kExtendedPanId: + aMessage.Read(offset, sizeof(extPanId), &extPanId); + VerifyOrExit(extPanId.IsValid(), error = kThreadError_Parse); + result.mExtPanId = extPanId.GetExtendedPanId(); + break; + + case MeshCoP::Tlv::kNetworkName: + aMessage.Read(offset, sizeof(networkName), &networkName); + VerifyOrExit(networkName.IsValid(), error = kThreadError_Parse); + memcpy(networkNameBuf, networkName.GetNetworkName(), networkName.GetLength()); + memset(networkNameBuf + networkName.GetLength(), 0, sizeof(networkNameBuf) - networkName.GetLength()); + result.mNetworkName = networkNameBuf; + break; + + default: + break; + } + + offset += sizeof(meshcopTlv) + meshcopTlv.GetLength(); + } + + // signal callback + mDiscoverHandler(&result, mDiscoverContext); + +exit: + return error; +} + Neighbor *Mle::GetNeighbor(uint16_t aAddress) { return (mParent.mState == Neighbor::kStateValid && mParent.mValid.mRloc16 == aAddress) ? &mParent : NULL; diff --git a/src/core/thread/mle.hpp b/src/core/thread/mle.hpp index e41035769..49799c99e 100644 --- a/src/core/thread/mle.hpp +++ b/src/core/thread/mle.hpp @@ -124,9 +124,10 @@ public: * */ bool IsValid(void) const { - return mSecuritySuite == 0 && - (mSecurityControl == (Mac::Frame::kKeyIdMode1 | Mac::Frame::kSecEncMic32) || - mSecurityControl == (Mac::Frame::kKeyIdMode2 | Mac::Frame::kSecEncMic32)); + return (mSecuritySuite == 255) || + (mSecuritySuite == 0 && + (mSecurityControl == (Mac::Frame::kKeyIdMode1 | Mac::Frame::kSecEncMic32) || + mSecurityControl == (Mac::Frame::kKeyIdMode2 | Mac::Frame::kSecEncMic32))); } /** @@ -136,10 +137,31 @@ public: * */ uint8_t GetLength(void) const { - return sizeof(mSecuritySuite) + sizeof(mSecurityControl) + sizeof(mFrameCounter) + - (IsKeyIdMode1() ? 1 : 5) + sizeof(mCommand); + uint8_t rval = sizeof(mSecuritySuite) + sizeof(mCommand); + + if (mSecuritySuite == 0) { + rval += sizeof(mSecurityControl) + sizeof(mFrameCounter) + (IsKeyIdMode1() ? 1 : 5); + } + + return rval; } + /** + * This method returns the Security Suite value. + * + * @returns The Security Suite value. + * + */ + uint8_t GetSecuritySuite(void) const { return mSecuritySuite; } + + /** + * This method sets the Security Suite value. + * + * @param[in] aSecuritySuite The Security Suite value. + * + */ + void SetSecuritySuite(uint8_t aSecuritySuite) { mSecuritySuite = aSecuritySuite; } + /** * This method returns the MLE header length (excluding the Command Type). * @@ -269,6 +291,8 @@ public: kCommandChildIdResponse = 12, ///< Child ID Response kCommandChildUpdateRequest = 13, ///< Child Update Request kCommandChildUpdateResponse = 14, ///< Child Update Response + kCommandDiscoveryRequest = 16, ///< Discovery Request + kCommandDiscoveryResponse = 17, ///< Discovery Response }; /** @@ -278,8 +302,13 @@ public: * */ Command GetCommand(void) const { - const uint8_t *command = IsKeyIdMode1() ? mKeyIdentifier + 1 : &mCommand; - return static_cast(*command); + if (mSecuritySuite == 255) { + return static_cast(mSecurityControl); + } + else { + const uint8_t *command = IsKeyIdMode1() ? mKeyIdentifier + 1 : &mCommand; + return static_cast(*command); + } } /** @@ -289,8 +318,13 @@ public: * */ void SetCommand(Command aCommand) { - uint8_t *commandField = IsKeyIdMode1() ? mKeyIdentifier + 1 : &mCommand; - *commandField = static_cast(aCommand); + if (mSecuritySuite == 255) { + mSecurityControl = static_cast(aCommand); + } + else { + uint8_t *commandField = IsKeyIdMode1() ? mKeyIdentifier + 1 : &mCommand; + *commandField = static_cast(aCommand); + } } /** @@ -325,6 +359,24 @@ public: */ explicit Mle(ThreadNetif &aThreadNetif); + /** + * This method enables MLE. + * + * @retval kThreadError_None Successfully enabled MLE. + * @retval kThreadError_Busy MLE was already enabled. + * + */ + ThreadError Enable(void); + + /** + * This method disables MLE. + * + * @retval kThreadError_None Successfully disabled MLE. + * @retval kThreadError_Busy MLE was already disabled. + * + */ + ThreadError Disable(void); + /** * This method starts the MLE protocol operation. * @@ -343,6 +395,37 @@ public: */ ThreadError Stop(void); + /** + * This function pointer is called on receiving an MLE Discovery Response message. + * + * @param[in] aResult A valid pointer to the Discovery Response information or NULL when the Discovery completes. + * @param[in] aContext A pointer to application-specific context. + * + */ + typedef void (*DiscoverHandler)(otActiveScanResult *aResult, void *aContext); + + /** + * This method initiates a Thread Discovery. + * + * @param[in] aScanChannels A bit vector indicating which channels to scan. + * @param[in] aScanDuration The time in milliseconds to spend scanning each channel. + * @param[in] aPanId The PAN ID filter (set to Broadcast PAN to disable filter). + * @param[in] aHandler A pointer to a function that is called on receiving an MLE Discovery Response. + * @param[in] aContext A pointer to arbitrary context information. + * + * @retval kThreadError_None Successfully started a Thread Discovery. + * @retval kThreadError_Busy Thread Discovery is already in progress. + * + */ + ThreadError Discover(uint32_t aScanChannels, uint16_t aScanDuration, uint16_t aPanId, + DiscoverHandler aCallback, void *aContext); + + /** + * This method is called by the MeshForwarder to indicate that discovery is complete. + * + */ + void HandleDiscoverComplete(void); + /** * This method causes the Thread interface to detach from the Thread network. * @@ -774,6 +857,17 @@ protected: */ ThreadError AppendAddressRegistration(Message &aMessage); + /** + * This method appends a Thread Discovery TLV to a message. + * + * @param[in] aMessage A reference to the message. + * + * @retval kThreadError_None Successfully appended the Thread Discovery TLV. + * @retval kThreadError_NoBufs Insufficient buffers available to append the Address Registration TLV. + * + */ + ThreadError AppendDiscovery(Message &aMessage); + /** * This method checks if the destination is reachable. * @@ -975,9 +1069,12 @@ private: ThreadError HandleDataResponse(const Message &aMessage, const Ip6::MessageInfo &aMessageInfo); ThreadError HandleParentResponse(const Message &aMessage, const Ip6::MessageInfo &aMessageInfo, uint32_t aKeySequence); + ThreadError HandleDiscoveryRequest(const Message &aMessage, const Ip6::MessageInfo &aMessageInfo); + ThreadError HandleDiscoveryResponse(const Message &aMessage, const Ip6::MessageInfo &aMessageInfo); ThreadError SendParentRequest(void); ThreadError SendChildIdRequest(void); + ThreadError SendDiscoveryResponse(const Ip6::Address &aDestination, uint16_t aPanId); bool IsBetterParent(uint16_t aRloc16, uint8_t aLinkQuality, ConnectivityTlv &aConnectivityTlv) const; @@ -1004,6 +1101,9 @@ private: Ip6::UdpSocket mSocket; uint32_t mTimeout; + DiscoverHandler mDiscoverHandler; + void *mDiscoverContext; + 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 6cc5d162c..0e5d03889 100644 --- a/src/core/thread/mle_tlvs.hpp +++ b/src/core/thread/mle_tlvs.hpp @@ -93,6 +93,7 @@ public: kStatus = 17, ///< Status TLV kVersion = 18, ///< Version TLV kAddressRegistration = 19, ///< Address Registration TLV + kDiscovery = 26, ///< Thread Discovery TLV kInvalid = 255, }; diff --git a/src/core/thread/thread_netif.cpp b/src/core/thread/thread_netif.cpp index 65731659e..73e9c183c 100644 --- a/src/core/thread/thread_netif.cpp +++ b/src/core/thread/thread_netif.cpp @@ -81,6 +81,7 @@ ThreadError ThreadNetif::Up(void) Netif::AddNetif(); mMeshForwarder.Start(); mCoapServer.Start(); + mMleRouter.Enable(); mIsUp = true; exit: @@ -90,7 +91,7 @@ exit: ThreadError ThreadNetif::Down(void) { mCoapServer.Stop(); - mMleRouter.Stop(); + mMleRouter.Disable(); mMeshForwarder.Stop(); Netif::RemoveNetif(); mIsUp = false; diff --git a/src/core/thread/thread_netif.hpp b/src/core/thread/thread_netif.hpp index 34ecb33dc..493fe2409 100644 --- a/src/core/thread/thread_netif.hpp +++ b/src/core/thread/thread_netif.hpp @@ -225,9 +225,11 @@ private: */ struct ThreadMessageInfo { - int8_t mRss; ///< The Received Signal Strength in dBm. - uint8_t mLqi; ///< The Link Quality Indicator for a received message. - bool mLinkSecurity; ///< Indicates whether or not link security is enabled. + uint16_t mPanId; ///< Source PAN ID + uint8_t mChannel; ///< 802.15.4 Channel + int8_t mRss; ///< Received Signal Strength in dBm. + uint8_t mLqi; ///< Link Quality Indicator for a received message. + bool mLinkSecurity; ///< Indicates whether or not link security is enabled. }; /**