1 /*
2 * Copyright (c) 2016-2017, 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 MechCop TLV helper functions.
32 */
33
34 #include "meshcop_tlvs.hpp"
35
36 #include "common/const_cast.hpp"
37 #include "common/debug.hpp"
38 #include "common/string.hpp"
39 #include "meshcop/meshcop.hpp"
40
41 namespace ot {
42 namespace MeshCoP {
43
IsValid(const Tlv & aTlv)44 bool Tlv::IsValid(const Tlv &aTlv)
45 {
46 bool rval = true;
47
48 switch (aTlv.GetType())
49 {
50 case Tlv::kChannel:
51 rval = As<ChannelTlv>(aTlv).IsValid();
52 break;
53
54 case Tlv::kPanId:
55 rval = As<PanIdTlv>(aTlv).IsValid();
56 break;
57
58 case Tlv::kExtendedPanId:
59 rval = As<ExtendedPanIdTlv>(aTlv).IsValid();
60 break;
61
62 case Tlv::kNetworkName:
63 rval = As<NetworkNameTlv>(aTlv).IsValid();
64 break;
65
66 case Tlv::kNetworkKey:
67 rval = As<NetworkKeyTlv>(aTlv).IsValid();
68 break;
69
70 case Tlv::kPskc:
71 rval = As<PskcTlv>(aTlv).IsValid();
72 break;
73
74 case Tlv::kMeshLocalPrefix:
75 rval = As<MeshLocalPrefixTlv>(aTlv).IsValid();
76 break;
77
78 case Tlv::kSecurityPolicy:
79 rval = As<SecurityPolicyTlv>(aTlv).IsValid();
80 break;
81
82 case Tlv::kChannelMask:
83 rval = As<ChannelMaskTlv>(aTlv).IsValid();
84 break;
85
86 default:
87 break;
88 }
89
90 return rval;
91 }
92
FindTlv(const uint8_t * aTlvsStart,uint16_t aTlvsLength,Type aType)93 const Tlv *Tlv::FindTlv(const uint8_t *aTlvsStart, uint16_t aTlvsLength, Type aType)
94 {
95 const Tlv *tlv;
96 const Tlv *end = reinterpret_cast<const Tlv *>(aTlvsStart + aTlvsLength);
97
98 for (tlv = reinterpret_cast<const Tlv *>(aTlvsStart); tlv < end; tlv = tlv->GetNext())
99 {
100 VerifyOrExit((tlv + 1) <= end, tlv = nullptr);
101 VerifyOrExit(!tlv->IsExtended() ||
102 (reinterpret_cast<const ExtendedTlv *>(tlv) + 1 <= reinterpret_cast<const ExtendedTlv *>(end)),
103 tlv = nullptr);
104 VerifyOrExit(tlv->GetNext() <= end, tlv = nullptr);
105
106 if (tlv->GetType() == aType)
107 {
108 ExitNow();
109 }
110 }
111
112 tlv = nullptr;
113
114 exit:
115 return tlv;
116 }
117
GetNetworkName(void) const118 NameData NetworkNameTlv::GetNetworkName(void) const
119 {
120 uint8_t len = GetLength();
121
122 if (len > sizeof(mNetworkName))
123 {
124 len = sizeof(mNetworkName);
125 }
126
127 return NameData(mNetworkName, len);
128 }
129
SetNetworkName(const NameData & aNameData)130 void NetworkNameTlv::SetNetworkName(const NameData &aNameData)
131 {
132 uint8_t len;
133
134 len = aNameData.CopyTo(mNetworkName, sizeof(mNetworkName));
135 SetLength(len);
136 }
137
IsValid(void) const138 bool NetworkNameTlv::IsValid(void) const
139 {
140 return GetLength() >= 1 && IsValidUtf8String(mNetworkName, GetLength());
141 }
142
CopyTo(SteeringData & aSteeringData) const143 void SteeringDataTlv::CopyTo(SteeringData &aSteeringData) const
144 {
145 aSteeringData.Init(GetSteeringDataLength());
146 memcpy(aSteeringData.GetData(), mSteeringData, GetSteeringDataLength());
147 }
148
IsValid(void) const149 bool SecurityPolicyTlv::IsValid(void) const
150 {
151 return GetLength() >= sizeof(mRotationTime) && GetFlagsLength() >= kThread11FlagsLength;
152 }
153
GetSecurityPolicy(void) const154 SecurityPolicy SecurityPolicyTlv::GetSecurityPolicy(void) const
155 {
156 SecurityPolicy securityPolicy;
157 uint8_t length = OT_MIN(static_cast<uint8_t>(sizeof(mFlags)), GetFlagsLength());
158
159 securityPolicy.mRotationTime = GetRotationTime();
160 securityPolicy.SetFlags(mFlags, length);
161
162 return securityPolicy;
163 }
164
SetSecurityPolicy(const SecurityPolicy & aSecurityPolicy)165 void SecurityPolicyTlv::SetSecurityPolicy(const SecurityPolicy &aSecurityPolicy)
166 {
167 SetRotationTime(aSecurityPolicy.mRotationTime);
168 aSecurityPolicy.GetFlags(mFlags, sizeof(mFlags));
169 }
170
IsValid(void) const171 bool ChannelTlv::IsValid(void) const
172 {
173 bool ret = false;
174
175 VerifyOrExit(GetLength() == sizeof(*this) - sizeof(Tlv));
176 VerifyOrExit(mChannelPage < sizeof(uint32_t) * CHAR_BIT);
177 VerifyOrExit((1U << mChannelPage) & Radio::kSupportedChannelPages);
178 VerifyOrExit(Radio::kChannelMin <= GetChannel() && GetChannel() <= Radio::kChannelMax);
179 ret = true;
180
181 exit:
182 return ret;
183 }
184
SetChannel(uint16_t aChannel)185 void ChannelTlv::SetChannel(uint16_t aChannel)
186 {
187 uint8_t channelPage = OT_RADIO_CHANNEL_PAGE_0;
188
189 #if OPENTHREAD_CONFIG_RADIO_2P4GHZ_OQPSK_SUPPORT
190 if ((OT_RADIO_2P4GHZ_OQPSK_CHANNEL_MIN <= aChannel) && (aChannel <= OT_RADIO_2P4GHZ_OQPSK_CHANNEL_MAX))
191 {
192 channelPage = OT_RADIO_CHANNEL_PAGE_0;
193 }
194 #endif
195
196 #if OPENTHREAD_CONFIG_RADIO_915MHZ_OQPSK_SUPPORT
197 if ((OT_RADIO_915MHZ_OQPSK_CHANNEL_MIN <= aChannel) && (aChannel <= OT_RADIO_915MHZ_OQPSK_CHANNEL_MAX))
198 {
199 channelPage = OT_RADIO_CHANNEL_PAGE_2;
200 }
201 #endif
202
203 #if OPENTHREAD_CONFIG_PLATFORM_RADIO_PROPRIETARY_SUPPORT
204 if ((OPENTHREAD_CONFIG_PLATFORM_RADIO_PROPRIETARY_CHANNEL_MIN == aChannel) ||
205 ((OPENTHREAD_CONFIG_PLATFORM_RADIO_PROPRIETARY_CHANNEL_MIN < aChannel) &&
206 (aChannel <= OPENTHREAD_CONFIG_PLATFORM_RADIO_PROPRIETARY_CHANNEL_MAX)))
207 {
208 channelPage = OPENTHREAD_CONFIG_PLATFORM_RADIO_PROPRIETARY_CHANNEL_PAGE;
209 }
210 #endif
211
212 SetChannelPage(channelPage);
213 mChannel = HostSwap16(aChannel);
214 }
215
IsValid(void) const216 bool ChannelMaskBaseTlv::IsValid(void) const
217 {
218 const ChannelMaskEntryBase *cur = GetFirstEntry();
219 const ChannelMaskEntryBase *end = reinterpret_cast<const ChannelMaskEntryBase *>(GetNext());
220 bool ret = false;
221
222 VerifyOrExit(cur != nullptr);
223
224 while (cur < end)
225 {
226 uint8_t channelPage;
227
228 VerifyOrExit((cur + 1) <= end && cur->GetNext() <= end);
229
230 channelPage = cur->GetChannelPage();
231
232 #if OPENTHREAD_CONFIG_PLATFORM_RADIO_PROPRIETARY_SUPPORT
233 if (channelPage == OPENTHREAD_CONFIG_PLATFORM_RADIO_PROPRIETARY_CHANNEL_PAGE)
234 #else
235 if ((channelPage == OT_RADIO_CHANNEL_PAGE_0) || (channelPage == OT_RADIO_CHANNEL_PAGE_2))
236 #endif
237 {
238 VerifyOrExit(static_cast<const ChannelMaskEntry *>(cur)->IsValid());
239 }
240
241 cur = cur->GetNext();
242 }
243
244 ret = true;
245
246 exit:
247 return ret;
248 }
249
GetFirstEntry(void) const250 const ChannelMaskEntryBase *ChannelMaskBaseTlv::GetFirstEntry(void) const
251 {
252 const ChannelMaskEntryBase *entry = nullptr;
253
254 VerifyOrExit(GetLength() >= sizeof(ChannelMaskEntryBase));
255
256 entry = reinterpret_cast<const ChannelMaskEntryBase *>(GetValue());
257 VerifyOrExit(GetLength() >= entry->GetEntrySize(), entry = nullptr);
258
259 exit:
260 return entry;
261 }
262
GetFirstEntry(void)263 ChannelMaskEntryBase *ChannelMaskBaseTlv::GetFirstEntry(void)
264 {
265 return AsNonConst(AsConst(this)->GetFirstEntry());
266 }
267
SetChannelMask(uint32_t aChannelMask)268 void ChannelMaskTlv::SetChannelMask(uint32_t aChannelMask)
269 {
270 uint8_t length = 0;
271 ChannelMaskEntry *entry;
272
273 entry = static_cast<ChannelMaskEntry *>(GetFirstEntry());
274
275 #if OPENTHREAD_CONFIG_RADIO_915MHZ_OQPSK_SUPPORT
276 if (aChannelMask & OT_RADIO_915MHZ_OQPSK_CHANNEL_MASK)
277 {
278 OT_ASSERT(entry != nullptr);
279 entry->Init();
280 entry->SetChannelPage(OT_RADIO_CHANNEL_PAGE_2);
281 entry->SetMask(aChannelMask & OT_RADIO_915MHZ_OQPSK_CHANNEL_MASK);
282
283 length += sizeof(ChannelMaskEntry);
284
285 entry = static_cast<ChannelMaskEntry *>(entry->GetNext());
286 }
287 #endif
288
289 #if OPENTHREAD_CONFIG_RADIO_2P4GHZ_OQPSK_SUPPORT
290 if (aChannelMask & OT_RADIO_2P4GHZ_OQPSK_CHANNEL_MASK)
291 {
292 OT_ASSERT(entry != nullptr);
293 entry->Init();
294 entry->SetChannelPage(OT_RADIO_CHANNEL_PAGE_0);
295 entry->SetMask(aChannelMask & OT_RADIO_2P4GHZ_OQPSK_CHANNEL_MASK);
296
297 length += sizeof(ChannelMaskEntry);
298 }
299 #endif
300
301 #if OPENTHREAD_CONFIG_PLATFORM_RADIO_PROPRIETARY_SUPPORT
302 if (aChannelMask & OPENTHREAD_CONFIG_PLATFORM_RADIO_PROPRIETARY_CHANNEL_MASK)
303 {
304 OT_ASSERT(entry != nullptr);
305 entry->Init();
306 entry->SetChannelPage(OPENTHREAD_CONFIG_PLATFORM_RADIO_PROPRIETARY_CHANNEL_PAGE);
307 entry->SetMask(aChannelMask & OPENTHREAD_CONFIG_PLATFORM_RADIO_PROPRIETARY_CHANNEL_MASK);
308
309 length += sizeof(ChannelMaskEntry);
310 }
311 #endif
312
313 SetLength(length);
314 }
315
GetChannelMask(void) const316 uint32_t ChannelMaskTlv::GetChannelMask(void) const
317 {
318 const ChannelMaskEntryBase *cur = GetFirstEntry();
319 const ChannelMaskEntryBase *end = reinterpret_cast<const ChannelMaskEntryBase *>(GetNext());
320 uint32_t mask = 0;
321
322 VerifyOrExit(cur != nullptr);
323
324 while (cur < end)
325 {
326 uint8_t channelPage;
327
328 VerifyOrExit((cur + 1) <= end && cur->GetNext() <= end);
329
330 channelPage = cur->GetChannelPage();
331
332 #if OPENTHREAD_CONFIG_RADIO_915MHZ_OQPSK_SUPPORT
333 if (channelPage == OT_RADIO_CHANNEL_PAGE_2)
334 {
335 mask |= static_cast<const ChannelMaskEntry *>(cur)->GetMask() & OT_RADIO_915MHZ_OQPSK_CHANNEL_MASK;
336 }
337 #endif
338
339 #if OPENTHREAD_CONFIG_RADIO_2P4GHZ_OQPSK_SUPPORT
340 if (channelPage == OT_RADIO_CHANNEL_PAGE_0)
341 {
342 mask |= static_cast<const ChannelMaskEntry *>(cur)->GetMask() & OT_RADIO_2P4GHZ_OQPSK_CHANNEL_MASK;
343 }
344 #endif
345
346 #if OPENTHREAD_CONFIG_PLATFORM_RADIO_PROPRIETARY_SUPPORT
347 if (channelPage == OPENTHREAD_CONFIG_PLATFORM_RADIO_PROPRIETARY_CHANNEL_PAGE)
348 {
349 mask |= static_cast<const ChannelMaskEntry *>(cur)->GetMask() &
350 OPENTHREAD_CONFIG_PLATFORM_RADIO_PROPRIETARY_CHANNEL_MASK;
351 }
352 #endif
353
354 cur = cur->GetNext();
355 }
356
357 exit:
358 return mask;
359 }
360
GetChannelMask(const Message & aMessage)361 uint32_t ChannelMaskTlv::GetChannelMask(const Message &aMessage)
362 {
363 uint32_t mask = 0;
364 uint16_t offset;
365 uint16_t end;
366
367 SuccessOrExit(FindTlvValueOffset(aMessage, kChannelMask, offset, end));
368 end += offset;
369
370 while (offset + sizeof(ChannelMaskEntryBase) <= end)
371 {
372 ChannelMaskEntry entry;
373
374 IgnoreError(aMessage.Read(offset, entry));
375 VerifyOrExit(offset + entry.GetEntrySize() <= end);
376
377 switch (entry.GetChannelPage())
378 {
379 #if OPENTHREAD_CONFIG_RADIO_2P4GHZ_OQPSK_SUPPORT
380 case OT_RADIO_CHANNEL_PAGE_0:
381 IgnoreError(aMessage.Read(offset, entry));
382 mask |= entry.GetMask() & OT_RADIO_2P4GHZ_OQPSK_CHANNEL_MASK;
383 break;
384 #endif
385
386 #if OPENTHREAD_CONFIG_RADIO_915MHZ_OQPSK_SUPPORT
387 case OT_RADIO_CHANNEL_PAGE_2:
388 IgnoreError(aMessage.Read(offset, entry));
389 mask |= entry.GetMask() & OT_RADIO_915MHZ_OQPSK_CHANNEL_MASK;
390 break;
391 #endif
392
393 #if OPENTHREAD_CONFIG_PLATFORM_RADIO_PROPRIETARY_SUPPORT
394 case OPENTHREAD_CONFIG_PLATFORM_RADIO_PROPRIETARY_CHANNEL_PAGE:
395 IgnoreError(aMessage.Read(offset, entry));
396 mask |= entry.GetMask() & OPENTHREAD_CONFIG_PLATFORM_RADIO_PROPRIETARY_CHANNEL_MASK;
397 break;
398 #endif
399 }
400 offset += entry.GetEntrySize();
401 }
402
403 exit:
404 return mask;
405 }
406
407 } // namespace MeshCoP
408 } // namespace ot
409