blob: 151abcd421e5a576bffe12dcf1527f3d23483ece [file] [log] [blame]
/*
* Copyright (c) 2016-2017, The OpenThread Authors.
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* 3. Neither the name of the copyright holder nor the
* names of its contributors may be used to endorse or promote products
* derived from this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
* POSSIBILITY OF SUCH DAMAGE.
*/
/**
* @file
* This file implements MechCop TLV helper functions.
*/
#include "meshcop_tlvs.hpp"
#include "common/const_cast.hpp"
#include "common/debug.hpp"
#include "common/num_utils.hpp"
#include "common/numeric_limits.hpp"
#include "common/string.hpp"
#include "meshcop/meshcop.hpp"
namespace ot {
namespace MeshCoP {
bool Tlv::IsValid(const Tlv &aTlv)
{
bool isValid = true;
uint8_t minLength = 0;
switch (aTlv.GetType())
{
case Tlv::kPanId:
minLength = sizeof(PanIdTlv::UintValueType);
break;
case Tlv::kExtendedPanId:
minLength = sizeof(ExtendedPanIdTlv::ValueType);
break;
case Tlv::kPskc:
minLength = sizeof(PskcTlv::ValueType);
break;
case Tlv::kNetworkKey:
minLength = sizeof(NetworkKeyTlv::ValueType);
break;
case Tlv::kMeshLocalPrefix:
minLength = sizeof(MeshLocalPrefixTlv::ValueType);
break;
case Tlv::kChannel:
VerifyOrExit(aTlv.GetLength() >= sizeof(ChannelTlvValue), isValid = false);
isValid = aTlv.ReadValueAs<ChannelTlv>().IsValid();
break;
case Tlv::kNetworkName:
isValid = As<NetworkNameTlv>(aTlv).IsValid();
break;
case Tlv::kSecurityPolicy:
isValid = As<SecurityPolicyTlv>(aTlv).IsValid();
break;
case Tlv::kChannelMask:
isValid = As<ChannelMaskTlv>(aTlv).IsValid();
break;
default:
break;
}
if (minLength > 0)
{
isValid = (aTlv.GetLength() >= minLength);
}
exit:
return isValid;
}
NameData NetworkNameTlv::GetNetworkName(void) const
{
uint8_t len = GetLength();
if (len > sizeof(mNetworkName))
{
len = sizeof(mNetworkName);
}
return NameData(mNetworkName, len);
}
void NetworkNameTlv::SetNetworkName(const NameData &aNameData)
{
uint8_t len;
len = aNameData.CopyTo(mNetworkName, sizeof(mNetworkName));
SetLength(len);
}
bool NetworkNameTlv::IsValid(void) const { return IsValidUtf8String(mNetworkName, GetLength()); }
void SteeringDataTlv::CopyTo(SteeringData &aSteeringData) const
{
aSteeringData.Init(GetSteeringDataLength());
memcpy(aSteeringData.GetData(), mSteeringData, GetSteeringDataLength());
}
bool SecurityPolicyTlv::IsValid(void) const
{
return GetLength() >= sizeof(mRotationTime) && GetFlagsLength() >= kThread11FlagsLength;
}
SecurityPolicy SecurityPolicyTlv::GetSecurityPolicy(void) const
{
SecurityPolicy securityPolicy;
uint8_t length = Min(static_cast<uint8_t>(sizeof(mFlags)), GetFlagsLength());
securityPolicy.mRotationTime = GetRotationTime();
securityPolicy.SetFlags(mFlags, length);
return securityPolicy;
}
void SecurityPolicyTlv::SetSecurityPolicy(const SecurityPolicy &aSecurityPolicy)
{
SetRotationTime(aSecurityPolicy.mRotationTime);
aSecurityPolicy.GetFlags(mFlags, sizeof(mFlags));
}
const char *StateTlv::StateToString(State aState)
{
static const char *const kStateStrings[] = {
"Pending", // (0) kPending,
"Accept", // (1) kAccept
"Reject", // (2) kReject,
};
static_assert(0 == kPending, "kPending value is incorrect");
static_assert(1 == kAccept, "kAccept value is incorrect");
return aState == kReject ? kStateStrings[2] : kStateStrings[aState];
}
bool ChannelMaskTlv::IsValid(void) const
{
uint32_t channelMask;
return (ReadChannelMask(channelMask) == kErrorNone);
}
Error ChannelMaskTlv::ReadChannelMask(uint32_t &aChannelMask) const
{
EntriesData entriesData;
entriesData.Clear();
entriesData.mData = &mEntriesStart;
entriesData.mLength = GetLength();
return entriesData.Parse(aChannelMask);
}
Error ChannelMaskTlv::FindIn(const Message &aMessage, uint32_t &aChannelMask)
{
Error error;
EntriesData entriesData;
entriesData.Clear();
entriesData.mMessage = &aMessage;
SuccessOrExit(error = FindTlvValueOffset(aMessage, Tlv::kChannelMask, entriesData.mOffset, entriesData.mLength));
error = entriesData.Parse(aChannelMask);
exit:
return error;
}
Error ChannelMaskTlv::EntriesData::Parse(uint32_t &aChannelMask)
{
// Validates and parses the Channel Mask TLV entries for each
// channel page and if successful updates `aChannelMask` to
// return the combined mask for all channel pages supported by
// radio. The entries can be either contained in `mMessage` from
// `mOffset` (when `mMessage` is non-null) or be in a buffer
// `mData`. `mLength` gives the number of bytes for all entries.
Error error = kErrorParse;
Entry readEntry;
const Entry *entry;
uint16_t size;
aChannelMask = 0;
VerifyOrExit(mLength > 0); // At least one entry.
while (mLength > 0)
{
VerifyOrExit(mLength > kEntryHeaderSize);
if (mMessage != nullptr)
{
// We first read the entry's header only and after
// validating the entry and that the entry's channel page
// is supported by radio, we read the full `Entry`.
mMessage->ReadBytes(mOffset, &readEntry, kEntryHeaderSize);
entry = &readEntry;
}
else
{
entry = reinterpret_cast<const Entry *>(mData);
}
size = kEntryHeaderSize + entry->GetMaskLength();
VerifyOrExit(size <= mLength);
if (Radio::SupportsChannelPage(entry->GetChannelPage()))
{
// Currently supported channel pages all use `uint32_t`
// channel mask.
VerifyOrExit(entry->GetMaskLength() == kMaskLength);
if (mMessage != nullptr)
{
IgnoreError(mMessage->Read(mOffset, readEntry));
}
aChannelMask |= (entry->GetMask() & Radio::ChannelMaskForPage(entry->GetChannelPage()));
}
mLength -= size;
if (mMessage != nullptr)
{
mOffset += size;
}
else
{
mData += size;
}
}
error = kErrorNone;
exit:
return error;
}
void ChannelMaskTlv::PrepareValue(Value &aValue, uint32_t aChannelMask)
{
Entry *entry = reinterpret_cast<Entry *>(aValue.mData);
aValue.mLength = 0;
for (uint8_t page : Radio::kSupportedChannelPages)
{
uint32_t mask = (Radio::ChannelMaskForPage(page) & aChannelMask);
if (mask != 0)
{
entry->SetChannelPage(page);
entry->SetMaskLength(kMaskLength);
entry->SetMask(mask);
aValue.mLength += sizeof(Entry);
entry++;
}
}
}
Error ChannelMaskTlv::AppendTo(Message &aMessage, uint32_t aChannelMask)
{
Value value;
PrepareValue(value, aChannelMask);
return Tlv::Append<ChannelMaskTlv>(aMessage, value.mData, value.mLength);
}
} // namespace MeshCoP
} // namespace ot