diff --git a/include/commissioning/commissioner.h b/include/commissioning/commissioner.h index 7d4eb3417..87e87264a 100644 --- a/include/commissioning/commissioner.h +++ b/include/commissioning/commissioner.h @@ -66,6 +66,12 @@ ThreadError otCommissionerStart(otInstance *aInstance, const char *aPSKd); */ ThreadError otCommissionerStop(otInstance *aInstance); +typedef void (*otCommissionerPanIdConflictCallback)(uint16_t aPanId, uint32_t aChannelMask, void *aContext); + +ThreadError otCommissionerPanIdQuery(otInstance *, uint16_t aPanId, uint32_t aChannelMask, + const otIp6Address *aAddress, + otCommissionerPanIdConflictCallback aCallback, void *aContext); + /** * @} * diff --git a/src/cli/cli.cpp b/src/cli/cli.cpp index 4bb986344..cae1043b7 100644 --- a/src/cli/cli.cpp +++ b/src/cli/cli.cpp @@ -1860,11 +1860,42 @@ void Interpreter::ProcessCommissioner(int argc, char *argv[]) { otCommissionerStop(mInstance); } + else if (strcmp(argv[0], "panid") == 0) + { + long panid; + long mask; + otIp6Address address; + + VerifyOrExit(argc > 3, error = kThreadError_Parse); + + // panid + SuccessOrExit(error = ParseLong(argv[1], panid)); + + // mask + SuccessOrExit(error = ParseLong(argv[2], mask)); + + // destination + SuccessOrExit(error = otIp6AddressFromString(argv[3], &address)); + + SuccessOrExit(error = otCommissionerPanIdQuery(mInstance, static_cast(panid), + static_cast(mask), + &address, Interpreter::s_HandlePanIdConflict, this)); + } exit: AppendResult(error); } +void Interpreter::s_HandlePanIdConflict(uint16_t aPanId, uint32_t aChannelMask, void *aContext) +{ + static_cast(aContext)->HandlePanIdConflict(aPanId, aChannelMask); +} + +void Interpreter::HandlePanIdConflict(uint16_t aPanId, uint32_t aChannelMask) +{ + sServer->OutputFormat("Conflict: %04x, %08x\r\n", aPanId, aChannelMask); +} + #endif // OPENTHREAD_ENABLE_COMMISSIONER #if OPENTHREAD_ENABLE_JOINER diff --git a/src/cli/cli.hpp b/src/cli/cli.hpp index b4861ac11..2630dfdcd 100644 --- a/src/cli/cli.hpp +++ b/src/cli/cli.hpp @@ -201,12 +201,14 @@ private: static void s_HandleActiveScanResult(otActiveScanResult *aResult, void *aContext); static void s_HandleNetifStateChanged(uint32_t aFlags, void *aContext); static void s_HandleLinkPcapReceive(const RadioPacket *aFrame, void *aContext); + static void s_HandlePanIdConflict(uint16_t aPanId, uint32_t aChannelMask, void *aContext); void HandleEchoResponse(Message &aMessage, const Ip6::MessageInfo &aMessageInfo); void HandlePingTimer(); void HandleActiveScanResult(otActiveScanResult *aResult); void HandleNetifStateChanged(uint32_t aFlags); void HandleLinkPcapReceive(const RadioPacket *aFrame); + void HandlePanIdConflict(uint16_t aPanId, uint32_t aChannelMask); static const struct Command sCommands[]; diff --git a/src/core/Makefile.am b/src/core/Makefile.am index 697fe32fe..c69f7695b 100644 --- a/src/core/Makefile.am +++ b/src/core/Makefile.am @@ -76,6 +76,7 @@ libopenthread_a_SOURCES = \ thread/network_data.cpp \ thread/network_data_local.cpp \ thread/network_data_leader.cpp \ + thread/panid_query_server.cpp \ thread/thread_netif.cpp \ thread/thread_tlvs.cpp \ $(NULL) @@ -83,6 +84,7 @@ libopenthread_a_SOURCES = \ if OPENTHREAD_ENABLE_COMMISSIONER libopenthread_a_SOURCES += \ meshcop/commissioner.cpp \ + meshcop/panid_query_client.cpp \ $(NULL) endif # OPENTHREAD_ENABLE_COMMISSIONER @@ -125,6 +127,7 @@ noinst_HEADERS = \ meshcop/joiner.hpp \ meshcop/joiner_router.hpp \ meshcop/leader.hpp \ + meshcop/panid_query_client.hpp \ net/icmp6.hpp \ net/ip6.hpp \ net/ip6_address.hpp \ @@ -153,6 +156,7 @@ noinst_HEADERS = \ thread/network_data_leader.hpp \ thread/network_data_local.hpp \ thread/network_data_tlvs.hpp \ + thread/panid_query_server.hpp \ thread/thread_netif.hpp \ thread/thread_tlvs.hpp \ thread/thread_uris.hpp \ diff --git a/src/core/meshcop/commissioner.cpp b/src/core/meshcop/commissioner.cpp index 57a60fe49..33edd2fb7 100644 --- a/src/core/meshcop/commissioner.cpp +++ b/src/core/meshcop/commissioner.cpp @@ -49,6 +49,7 @@ namespace Thread { namespace MeshCoP { Commissioner::Commissioner(ThreadNetif &aThreadNetif): + mPanIdQuery(aThreadNetif), mTimer(aThreadNetif.GetIp6().mTimerScheduler, HandleTimer, this), mTransmitTask(aThreadNetif.GetIp6().mTaskletScheduler, &HandleUdpTransmit, this), mSendKek(false), @@ -83,6 +84,11 @@ ThreadError Commissioner::Stop(void) return kThreadError_None; } +uint16_t Commissioner::GetSessionId(void) const +{ + return mSessionId; +} + void Commissioner::HandleTimer(void *aContext) { static_cast(aContext)->HandleTimer(); diff --git a/src/core/meshcop/commissioner.hpp b/src/core/meshcop/commissioner.hpp index 3dc9948f5..d7ce43675 100644 --- a/src/core/meshcop/commissioner.hpp +++ b/src/core/meshcop/commissioner.hpp @@ -36,6 +36,7 @@ #include #include +#include #include #include @@ -74,6 +75,16 @@ public: */ ThreadError Stop(void); + /** + * This method returns the Commissioner Session ID. + * + * @retuns The Commissioner Session ID. + * + */ + uint16_t GetSessionId(void) const; + + PanIdQueryClient mPanIdQuery; + private: static void HandleTimer(void *aContext); void HandleTimer(void); diff --git a/src/core/meshcop/panid_query_client.cpp b/src/core/meshcop/panid_query_client.cpp new file mode 100644 index 000000000..67382040c --- /dev/null +++ b/src/core/meshcop/panid_query_client.cpp @@ -0,0 +1,226 @@ +/* + * Copyright (c) 2016, Nest Labs, Inc. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the copyright holder nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +/** + * @file + * This file implements the PAN ID Query Client. + */ + +#include +#include +#include +#include +#include +#include +#include +#include + +namespace Thread { + +PanIdQueryClient::PanIdQueryClient(ThreadNetif &aThreadNetif) : + mPanIdQuery(OPENTHREAD_URI_PANID_CONFLICT, &HandleConflict, this), + mSocket(aThreadNetif.GetIp6().mUdp), + mTimer(aThreadNetif.GetIp6().mTimerScheduler, &HandleTimer, this), + mCoapServer(aThreadNetif.GetCoapServer()), + mNetif(aThreadNetif) +{ + mCoapServer.AddResource(mPanIdQuery); + mSocket.Open(HandleUdpReceive, this); +} + +ThreadError PanIdQueryClient::SendQuery(uint16_t aPanId, uint32_t aChannelMask, const Ip6::Address &aAddress, + otCommissionerPanIdConflictCallback aCallback, void *aContext) +{ + ThreadError error = kThreadError_None; + Coap::Header header; + MeshCoP::CommissionerSessionIdTlv sessionId; + MeshCoP::ChannelMaskTlv channelMask; + union + { + MeshCoP::ChannelMaskEntry channelMaskEntry; + uint8_t channelMaskBuf[sizeof(MeshCoP::ChannelMaskEntry) + sizeof(aChannelMask)]; + }; + MeshCoP::PanIdTlv panId; + Ip6::MessageInfo messageInfo; + Message *message; + + header.Init(); + header.SetVersion(1); + header.SetType(aAddress.IsMulticast() ? Coap::Header::kTypeNonConfirmable : Coap::Header::kTypeConfirmable); + header.SetCode(Coap::Header::kCodePost); + header.SetMessageId(0); + header.SetToken(NULL, 0); + header.AppendUriPathOptions(OPENTHREAD_URI_PANID_QUERY); + header.AppendContentFormatOption(Coap::Header::kApplicationOctetStream); + 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.Init(); + channelMask.SetLength(sizeof(channelMaskBuf)); + SuccessOrExit(error = message->Append(&channelMask, sizeof(channelMask))); + + channelMaskEntry.SetChannelPage(0); + channelMaskEntry.SetMaskLength(sizeof(aChannelMask)); + + for (size_t i = 0; i < sizeof(aChannelMask); i++) + { + channelMaskBuf[sizeof(MeshCoP::ChannelMaskEntry) + i] = (aChannelMask >> (8 * i)) & 0xff; + } + + SuccessOrExit(error = message->Append(channelMaskBuf, sizeof(channelMaskBuf))); + + panId.Init(); + panId.SetPanId(aPanId); + SuccessOrExit(error = message->Append(&panId, sizeof(panId))); + + memset(&messageInfo, 0, sizeof(messageInfo)); + messageInfo.GetPeerAddr() = aAddress; + messageInfo.mPeerPort = kCoapUdpPort; + messageInfo.mInterfaceId = mNetif.GetInterfaceId(); + SuccessOrExit(error = mSocket.SendTo(*message, messageInfo)); + + otLogInfoMeshCoP("sent panid query\r\n"); + + mCallback = aCallback; + mContext = aContext; + +exit: + + if (error != kThreadError_None && message != NULL) + { + message->Free(); + } + + return error; +} + +void PanIdQueryClient::HandleConflict(void *aContext, Coap::Header &aHeader, Message &aMessage, + const Ip6::MessageInfo &aMessageInfo) +{ + static_cast(aContext)->HandleConflict(aHeader, aMessage, aMessageInfo); +} + +void PanIdQueryClient::HandleConflict(Coap::Header &aHeader, Message &aMessage, const Ip6::MessageInfo &aMessageInfo) +{ + MeshCoP::PanIdTlv panId; + union + { + MeshCoP::ChannelMaskEntry channelMaskEntry; + uint8_t channelMaskBuf[sizeof(MeshCoP::ChannelMaskEntry) + sizeof(uint32_t)]; + }; + uint32_t channelMask; + uint16_t offset; + uint16_t length; + + VerifyOrExit(aHeader.GetType() == Coap::Header::kTypeConfirmable && + aHeader.GetCode() == Coap::Header::kCodePost, ;); + + otLogInfoMeshCoP("received panid conflict\r\n"); + + SuccessOrExit(MeshCoP::Tlv::GetTlv(aMessage, MeshCoP::Tlv::kPanId, sizeof(panId), panId)); + VerifyOrExit(panId.IsValid(), ;); + + SuccessOrExit(MeshCoP::Tlv::GetValueOffset(aMessage, MeshCoP::Tlv::kChannelMask, offset, length)); + aMessage.Read(offset, sizeof(channelMaskBuf), channelMaskBuf); + VerifyOrExit(channelMaskEntry.GetChannelPage() == 0 && + channelMaskEntry.GetMaskLength() == sizeof(uint32_t), ;); + + channelMask = + (static_cast(channelMaskBuf[sizeof(MeshCoP::ChannelMaskEntry) + 0]) << 0) | + (static_cast(channelMaskBuf[sizeof(MeshCoP::ChannelMaskEntry) + 1]) << 8) | + (static_cast(channelMaskBuf[sizeof(MeshCoP::ChannelMaskEntry) + 2]) << 16) | + (static_cast(channelMaskBuf[sizeof(MeshCoP::ChannelMaskEntry) + 3]) << 24); + + if (mCallback != NULL) + { + mCallback(panId.GetPanId(), channelMask, mContext); + } + + SendConflictResponse(aHeader, aMessageInfo); + +exit: + return; +} + +ThreadError PanIdQueryClient::SendConflictResponse(const Coap::Header &aRequestHeader, + const Ip6::MessageInfo &aRequestInfo) +{ + ThreadError error = kThreadError_None; + Message *message; + Coap::Header responseHeader; + Ip6::MessageInfo responseInfo; + + VerifyOrExit((message = mCoapServer.NewMessage(0)) != NULL, error = kThreadError_NoBufs); + + responseHeader.Init(); + responseHeader.SetVersion(1); + responseHeader.SetType(Coap::Header::kTypeAcknowledgment); + responseHeader.SetCode(Coap::Header::kCodeChanged); + responseHeader.SetMessageId(aRequestHeader.GetMessageId()); + responseHeader.SetToken(aRequestHeader.GetToken(), aRequestHeader.GetTokenLength()); + responseHeader.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)); + +exit: + + if (error != kThreadError_None && message != NULL) + { + message->Free(); + } + + return error; +} + +void PanIdQueryClient::HandleTimer(void *aContext) +{ + static_cast(aContext)->HandleTimer(); +} + +void PanIdQueryClient::HandleTimer(void) +{ +} + +void PanIdQueryClient::HandleUdpReceive(void *aContext, otMessage aMessage, const otMessageInfo *aMessageInfo) +{ + otLogInfoMeshCoP("received panid query response\r\n"); + (void)aContext; + (void)aMessage; + (void)aMessageInfo; +} + +} // namespace Thread diff --git a/src/core/meshcop/panid_query_client.hpp b/src/core/meshcop/panid_query_client.hpp new file mode 100644 index 000000000..3fd26e888 --- /dev/null +++ b/src/core/meshcop/panid_query_client.hpp @@ -0,0 +1,94 @@ +/* + * 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 responding to PANID Query Requests. + */ + +#ifndef PANID_QUERY_CLIENT_HPP_ +#define PANID_QUERY_CLIENT_HPP_ + +#include +#include +#include +#include +#include +#include +#include + +namespace Thread { + +class ThreadNetif; + +/** + * This class implements handling PANID Query Requests. + * + */ +class PanIdQueryClient +{ +public: + /** + * This constructor initializes the object. + * + */ + PanIdQueryClient(ThreadNetif &aThreadNetif); + + ThreadError SendQuery(uint16_t aPanId, uint32_t aChannelMask, const Ip6::Address &aAddress, + otCommissionerPanIdConflictCallback aCallback, void *aContext); + +private: + static void HandleConflict(void *aContext, Coap::Header &aHeader, Message &aMessage, + const Ip6::MessageInfo &aMessageInfo); + void HandleConflict(Coap::Header &aHeader, Message &aMessage, const Ip6::MessageInfo &aMessageInfo); + + static void HandleTimer(void *aContext); + void HandleTimer(void); + + static void HandleUdpReceive(void *aContext, otMessage aMessage, const otMessageInfo *aMessageInfo); + + ThreadError SendConflictResponse(const Coap::Header &aRequestHeader, const Ip6::MessageInfo &aRequestMessageInfo); + + otCommissionerPanIdConflictCallback mCallback; + void *mContext; + + Coap::Resource mPanIdQuery; + Ip6::UdpSocket mSocket; + Timer mTimer; + + Coap::Server &mCoapServer; + ThreadNetif &mNetif; +}; + +/** + * @} + */ + +} // namespace Thread + +#endif // PANID_QUERY_CLIENT_HPP_ diff --git a/src/core/openthread.cpp b/src/core/openthread.cpp index 6e40476ee..10a6424bb 100644 --- a/src/core/openthread.cpp +++ b/src/core/openthread.cpp @@ -1283,6 +1283,7 @@ ThreadError otSendPendingSet(otInstance *, const otOperationalDataset *aDataset, } #if OPENTHREAD_ENABLE_COMMISSIONER +#include ThreadError otCommissionerStart(otInstance *, const char *aPSKd) { return sThreadNetif->GetCommissioner().Start(aPSKd); @@ -1292,6 +1293,15 @@ ThreadError otCommissionerStop(otInstance *) { return sThreadNetif->GetCommissioner().Stop(); } + +ThreadError otCommissionerPanIdQuery(otInstance *, uint16_t aPanId, uint32_t aChannelMask, + const otIp6Address *aAddress, + otCommissionerPanIdConflictCallback aCallback, void *aContext) +{ + return sThreadNetif->GetCommissioner().mPanIdQuery.SendQuery(aPanId, aChannelMask, + *static_cast(aAddress), + aCallback, aContext); +} #endif // OPENTHREAD_ENABLE_COMMISSIONER #if OPENTHREAD_ENABLE_JOINER diff --git a/src/core/thread/panid_query_server.cpp b/src/core/thread/panid_query_server.cpp new file mode 100644 index 000000000..cf29de780 --- /dev/null +++ b/src/core/thread/panid_query_server.cpp @@ -0,0 +1,241 @@ +/* + * Copyright (c) 2016, Nest Labs, Inc. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the copyright holder nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +/** + * @file + * This file implements the PAN ID Query Server. + */ + +#include +#include +#include +#include +#include +#include +#include +#include + +namespace Thread { + +PanIdQueryServer::PanIdQueryServer(ThreadNetif &aThreadNetif) : + mPanIdQuery(OPENTHREAD_URI_PANID_QUERY, &HandleQuery, this), + mSocket(aThreadNetif.GetIp6().mUdp), + mTimer(aThreadNetif.GetIp6().mTimerScheduler, &HandleTimer, this), + mCoapServer(aThreadNetif.GetCoapServer()), + mNetif(aThreadNetif) +{ + mCoapServer.AddResource(mPanIdQuery); + mSocket.Open(HandleUdpReceive, this); +} + +void PanIdQueryServer::HandleQuery(void *aContext, Coap::Header &aHeader, Message &aMessage, + const Ip6::MessageInfo &aMessageInfo) +{ + static_cast(aContext)->HandleQuery(aHeader, aMessage, aMessageInfo); +} + +void PanIdQueryServer::HandleQuery(Coap::Header &aHeader, Message &aMessage, const Ip6::MessageInfo &aMessageInfo) +{ + MeshCoP::PanIdTlv panId; + union + { + MeshCoP::ChannelMaskEntry channelMaskEntry; + uint8_t channelMaskBuf[sizeof(MeshCoP::ChannelMaskEntry) + sizeof(uint32_t)]; + }; + uint16_t offset; + uint16_t length; + + VerifyOrExit(aHeader.GetCode() == Coap::Header::kCodePost, ;); + + SuccessOrExit(MeshCoP::Tlv::GetValueOffset(aMessage, MeshCoP::Tlv::kChannelMask, offset, length)); + aMessage.Read(offset, sizeof(channelMaskBuf), channelMaskBuf); + VerifyOrExit(channelMaskEntry.GetChannelPage() == 0 && + channelMaskEntry.GetMaskLength() == sizeof(uint32_t), ;); + + mChannelMask = + (static_cast(channelMaskBuf[sizeof(MeshCoP::ChannelMaskEntry) + 0]) << 0) | + (static_cast(channelMaskBuf[sizeof(MeshCoP::ChannelMaskEntry) + 1]) << 8) | + (static_cast(channelMaskBuf[sizeof(MeshCoP::ChannelMaskEntry) + 2]) << 16) | + (static_cast(channelMaskBuf[sizeof(MeshCoP::ChannelMaskEntry) + 3]) << 24); + + SuccessOrExit(MeshCoP::Tlv::GetTlv(aMessage, MeshCoP::Tlv::kPanId, sizeof(panId), panId)); + VerifyOrExit(panId.IsValid(), ;); + + mCommissioner = aMessageInfo.GetPeerAddr(); + mPanId = panId.GetPanId(); + mTimer.Start(kScanDelay); + + SendQueryResponse(aHeader, aMessageInfo); + +exit: + return; +} + +ThreadError PanIdQueryServer::SendQueryResponse(const Coap::Header &aRequestHeader, + const Ip6::MessageInfo &aRequestInfo) +{ + ThreadError error = kThreadError_None; + Message *message; + Coap::Header responseHeader; + Ip6::MessageInfo responseInfo; + + VerifyOrExit(aRequestHeader.GetType() == Coap::Header::kTypeConfirmable, ;); + + VerifyOrExit((message = mCoapServer.NewMessage(0)) != NULL, error = kThreadError_NoBufs); + + responseHeader.Init(); + responseHeader.SetVersion(1); + responseHeader.SetType(Coap::Header::kTypeAcknowledgment); + responseHeader.SetCode(Coap::Header::kCodeChanged); + responseHeader.SetMessageId(aRequestHeader.GetMessageId()); + responseHeader.SetToken(aRequestHeader.GetToken(), aRequestHeader.GetTokenLength()); + responseHeader.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 panid query response\r\n"); + +exit: + + if (error != kThreadError_None && message != NULL) + { + message->Free(); + } + + return error; +} + +void PanIdQueryServer::HandleScanResult(void *aContext, Mac::Frame *aFrame) +{ + static_cast(aContext)->HandleScanResult(aFrame); +} + +void PanIdQueryServer::HandleScanResult(Mac::Frame *aFrame) +{ + uint16_t panId; + + if (aFrame != NULL) + { + aFrame->GetSrcPanId(panId); + + if (panId == mPanId) + { + mChannelMask |= 1 << aFrame->GetChannel(); + } + } + else if (mChannelMask != 0) + { + SendConflict(); + } +} + +ThreadError PanIdQueryServer::SendConflict(void) +{ + ThreadError error = kThreadError_None; + Coap::Header header; + MeshCoP::ChannelMaskTlv channelMask; + union + { + MeshCoP::ChannelMaskEntry channelMaskEntry; + uint8_t channelMaskBuf[sizeof(MeshCoP::ChannelMaskEntry) + sizeof(uint32_t)]; + }; + MeshCoP::PanIdTlv panId; + Ip6::MessageInfo messageInfo; + Message *message; + + header.Init(); + header.SetVersion(1); + header.SetType(Coap::Header::kTypeConfirmable); + header.SetCode(Coap::Header::kCodePost); + header.SetMessageId(0); + header.SetToken(NULL, 0); + header.AppendUriPathOptions(OPENTHREAD_URI_PANID_CONFLICT); + header.AppendContentFormatOption(Coap::Header::kApplicationOctetStream); + header.Finalize(); + + VerifyOrExit((message = mSocket.NewMessage(0)) != NULL, error = kThreadError_NoBufs); + SuccessOrExit(error = message->Append(header.GetBytes(), header.GetLength())); + + channelMask.Init(); + channelMask.SetLength(sizeof(channelMaskBuf)); + SuccessOrExit(error = message->Append(&channelMask, sizeof(channelMask))); + + channelMaskEntry.SetChannelPage(0); + channelMaskEntry.SetMaskLength(sizeof(mChannelMask)); + + for (size_t i = 0; i < sizeof(mChannelMask); i++) + { + channelMaskBuf[sizeof(MeshCoP::ChannelMaskEntry) + i] = (mChannelMask >> (8 * i)) & 0xff; + } + + SuccessOrExit(error = message->Append(channelMaskBuf, sizeof(channelMaskBuf))); + + panId.Init(); + panId.SetPanId(mPanId); + SuccessOrExit(error = message->Append(&panId, sizeof(panId))); + + memset(&messageInfo, 0, sizeof(messageInfo)); + messageInfo.GetPeerAddr() = mCommissioner; + messageInfo.mPeerPort = kCoapUdpPort; + SuccessOrExit(error = mSocket.SendTo(*message, messageInfo)); + + otLogInfoMeshCoP("sent panid conflict\r\n"); + +exit: + + if (error != kThreadError_None && message != NULL) + { + message->Free(); + } + + return error; +} + +void PanIdQueryServer::HandleTimer(void *aContext) +{ + static_cast(aContext)->HandleTimer(); +} + +void PanIdQueryServer::HandleTimer(void) +{ + otLogInfoMeshCoP("%x\r\n", mChannelMask); + mNetif.GetMac().ActiveScan(mChannelMask, 0, HandleScanResult, this); + mChannelMask = 0; +} + +void PanIdQueryServer::HandleUdpReceive(void *aContext, otMessage aMessage, const otMessageInfo *aMessageInfo) +{ + (void)aContext; + (void)aMessage; + (void)aMessageInfo; +} + +} // namespace Thread diff --git a/src/core/thread/panid_query_server.hpp b/src/core/thread/panid_query_server.hpp new file mode 100644 index 000000000..b7dc976a0 --- /dev/null +++ b/src/core/thread/panid_query_server.hpp @@ -0,0 +1,104 @@ +/* + * 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 responding to PANID Query Requests. + */ + +#ifndef PANID_QUERY_SERVER_HPP_ +#define PANID_QUERY_SERVER_HPP_ + +#include +#include +#include +#include +#include +#include + +namespace Thread { + +class MeshForwarder; +class ThreadLastTransactionTimeTlv; +class ThreadMeshLocalEidTlv; +class ThreadNetif; +class ThreadTargetTlv; + +/** + * This class implements handling PANID Query Requests. + * + */ +class PanIdQueryServer +{ +public: + /** + * This constructor initializes the object. + * + */ + PanIdQueryServer(ThreadNetif &aThreadNetif); + +private: + enum + { + kScanDelay = 1000, ///< SCAN_DELAY (milliseconds) + }; + + static void HandleQuery(void *aContext, Coap::Header &aHeader, Message &aMessage, + const Ip6::MessageInfo &aMessageInfo); + void HandleQuery(Coap::Header &aHeader, Message &aMessage, const Ip6::MessageInfo &aMessageInfo); + + static void HandleScanResult(void *aContext, Mac::Frame *aFrame); + void HandleScanResult(Mac::Frame *aFrame); + + static void HandleTimer(void *aContext); + void HandleTimer(void); + + static void HandleUdpReceive(void *aContext, otMessage aMessage, const otMessageInfo *aMessageInfo); + + ThreadError SendConflict(); + ThreadError SendQueryResponse(const Coap::Header &aRequestHeader, const Ip6::MessageInfo &aRequestMessageInfo); + + Ip6::Address mCommissioner; + uint32_t mChannelMask; + uint16_t mPanId; + + Coap::Resource mPanIdQuery; + Ip6::UdpSocket mSocket; + Timer mTimer; + + Coap::Server &mCoapServer; + ThreadNetif &mNetif; +}; + +/** + * @} + */ + +} // namespace Thread + +#endif // PANID_QUERY_SERVER_HPP_ diff --git a/src/core/thread/thread_netif.cpp b/src/core/thread/thread_netif.cpp index f88ad94f7..05780d46a 100644 --- a/src/core/thread/thread_netif.cpp +++ b/src/core/thread/thread_netif.cpp @@ -77,7 +77,8 @@ ThreadNetif::ThreadNetif(Ip6::Ip6 &aIp6): mJoiner(*this), #endif // OPENTHREAD_ENABLE_JOINER mJoinerRouter(*this), - mLeader(*this) + mLeader(*this), + mPanIdQuery(*this) { mKeyManager.SetMasterKey(kThreadMasterKey, sizeof(kThreadMasterKey)); } diff --git a/src/core/thread/thread_netif.hpp b/src/core/thread/thread_netif.hpp index 6dc8674ee..1734113ad 100644 --- a/src/core/thread/thread_netif.hpp +++ b/src/core/thread/thread_netif.hpp @@ -49,6 +49,7 @@ #include #include #include +#include #if OPENTHREAD_ENABLE_COMMISSIONER #include @@ -271,6 +272,7 @@ private: MeshCoP::JoinerRouter mJoinerRouter; MeshCoP::Leader mLeader; + PanIdQueryServer mPanIdQuery; }; /** diff --git a/src/core/thread/thread_uris.hpp b/src/core/thread/thread_uris.hpp index 0032c3cb5..dc07c375e 100644 --- a/src/core/thread/thread_uris.hpp +++ b/src/core/thread/thread_uris.hpp @@ -162,6 +162,22 @@ namespace Thread { */ #define OPENTHREAD_URI_LEADER_KEEP_ALIVE "c/la" +/** + * @def OPENTHREAD_URI_PANID_CONFLICT + * + * The URI Path for PAN ID Conflict + * + */ +#define OPENTHREAD_URI_PANID_CONFLICT "c/pc" + +/** + * @def OPENTHREAD_URI_PANID_QUERY + * + * The URI Path for PAN ID Query + * + */ +#define OPENTHREAD_URI_PANID_QUERY "c/pq" + } // namespace Thread #endif // THREAD_URIS_HPP_ diff --git a/tests/scripts/Makefile.am b/tests/scripts/Makefile.am index 8fa589075..ef590dff1 100644 --- a/tests/scripts/Makefile.am +++ b/tests/scripts/Makefile.am @@ -102,6 +102,7 @@ EXTRA_DIST = \ thread-cert/Cert_7_1_03_BorderRouterAsLeader.py \ thread-cert/Cert_7_1_04_BorderRouterAsRouter.py \ thread-cert/Cert_7_1_05_BorderRouterAsRouter.py \ + thread-cert/Cert_9_2_14_PanIdQuery.py \ thread-cert/node.py \ $(NULL) @@ -216,6 +217,7 @@ check_SCRIPTS += \ thread-cert/Cert_7_1_03_BorderRouterAsLeader.py \ thread-cert/Cert_7_1_04_BorderRouterAsRouter.py \ thread-cert/Cert_7_1_05_BorderRouterAsRouter.py \ + thread-cert/Cert_9_2_14_PanIdQuery.py \ $(NULL) endif # OPENTHREAD_TESTS_SUBSET5 @@ -247,6 +249,7 @@ XFAIL_NCP_TESTS = \ thread-cert/Cert_7_1_03_BorderRouterAsLeader.py \ thread-cert/Cert_7_1_04_BorderRouterAsRouter.py \ thread-cert/Cert_7_1_05_BorderRouterAsRouter.py \ + thread-cert/Cert_9_2_14_PanIdQuery.py \ $(NULL) XFAIL_TESTS = $(if $(filter $(NODE_TYPE),ncp-sim),$(XFAIL_NCP_TESTS)) diff --git a/tests/scripts/thread-cert/Cert_9_2_14_PanIdQuery.py b/tests/scripts/thread-cert/Cert_9_2_14_PanIdQuery.py new file mode 100755 index 000000000..d579c6983 --- /dev/null +++ b/tests/scripts/thread-cert/Cert_9_2_14_PanIdQuery.py @@ -0,0 +1,102 @@ +#!/usr/bin/python +# +# 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. +# + +import time +import unittest + +import node + +COMMISSIONER = 1 +LEADER1 = 2 +ROUTER1 = 3 +LEADER2 = 4 + +class Cert_9_2_14_PanIdQuery(unittest.TestCase): + def setUp(self): + self.nodes = {} + for i in range(1,5): + self.nodes[i] = node.Node(i) + + self.nodes[COMMISSIONER].set_panid(0xface) + self.nodes[COMMISSIONER].set_mode('rsdn') + self.nodes[COMMISSIONER].add_whitelist(self.nodes[LEADER1].get_addr64()) + self.nodes[COMMISSIONER].enable_whitelist() + + self.nodes[LEADER1].set_panid(0xface) + self.nodes[LEADER1].set_mode('rsdn') + self.nodes[LEADER1].add_whitelist(self.nodes[COMMISSIONER].get_addr64()) + self.nodes[LEADER1].add_whitelist(self.nodes[ROUTER1].get_addr64()) + self.nodes[LEADER1].enable_whitelist() + + self.nodes[ROUTER1].set_panid(0xface) + 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[LEADER2].set_panid(0xdead) + self.nodes[LEADER2].set_mode('rsdn') + self.nodes[LEADER2].add_whitelist(self.nodes[ROUTER1].get_addr64()) + self.nodes[LEADER2].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[COMMISSIONER].start() + time.sleep(3) + self.assertEqual(self.nodes[COMMISSIONER].get_state(), 'router') + + self.nodes[ROUTER1].start() + time.sleep(3) + self.assertEqual(self.nodes[ROUTER1].get_state(), 'router') + + self.nodes[LEADER2].start() + time.sleep(5) + self.assertEqual(self.nodes[LEADER2].get_state(), 'leader') + + ipaddrs = self.nodes[ROUTER1].get_addrs() + for ipaddr in ipaddrs: + if ipaddr[0:4] != 'fe80': + break + + self.nodes[COMMISSIONER].panid_query('0xdead', '0xffffffff', ipaddr) + + self.nodes[COMMISSIONER].panid_query('0xdead', '0xffffffff', 'ff33:0040:fdde:ad00:beef:0:0:1') + + self.nodes[COMMISSIONER].ping(ipaddr) + +if __name__ == '__main__': + unittest.main() diff --git a/tests/scripts/thread-cert/node.py b/tests/scripts/thread-cert/node.py index aa9bddbbd..05ef04deb 100755 --- a/tests/scripts/thread-cert/node.py +++ b/tests/scripts/thread-cert/node.py @@ -325,6 +325,11 @@ class Node: self.send_command('netdataregister') self.pexpect.expect('Done') + def panid_query(self, panid, mask, ipaddr): + cmd = 'commissioner panid ' + panid + ' ' + mask + ' ' + ipaddr + self.send_command(cmd) + self.pexpect.expect('Conflict:', timeout=8) + def scan(self): self.send_command('scan')