1 /*
2  *  Copyright (c) 2016, The OpenThread Authors.
3  *  All rights reserved.
4  *
5  *  Redistribution and use in source and binary forms, with or without
6  *  modification, are permitted provided that the following conditions are met:
7  *  1. Redistributions of source code must retain the above copyright
8  *     notice, this list of conditions and the following disclaimer.
9  *  2. Redistributions in binary form must reproduce the above copyright
10  *     notice, this list of conditions and the following disclaimer in the
11  *     documentation and/or other materials provided with the distribution.
12  *  3. Neither the name of the copyright holder nor the
13  *     names of its contributors may be used to endorse or promote products
14  *     derived from this software without specific prior written permission.
15  *
16  *  THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
17  *  AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
18  *  IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
19  *  ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
20  *  LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
21  *  CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
22  *  SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
23  *  INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
24  *  CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
25  *  ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
26  *  POSSIBILITY OF SUCH DAMAGE.
27  */
28 
29 /**
30  * @file
31  *   This file implements a MeshCoP Leader.
32  */
33 
34 #include "meshcop_leader.hpp"
35 
36 #if OPENTHREAD_FTD
37 
38 #include <stdio.h>
39 
40 #include "coap/coap_message.hpp"
41 #include "common/as_core_type.hpp"
42 #include "common/code_utils.hpp"
43 #include "common/instance.hpp"
44 #include "common/locator_getters.hpp"
45 #include "common/log.hpp"
46 #include "common/random.hpp"
47 #include "meshcop/meshcop.hpp"
48 #include "meshcop/meshcop_tlvs.hpp"
49 #include "thread/thread_netif.hpp"
50 #include "thread/thread_tlvs.hpp"
51 #include "thread/uri_paths.hpp"
52 
53 namespace ot {
54 namespace MeshCoP {
55 
56 RegisterLogModule("MeshCoPLeader");
57 
Leader(Instance & aInstance)58 Leader::Leader(Instance &aInstance)
59     : InstanceLocator(aInstance)
60     , mTimer(aInstance)
61     , mDelayTimerMinimal(DelayTimerTlv::kDelayTimerMinimal)
62     , mSessionId(Random::NonCrypto::GetUint16())
63 {
64 }
65 
HandleTmf(Coap::Message & aMessage,const Ip6::MessageInfo & aMessageInfo)66 template <> void Leader::HandleTmf<kUriLeaderPetition>(Coap::Message &aMessage, const Ip6::MessageInfo &aMessageInfo)
67 {
68     OT_UNUSED_VARIABLE(aMessageInfo);
69 
70     CommissioningData data;
71     CommissionerIdTlv commissionerId;
72     StateTlv::State   state = StateTlv::kReject;
73 
74     LogInfo("Received %s", UriToString<kUriLeaderPetition>());
75 
76     VerifyOrExit(Get<Mle::MleRouter>().IsRoutingLocator(aMessageInfo.GetPeerAddr()));
77     SuccessOrExit(Tlv::FindTlv(aMessage, commissionerId));
78 
79     if (mTimer.IsRunning())
80     {
81         VerifyOrExit((commissionerId.GetCommissionerIdLength() == mCommissionerId.GetCommissionerIdLength()) &&
82                      (!strncmp(commissionerId.GetCommissionerId(), mCommissionerId.GetCommissionerId(),
83                                commissionerId.GetCommissionerIdLength())));
84 
85         ResignCommissioner();
86     }
87 
88     data.mBorderAgentLocator.Init();
89     data.mBorderAgentLocator.SetBorderAgentLocator(aMessageInfo.GetPeerAddr().GetIid().GetLocator());
90 
91     data.mCommissionerSessionId.Init();
92     data.mCommissionerSessionId.SetCommissionerSessionId(++mSessionId);
93 
94     data.mSteeringData.Init();
95     data.mSteeringData.SetLength(1);
96     data.mSteeringData.Clear();
97 
98     SuccessOrExit(
99         Get<NetworkData::Leader>().SetCommissioningData(reinterpret_cast<uint8_t *>(&data), data.GetLength()));
100 
101     mCommissionerId = commissionerId;
102 
103     if (mCommissionerId.GetLength() > CommissionerIdTlv::kMaxLength)
104     {
105         mCommissionerId.SetLength(CommissionerIdTlv::kMaxLength);
106     }
107 
108     state = StateTlv::kAccept;
109     mTimer.Start(Time::SecToMsec(kTimeoutLeaderPetition));
110 
111 exit:
112     SendPetitionResponse(aMessage, aMessageInfo, state);
113 }
114 
SendPetitionResponse(const Coap::Message & aRequest,const Ip6::MessageInfo & aMessageInfo,StateTlv::State aState)115 void Leader::SendPetitionResponse(const Coap::Message    &aRequest,
116                                   const Ip6::MessageInfo &aMessageInfo,
117                                   StateTlv::State         aState)
118 {
119     Error          error = kErrorNone;
120     Coap::Message *message;
121 
122     message = Get<Tmf::Agent>().NewPriorityResponseMessage(aRequest);
123     VerifyOrExit(message != nullptr, error = kErrorNoBufs);
124 
125     SuccessOrExit(error = Tlv::Append<StateTlv>(*message, aState));
126 
127     if (mTimer.IsRunning())
128     {
129         SuccessOrExit(error = mCommissionerId.AppendTo(*message));
130     }
131 
132     if (aState == StateTlv::kAccept)
133     {
134         SuccessOrExit(error = Tlv::Append<CommissionerSessionIdTlv>(*message, mSessionId));
135     }
136 
137     SuccessOrExit(error = Get<Tmf::Agent>().SendMessage(*message, aMessageInfo));
138 
139     LogInfo("Sent %s response", UriToString<kUriLeaderPetition>());
140 
141 exit:
142     FreeMessageOnError(message, error);
143     LogError("send petition response", error);
144 }
145 
HandleTmf(Coap::Message & aMessage,const Ip6::MessageInfo & aMessageInfo)146 template <> void Leader::HandleTmf<kUriLeaderKeepAlive>(Coap::Message &aMessage, const Ip6::MessageInfo &aMessageInfo)
147 {
148     uint8_t                state;
149     uint16_t               sessionId;
150     BorderAgentLocatorTlv *borderAgentLocator;
151     StateTlv::State        responseState;
152 
153     LogInfo("Received %s", UriToString<kUriLeaderKeepAlive>());
154 
155     SuccessOrExit(Tlv::Find<StateTlv>(aMessage, state));
156 
157     SuccessOrExit(Tlv::Find<CommissionerSessionIdTlv>(aMessage, sessionId));
158 
159     borderAgentLocator =
160         As<BorderAgentLocatorTlv>(Get<NetworkData::Leader>().GetCommissioningDataSubTlv(Tlv::kBorderAgentLocator));
161 
162     if ((borderAgentLocator == nullptr) || (sessionId != mSessionId))
163     {
164         responseState = StateTlv::kReject;
165     }
166     else if (state != StateTlv::kAccept)
167     {
168         responseState = StateTlv::kReject;
169         ResignCommissioner();
170     }
171     else
172     {
173         uint16_t rloc = aMessageInfo.GetPeerAddr().GetIid().GetLocator();
174 
175         if (borderAgentLocator->GetBorderAgentLocator() != rloc)
176         {
177             borderAgentLocator->SetBorderAgentLocator(rloc);
178             Get<NetworkData::Leader>().IncrementVersion();
179         }
180 
181         responseState = StateTlv::kAccept;
182         mTimer.Start(Time::SecToMsec(kTimeoutLeaderPetition));
183     }
184 
185     SendKeepAliveResponse(aMessage, aMessageInfo, responseState);
186 
187 exit:
188     return;
189 }
190 
SendKeepAliveResponse(const Coap::Message & aRequest,const Ip6::MessageInfo & aMessageInfo,StateTlv::State aState)191 void Leader::SendKeepAliveResponse(const Coap::Message    &aRequest,
192                                    const Ip6::MessageInfo &aMessageInfo,
193                                    StateTlv::State         aState)
194 {
195     Error          error = kErrorNone;
196     Coap::Message *message;
197 
198     message = Get<Tmf::Agent>().NewPriorityResponseMessage(aRequest);
199     VerifyOrExit(message != nullptr, error = kErrorNoBufs);
200 
201     SuccessOrExit(error = Tlv::Append<StateTlv>(*message, aState));
202 
203     SuccessOrExit(error = Get<Tmf::Agent>().SendMessage(*message, aMessageInfo));
204 
205     LogInfo("Sent %s response", UriToString<kUriLeaderKeepAlive>());
206 
207 exit:
208     FreeMessageOnError(message, error);
209     LogError("send keep alive response", error);
210 }
211 
SendDatasetChanged(const Ip6::Address & aAddress)212 void Leader::SendDatasetChanged(const Ip6::Address &aAddress)
213 {
214     Error            error = kErrorNone;
215     Tmf::MessageInfo messageInfo(GetInstance());
216     Coap::Message   *message;
217 
218     message = Get<Tmf::Agent>().NewPriorityConfirmablePostMessage(kUriDatasetChanged);
219     VerifyOrExit(message != nullptr, error = kErrorNoBufs);
220 
221     messageInfo.SetSockAddrToRlocPeerAddrTo(aAddress);
222     SuccessOrExit(error = Get<Tmf::Agent>().SendMessage(*message, messageInfo));
223 
224     LogInfo("Sent %s", UriToString<kUriDatasetChanged>());
225 
226 exit:
227     FreeMessageOnError(message, error);
228     LogError("send dataset changed", error);
229 }
230 
SetDelayTimerMinimal(uint32_t aDelayTimerMinimal)231 Error Leader::SetDelayTimerMinimal(uint32_t aDelayTimerMinimal)
232 {
233     Error error = kErrorNone;
234     VerifyOrExit((aDelayTimerMinimal != 0 && aDelayTimerMinimal < DelayTimerTlv::kDelayTimerDefault),
235                  error = kErrorInvalidArgs);
236     mDelayTimerMinimal = aDelayTimerMinimal;
237 
238 exit:
239     return error;
240 }
241 
GetDelayTimerMinimal(void) const242 uint32_t Leader::GetDelayTimerMinimal(void) const { return mDelayTimerMinimal; }
243 
HandleTimer(void)244 void Leader::HandleTimer(void)
245 {
246     VerifyOrExit(Get<Mle::MleRouter>().IsLeader());
247 
248     ResignCommissioner();
249 
250 exit:
251     return;
252 }
253 
SetEmptyCommissionerData(void)254 void Leader::SetEmptyCommissionerData(void)
255 {
256     CommissionerSessionIdTlv mCommissionerSessionId;
257 
258     mCommissionerSessionId.Init();
259     mCommissionerSessionId.SetCommissionerSessionId(++mSessionId);
260 
261     IgnoreError(Get<NetworkData::Leader>().SetCommissioningData(reinterpret_cast<uint8_t *>(&mCommissionerSessionId),
262                                                                 sizeof(Tlv) + mCommissionerSessionId.GetLength()));
263 }
264 
ResignCommissioner(void)265 void Leader::ResignCommissioner(void)
266 {
267     mTimer.Stop();
268     SetEmptyCommissionerData();
269 
270     LogInfo("commissioner inactive");
271 }
272 
273 } // namespace MeshCoP
274 } // namespace ot
275 
276 #endif // OPENTHREAD_FTD
277