blob: c0edcca387814f437be20cd4a344f185dbbd41d1 [file] [log] [blame]
/*
* Copyright (c) 2016, 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 includes definitions for 6LoWPAN header compression.
*/
#ifndef LOWPAN_HPP_
#define LOWPAN_HPP_
#include "openthread-core-config.h"
#include "common/debug.hpp"
#include "common/locator.hpp"
#include "common/message.hpp"
#include "common/non_copyable.hpp"
#include "mac/mac_types.hpp"
#include "net/ip6.hpp"
#include "net/ip6_address.hpp"
#include "net/ip6_types.hpp"
namespace ot {
/**
* @addtogroup core-6lowpan
*
* @brief
* This module includes definitions for 6LoWPAN header compression.
*
* @{
*/
/**
* @namespace ot::Lowpan
*
* @brief
* This namespace includes definitions for 6LoWPAN message processing.
*
*/
namespace Lowpan {
using ot::Encoding::BigEndian::HostSwap16;
/**
* This structure represents a LOWPAN_IPHC Context.
*
*/
struct Context
{
Ip6::Prefix mPrefix; ///< The Prefix
uint8_t mContextId; ///< The Context ID.
bool mCompressFlag; ///< The Context compression flag.
};
/**
* This class defines a buffer writer used by the 6LoWPAN compressor.
*
*/
class BufferWriter
{
public:
/**
* This constructor initializes the buffer writer.
*
* @param[in] aBuf A pointer to the write buffer.
* @param[in] aLength The size of the write buffer.
*
*/
BufferWriter(uint8_t *aBuf, uint16_t aLength)
: mWritePointer(aBuf)
, mEndPointer(aBuf + aLength)
{
}
/**
* This method indicates whether there is buffer space available to write @p aLength bytes.
*
* @param[in] aLength Number of bytes to write.
*
* @retval TRUE Enough buffer space is available to write the requested number of bytes.
* @retval FALSE Insufficient buffer space to write the requested number of bytes.
*
*/
bool CanWrite(uint8_t aLength) const { return (mWritePointer + aLength) <= mEndPointer; }
/**
* This method returns the current write pointer value.
*
* @returns the current write pointer value.
*
*/
uint8_t *GetWritePointer(void) { return mWritePointer; }
/**
* This method advances the write pointer.
*
* @param[in] aLength Number of bytes to advance.
*
* @retval kErrorNone Enough buffer space is available to advance the requested number of bytes.
* @retval kErrorNoBufs Insufficient buffer space to advance the requested number of bytes.
*
*/
Error Advance(uint8_t aLength)
{
Error error = kErrorNone;
VerifyOrExit(CanWrite(aLength), error = kErrorNoBufs);
mWritePointer += aLength;
exit:
return error;
}
/**
* This method writes a byte into the buffer and updates the write pointer, if space is available.
*
* @param[in] aByte Byte to write.
*
* @retval kErrorNone Successfully wrote the byte and updated the pointer.
* @retval kErrorNoBufs Insufficient buffer space to write the byte.
*
*/
Error Write(uint8_t aByte)
{
Error error = kErrorNone;
VerifyOrExit(CanWrite(sizeof(aByte)), error = kErrorNoBufs);
*mWritePointer++ = aByte;
exit:
return error;
}
/**
* This method writes a byte sequence into the buffer and updates the write pointer, if space is available.
*
* @param[in] aBuf A pointer to the byte sequence.
* @param[in] aLength Number of bytes to write.
*
* @retval kErrorNone Successfully wrote the byte sequence and updated the pointer.
* @retval kErrorNoBufs Insufficient buffer space to write the byte sequence.
*
*/
Error Write(const void *aBuf, uint8_t aLength)
{
Error error = kErrorNone;
VerifyOrExit(CanWrite(aLength), error = kErrorNoBufs);
memcpy(mWritePointer, aBuf, aLength);
mWritePointer += aLength;
exit:
return error;
}
/**
* This method writes a byte sequence into the buffer and updates the write pointer, if space is available.
*
* The byte sequence is taken from a message buffer at the current message buffer's offset.
*
* @param[in] aMessage A message buffer.
* @param[in] aLength Number of bytes to write.
*
* @retval kErrorNone Successfully wrote the byte sequence and updated the pointer.
* @retval kErrorNoBufs Insufficient buffer space to write the byte sequence.
*
*/
Error Write(const Message &aMessage, uint8_t aLength)
{
Error error = kErrorNone;
int rval;
OT_UNUSED_VARIABLE(rval);
VerifyOrExit(CanWrite(aLength), error = kErrorNoBufs);
rval = aMessage.ReadBytes(aMessage.GetOffset(), mWritePointer, aLength);
OT_ASSERT(rval == aLength);
mWritePointer += aLength;
exit:
return error;
}
private:
uint8_t *mWritePointer;
uint8_t *mEndPointer;
};
/**
* This class implements LOWPAN_IPHC header compression.
*
*/
class Lowpan : public InstanceLocator, private NonCopyable
{
public:
/**
* This constructor initializes the object.
*
* @param[in] aInstance A reference to the OpenThread instance.
*
*/
explicit Lowpan(Instance &aInstance);
/**
* This method indicates whether or not the header is a LOWPAN_IPHC header.
*
* @param[in] aHeader A pointer to the header.
*
* @retval TRUE If the header matches the LOWPAN_IPHC dispatch value.
* @retval FALSE If the header does not match the LOWPAN_IPHC dispatch value.
*/
static bool IsLowpanHc(const uint8_t *aHeader)
{
return (aHeader[0] & (Lowpan::kHcDispatchMask >> 8)) == (Lowpan::kHcDispatch >> 8);
}
/**
* This method compresses an IPv6 header.
*
* @param[in] aMessage A reference to the IPv6 message.
* @param[in] aMacSource The MAC source address.
* @param[in] aMacDest The MAC destination address.
* @param[out] aBuf A pointer where the compressed IPv6 header will be placed.
*
* @returns The size of the compressed header in bytes.
*
*/
Error Compress(Message &aMessage, const Mac::Address &aMacSource, const Mac::Address &aMacDest, BufferWriter &aBuf);
/**
* This method decompresses a LOWPAN_IPHC header.
*
* @param[out] aMessage A reference where the IPv6 header will be placed.
* @param[in] aMacSource The MAC source address.
* @param[in] aMacDest The MAC destination address.
* @param[in] aBuf A pointer to the LOWPAN_IPHC header.
* @param[in] aBufLength The number of bytes in @p aBuf.
* @param[in] aDatagramLength The IPv6 datagram length.
*
* @returns The size of the compressed header in bytes.
*
*/
int Decompress(Message & aMessage,
const Mac::Address &aMacSource,
const Mac::Address &aMacDest,
const uint8_t * aBuf,
uint16_t aBufLength,
uint16_t aDatagramLength);
/**
* This method decompresses a LOWPAN_IPHC header.
*
* @param[out] aIp6Header A reference where the IPv6 header will be placed.
* @param[out] aCompressedNextHeader A boolean reference to output whether next header is compressed or not.
* @param[in] aMacSource The MAC source address.
* @param[in] aMacDest The MAC destination address.
* @param[in] aBuf A pointer to the LOWPAN_IPHC header.
* @param[in] aBufLength The number of bytes in @p aBuf.
*
* @returns The size of the compressed header in bytes or -1 if decompression fails.
*
*/
int DecompressBaseHeader(Ip6::Header & aIp6Header,
bool & aCompressedNextHeader,
const Mac::Address &aMacSource,
const Mac::Address &aMacDest,
const uint8_t * aBuf,
uint16_t aBufLength);
/**
* This method decompresses a LOWPAN_NHC UDP header.
*
* @param[out] aUdpHeader A reference where the UDP header will be placed.
* @param[in] aBuf A pointer to the LOWPAN_NHC header.
* @param[in] aBufLength The number of bytes in @p aBuf.
*
* @returns The size of the compressed header in bytes or -1 if decompression fails.
*
*/
int DecompressUdpHeader(Ip6::Udp::Header &aUdpHeader, const uint8_t *aBuf, uint16_t aBufLength);
/**
* This method decompresses the IPv6 ECN field in a LOWPAN_IPHC header.
*
* @param[in] aMessage The message to read the IPHC header from.
* @param[in] aOffset The offset in @p aMessage to start of IPHC header.
*
* @returns The decompressed ECN field. If the IPHC header is not valid `kEcnNotCapable` is returned.
*
*/
Ip6::Ecn DecompressEcn(const Message &aMessage, uint16_t aOffset) const;
/**
* This method updates the compressed ECN field in a LOWPAN_IPHC header to `kEcnMarked`.
*
* This method MUST be used when the ECN field is not elided in the IPHC header. Note that the ECN is not elided
* when it is not zero (`kEcnNotCapable`).
*
* @param[in,out] aMessage The message containing the IPHC header and to update.
* @param[in] aOffset The offset in @p aMessage to start of IPHC header.
*
*/
void MarkCompressedEcn(Message &aMessage, uint16_t aOffset);
private:
static constexpr uint16_t kHcDispatch = 3 << 13;
static constexpr uint16_t kHcDispatchMask = 7 << 13;
static constexpr uint16_t kHcTrafficClass = 1 << 11;
static constexpr uint16_t kHcFlowLabel = 2 << 11;
static constexpr uint16_t kHcTrafficFlow = 3 << 11;
static constexpr uint16_t kHcTrafficFlowMask = 3 << 11;
static constexpr uint16_t kHcNextHeader = 1 << 10;
static constexpr uint16_t kHcHopLimit1 = 1 << 8;
static constexpr uint16_t kHcHopLimit64 = 2 << 8;
static constexpr uint16_t kHcHopLimit255 = 3 << 8;
static constexpr uint16_t kHcHopLimitMask = 3 << 8;
static constexpr uint16_t kHcContextId = 1 << 7;
static constexpr uint16_t kHcSrcAddrContext = 1 << 6;
static constexpr uint16_t kHcSrcAddrMode0 = 0 << 4;
static constexpr uint16_t kHcSrcAddrMode1 = 1 << 4;
static constexpr uint16_t kHcSrcAddrMode2 = 2 << 4;
static constexpr uint16_t kHcSrcAddrMode3 = 3 << 4;
static constexpr uint16_t kHcSrcAddrModeMask = 3 << 4;
static constexpr uint16_t kHcMulticast = 1 << 3;
static constexpr uint16_t kHcDstAddrContext = 1 << 2;
static constexpr uint16_t kHcDstAddrMode0 = 0 << 0;
static constexpr uint16_t kHcDstAddrMode1 = 1 << 0;
static constexpr uint16_t kHcDstAddrMode2 = 2 << 0;
static constexpr uint16_t kHcDstAddrMode3 = 3 << 0;
static constexpr uint16_t kHcDstAddrModeMask = 3 << 0;
static constexpr uint8_t kEcnOffset = 6;
static constexpr uint8_t kEcnMask = 3 << kEcnOffset;
static constexpr uint8_t kExtHdrDispatch = 0xe0;
static constexpr uint8_t kExtHdrDispatchMask = 0xf0;
static constexpr uint8_t kExtHdrEidHbh = 0x00;
static constexpr uint8_t kExtHdrEidRouting = 0x02;
static constexpr uint8_t kExtHdrEidFragment = 0x04;
static constexpr uint8_t kExtHdrEidDst = 0x06;
static constexpr uint8_t kExtHdrEidMobility = 0x08;
static constexpr uint8_t kExtHdrEidIp6 = 0x0e;
static constexpr uint8_t kExtHdrEidMask = 0x0e;
static constexpr uint8_t kExtHdrNextHeader = 0x01;
static constexpr uint16_t kExtHdrMaxLength = 255;
static constexpr uint8_t kUdpDispatch = 0xf0;
static constexpr uint8_t kUdpDispatchMask = 0xf8;
static constexpr uint8_t kUdpChecksum = 1 << 2;
static constexpr uint8_t kUdpPortMask = 3 << 0;
Error Compress(Message & aMessage,
const Mac::Address &aMacSource,
const Mac::Address &aMacDest,
BufferWriter & aBuf,
uint8_t & aHeaderDepth);
Error CompressExtensionHeader(Message &aMessage, BufferWriter &aBuf, uint8_t &aNextHeader);
Error CompressSourceIid(const Mac::Address &aMacAddr,
const Ip6::Address &aIpAddr,
const Context & aContext,
uint16_t & aHcCtl,
BufferWriter & aBuf);
Error CompressDestinationIid(const Mac::Address &aMacAddr,
const Ip6::Address &aIpAddr,
const Context & aContext,
uint16_t & aHcCtl,
BufferWriter & aBuf);
Error CompressMulticast(const Ip6::Address &aIpAddr, uint16_t &aHcCtl, BufferWriter &aBuf);
Error CompressUdp(Message &aMessage, BufferWriter &aBuf);
int DecompressExtensionHeader(Message &aMessage, const uint8_t *aBuf, uint16_t aBufLength);
int DecompressUdpHeader(Message &aMessage, const uint8_t *aBuf, uint16_t aBufLength, uint16_t aDatagramLength);
Error DispatchToNextHeader(uint8_t aDispatch, uint8_t &aNextHeader);
static void CopyContext(const Context &aContext, Ip6::Address &aAddress);
static Error ComputeIid(const Mac::Address &aMacAddr, const Context &aContext, Ip6::Address &aIpAddress);
};
/**
* This class implements Mesh Header generation and processing.
*
*/
class MeshHeader
{
public:
/**
* The additional value that is added to predicted value of the route cost.
*
*/
static constexpr uint8_t kAdditionalHopsLeft = 1;
/**
* This method initializes the Mesh Header with a given Mesh Source, Mesh Destination and Hops Left value.
*
* @param[in] aSource The Mesh Source address.
* @param[in] aDestination The Mesh Destination address.
* @param[in] aHopsLeft The Hops Left value.
*
*/
void Init(uint16_t aSource, uint16_t aDestination, uint8_t aHopsLeft);
/**
* This static method indicates whether or not the header (in a given frame) is a Mesh Header.
*
* @note This method checks whether the first byte in header/frame (dispatch byte) matches the Mesh Header dispatch
* It does not fully parse and validate the Mesh Header. `ParseFrom()` method can be used to fully parse and
* validate the header.
*
* @retval TRUE If the header matches the Mesh Header dispatch value.
* @retval FALSE If the header does not match the Mesh Header dispatch value.
*
*/
static bool IsMeshHeader(const uint8_t *aFrame, uint16_t aFrameLength);
/**
* This method parses the Mesh Header from a frame @p aFrame.
*
* @param[in] aFrame The pointer to the frame.
* @param[in] aFrameLength The length of the frame.
* @param[out] aHeaderLength A reference to a variable to output the parsed header length (on success).
*
* @retval kErrorNone Mesh Header parsed successfully.
* @retval kErrorParse Mesh Header could not be parsed.
*
*/
Error ParseFrom(const uint8_t *aFrame, uint16_t aFrameLength, uint16_t &aHeaderLength);
/**
* This method parses the Mesh Header from a given message.
*
* @note The Mesh Header is read from offset zero within the @p aMessage.
*
* @param[in] aMessage The message to read from.
*
* @retval kErrorNone Mesh Header parsed successfully.
* @retval kErrorParse Mesh Header could not be parsed.
*
*/
Error ParseFrom(const Message &aMessage);
/**
* This method parses the Mesh Header from a given message.
*
* @note The Mesh Header is read from offset zero within the @p aMessage.
*
* @param[in] aMessage The message to read from.
* @param[out] aHeaderLength A reference to a variable to output the parsed header length (on success).
*
* @retval kErrorNone Mesh Header parsed successfully.
* @retval kErrorParse Mesh Header could not be parsed.
*
*/
Error ParseFrom(const Message &aMessage, uint16_t &aHeaderLength);
/**
* This method returns the the Mesh Header length when written to a frame.
*
* @note The returned value from this method gives the header length (number of bytes) when the header is written
* to a frame or message. This should not be used to determine the parsed length (number of bytes read) when the
* Mesh Header is parsed from a frame/message (using `ParseFrom()` methods).
*
* @returns The length of the Mesh Header (in bytes) when written to a frame.
*
*/
uint16_t GetHeaderLength(void) const;
/**
* This method returns the Hops Left value.
*
* @returns The Hops Left value.
*
*/
uint8_t GetHopsLeft(void) const { return mHopsLeft; }
/**
* This method decrements the Hops Left value (if it is not zero).
*
*/
void DecrementHopsLeft(void);
/**
* This method returns the Mesh Source address.
*
* @returns The Mesh Source address.
*
*/
uint16_t GetSource(void) const { return mSource; }
/**
* This method returns the Mesh Destination address.
*
* @returns The Mesh Destination address.
*
*/
uint16_t GetDestination(void) const { return mDestination; }
/**
* This method writes the Mesh Header into a given frame.
*
* @note This method expects the frame buffer to have enough space for the entire Mesh Header.
*
* @param[out] aFrame The pointer to the frame buffer to write to.
*
* @returns The header length (number of bytes written).
*
*/
uint16_t WriteTo(uint8_t *aFrame) const;
/**
* This method writes the Mesh Header to a message at a given offset.
*
* @note This method expects the @p aMessage length to be already set such that there is enough space for the
* entire Mesh Header to be written.
*
* @param[out] aMessage A message to write the Mesh Header into.
* @param[in] aOffset The offset at which to write the header.
*
* @returns The header length (number of bytes written).
*
*/
uint16_t WriteTo(Message &aMessage, uint16_t aOffset) const;
private:
static constexpr uint8_t kDispatch = 2 << 6;
static constexpr uint8_t kDispatchMask = 3 << 6;
static constexpr uint8_t kHopsLeftMask = 0x0f;
static constexpr uint8_t kSourceShort = 1 << 5;
static constexpr uint8_t kDestShort = 1 << 4;
static constexpr uint8_t kDeepHopsLeft = 0x0f;
// Dispatch byte + src + dest
static constexpr uint16_t kMinHeaderLength = sizeof(uint8_t) + sizeof(uint16_t) + sizeof(uint16_t);
static constexpr uint16_t kDeepHopsHeaderLength = kMinHeaderLength + sizeof(uint8_t); // min header + deep hops
uint16_t mSource;
uint16_t mDestination;
uint8_t mHopsLeft;
};
/**
* This class implements Fragment Header generation and parsing.
*
*/
class FragmentHeader
{
public:
static constexpr uint16_t kFirstFragmentHeaderSize = 4; ///< First fragment header size in octets.
static constexpr uint16_t kSubsequentFragmentHeaderSize = 5; ///< Subsequent fragment header size in octets.
/**
* This method initializes the Fragment Header as a first fragment.
*
* A first fragment header starts at offset zero.
*
* @param[in] aSize The Datagram Size value.
* @param[in] aTag The Datagram Tag value.
*
*/
void InitFirstFragment(uint16_t aSize, uint16_t aTag) { Init(aSize, aTag, 0); }
/**
* This method initializes the Fragment Header.
*
* The @p aOffset value will be truncated to become a multiple of 8.
*
* @param[in] aSize The Datagram Size value.
* @param[in] aTag The Datagram Tag value.
* @param[in] aOffset The Datagram Offset value.
*
*/
void Init(uint16_t aSize, uint16_t aTag, uint16_t aOffset);
/**
* This static method indicates whether or not the header (in a given frame) is a Fragment Header.
*
* @note This method checks whether the frame has the minimum required length and that the first byte in
* header (dispatch byte) matches the Fragment Header dispatch value. It does not fully parse and validate the
* Fragment Header. `ParseFrom()` method can be used to fully parse and validate the header.
*
* @retval TRUE If the header matches the Fragment Header dispatch value.
* @retval FALSE If the header does not match the Fragment Header dispatch value.
*
*/
static bool IsFragmentHeader(const uint8_t *aFrame, uint16_t aFrameLength);
/**
* This method parses the Fragment Header from a frame @p aFrame.
*
* @param[in] aFrame The pointer to the frame.
* @param[in] aFrameLength The length of the frame.
* @param[out] aHeaderLength A reference to a variable to output the parsed header length (on success).
*
* @retval kErrorNone Fragment Header parsed successfully.
* @retval kErrorParse Fragment header could not be parsed from @p aFrame.
*
*/
Error ParseFrom(const uint8_t *aFrame, uint16_t aFrameLength, uint16_t &aHeaderLength);
/**
* This method parses the Fragment Header from a message.
*
* @param[in] aMessage The message to read from.
* @param[in] aOffset The offset within the message to start reading from.
* @param[out] aHeaderLength A reference to a variable to output the parsed header length (on success).
*
* @retval kErrorNone Fragment Header parsed successfully.
* @retval kErrorParse Fragment header could not be parsed from @p aFrame.
*
*/
Error ParseFrom(const Message &aMessage, uint16_t aOffset, uint16_t &aHeaderLength);
/**
* This method returns the Datagram Size value.
*
* @returns The Datagram Size value.
*
*/
uint16_t GetDatagramSize(void) const { return mSize; }
/**
* This method returns the Datagram Tag value.
*
* @returns The Datagram Tag value.
*
*/
uint16_t GetDatagramTag(void) const { return mTag; }
/**
* This method returns the Datagram Offset value.
*
* The returned offset value is always multiple of 8.
*
* @returns The Datagram Offset value (multiple of 8).
*
*/
uint16_t GetDatagramOffset(void) const { return mOffset; }
/**
* This method writes the Fragment Header into a given frame.
*
* @note This method expects the frame buffer to have enough space for the entire Fragment Header
*
* @param[out] aFrame The pointer to the frame buffer to write to.
*
* @returns The header length (number of bytes written).
*
*/
uint16_t WriteTo(uint8_t *aFrame) const;
private:
static constexpr uint8_t kDispatch = 0xc0; // 0b1100_0000
static constexpr uint8_t kDispatchMask = 0xd8; // 0b1101_1000 accepts first (0b1100_0xxx) and next (0b1110_0xxx).
static constexpr uint8_t kOffsetFlag = 1 << 5; // Indicate first (no offset) vs. next (offset present) fragment.
static constexpr uint16_t kSizeMask = 0x7ff; // 0b0111_1111_1111 (first 11 bits).
static constexpr uint16_t kOffsetMask = 0xfff8; // Clears the last 3 bits to ensure offset is a multiple of 8.
static constexpr uint8_t kSizeIndex = 0; // Start index of Size field in the Fragment Header byte sequence.
static constexpr uint8_t kTagIndex = 2; // Start index of Tag field in the Fragment Header byte sequence.
static constexpr uint8_t kOffsetIndex = 4; // Start index of Offset field in the Fragment Header byte sequence.
uint16_t mSize;
uint16_t mTag;
uint16_t mOffset;
};
/**
* @}
*/
} // namespace Lowpan
} // namespace ot
#endif // LOWPAN_HPP_