/* * 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/frame_builder.hpp" #include "common/frame_data.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 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 indicates whether or not header in a given frame is a LOWPAN_IPHC header. * * @param[in] aFrameData The frame data. * * @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 FrameData &aFrameData) { return (aFrameData.GetLength() > 0) && IsLowpanHc(aFrameData.GetBytes()); } /** * 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[in] aFrameBuilder The `FrameBuilder` to use to append the compressed headers. * * @returns The size of the compressed header in bytes. * */ Error Compress(Message & aMessage, const Mac::Address &aMacSource, const Mac::Address &aMacDest, FrameBuilder & aFrameBuilder); /** * This method decompresses a LOWPAN_IPHC header. * * If the header is parsed successfully the @p aFrameData is updated to skip over the parsed header bytes. * * @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,out] aFrameData A frame data containing the LOWPAN_IPHC header. * @param[in] aDatagramLength The IPv6 datagram length. * * @retval kErrorNone The header was decompressed successfully. @p aMessage and @p aFrameData are updated. * @retval kErrorParse Failed to parse the lowpan header. * @retval kErrorNoBufs Could not grow @p aMessage to write the parsed IPv6 header. * */ Error Decompress(Message & aMessage, const Mac::Address &aMacSource, const Mac::Address &aMacDest, FrameData & aFrameData, uint16_t aDatagramLength); /** * This method decompresses a LOWPAN_IPHC header. * * If the header is parsed successfully the @p aFrameData is updated to skip over the parsed header bytes. * * @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,out] aFrameData A frame data containing the LOWPAN_IPHC header. * * @retval kErrorNone The header was decompressed successfully. @p aIp6Headre and @p aFrameData are updated. * @retval kErrorParse Failed to parse the lowpan header. * */ Error DecompressBaseHeader(Ip6::Header & aIp6Header, bool & aCompressedNextHeader, const Mac::Address &aMacSource, const Mac::Address &aMacDest, FrameData & aFrameData); /** * This method decompresses a LOWPAN_NHC UDP header. * * If the header is parsed successfully the @p aFrameData is updated to skip over the parsed header bytes. * * @param[out] aUdpHeader A reference where the UDP header will be placed. * @param[in,out] aFrameData A frame data containing the LOWPAN_NHC header. * * @retval kErrorNone The header was decompressed successfully. @p aUdpHeader and @p aFrameData are updated. * @retval kErrorParse Failed to parse the lowpan header. * */ Error DecompressUdpHeader(Ip6::Udp::Header &aUdpHeader, FrameData &aFrameData); /** * 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, FrameBuilder & aFrameBuilder, uint8_t & aHeaderDepth); Error CompressExtensionHeader(Message &aMessage, FrameBuilder &aFrameBuilder, uint8_t &aNextHeader); Error CompressSourceIid(const Mac::Address &aMacAddr, const Ip6::Address &aIpAddr, const Context & aContext, uint16_t & aHcCtl, FrameBuilder & aFrameBuilder); Error CompressDestinationIid(const Mac::Address &aMacAddr, const Ip6::Address &aIpAddr, const Context & aContext, uint16_t & aHcCtl, FrameBuilder & aFrameBuilder); Error CompressMulticast(const Ip6::Address &aIpAddr, uint16_t &aHcCtl, FrameBuilder &aFrameBuilder); Error CompressUdp(Message &aMessage, FrameBuilder &aFrameBuilder); Error DecompressExtensionHeader(Message &aMessage, FrameData &aFrameData); Error DecompressUdpHeader(Message &aMessage, FrameData &aFrameData, 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 FrameData &aFrameData); /** * 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 frame data. * * If the Mesh Header is parsed successfully the @p aFrameData is updated to skip over the parsed header bytes. * * @param[in,out] aFrameData The frame data to parse from. * * @retval kErrorNone Mesh Header parsed successfully. @p aFrameData is updated to skip over parsed header. * @retval kErrorParse Mesh Header could not be parsed. * */ Error ParseFrom(FrameData &aFrameData); /** * 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 appends the Mesh Header to a given message. * * * @param[out] aMessage A message to append the Mesh Header to. * * @retval kErrorNone Successfully appended the Mesh Header to @p aMessage. * @retval kErrorNoBufs Insufficient available buffers to grow @p aMessage. * */ Error AppendTo(Message &aMessage) 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. * * @param[in] aFrameData The frame data. * * @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 FrameData &aFrameData); /** * 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); //~~~ REMOVE OR MAKE PRIVATE /** * This method parses the Fragment Header from a given frame data. * * If the Fragment Header is parsed successfully the @p aFrameData is updated to skip over the parsed header bytes. * * @param[in,out] aFrameData The frame data to parse from. * * @retval kErrorNone Fragment Header parsed successfully. @p aFrameData is updated to skip over parsed header. * @retval kErrorParse Fragment header could not be parsed. * */ Error ParseFrom(FrameData &aFrameData); /** * 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. static bool IsFragmentHeader(const uint8_t *aFrame, uint16_t aFrameLength); uint16_t mSize; uint16_t mTag; uint16_t mOffset; }; /** * @} */ } // namespace Lowpan } // namespace ot #endif // LOWPAN_HPP_