/* * Copyright (c) 2020, 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 IPv6 Neighbor Discovery (ND). * * See RFC 4861 (https://tools.ietf.org/html/rfc4861) and RFC 4191 (https://tools.ietf.org/html/rfc4191). * */ #ifndef ND6_HPP_ #define ND6_HPP_ #include "openthread-core-config.h" #include #include #include #include "common/const_cast.hpp" #include "common/encoding.hpp" #include "common/equatable.hpp" #include "net/icmp6.hpp" #include "net/ip6.hpp" #include "thread/network_data_types.hpp" using ot::Encoding::BigEndian::HostSwap16; using ot::Encoding::BigEndian::HostSwap32; namespace ot { namespace Ip6 { namespace Nd { typedef NetworkData::RoutePreference RoutePreference; ///< Route Preference /** * This class represents the variable length options in Neighbor Discovery messages. * * @sa PrefixInfoOption * @sa RouteInfoOption * */ OT_TOOL_PACKED_BEGIN class Option { friend class RouterAdvertMessage; public: enum Type : uint8_t { kTypePrefixInfo = 3, ///< Prefix Information Option. kTypeRouteInfo = 24, ///< Route Information Option. }; static constexpr uint16_t kLengthUnit = 8; ///< The unit of length in octets. /** * This method gets the option type. * * @returns The option type. * */ uint8_t GetType(void) const { return mType; } /** * This method sets the option type. * * @param[in] aType The option type. * * */ void SetType(Type aType) { mType = aType; } /** * This method sets the length based on a given total option size in bytes. * * Th option must end on a 64-bit boundary, so the length is derived as `(aSize + 7) / 8 * 8`. * * @param[in] aSize The size of option in bytes. * */ void SetSize(uint16_t aSize) { mLength = static_cast((aSize + kLengthUnit - 1) / kLengthUnit); } /** * This method returns the size of the option in bytes. * * @returns The size of the option in bytes. * */ uint16_t GetSize(void) const { return mLength * kLengthUnit; } /** * This method sets the length of the option (in unit of 8 bytes). * * @param[in] aLength The length of the option in unit of 8 bytes. * */ void SetLength(uint8_t aLength) { mLength = aLength; } /** * This method returns the length of the option (in unit of 8 bytes). * * @returns The length of the option in unit of 8 bytes. * */ uint16_t GetLength(void) const { return mLength; } /** * This method indicates whether or not this option is valid. * * @retval TRUE The option is valid. * @retval FALSE The option is not valid. * */ bool IsValid(void) const { return mLength > 0; } private: class Iterator : public Unequatable { public: Iterator(void); Iterator(const void *aStart, const void *aEnd); const Option &operator*(void) { return *mOption; } void operator++(void) { Advance(); } void operator++(int) { Advance(); } bool operator==(const Iterator &aOther) const { return mOption == aOther.mOption; } private: static const Option *Next(const Option *aOption); void Advance(void); const Option * Validate(const Option *aOption) const; const Option *mOption; const Option *mEnd; }; uint8_t mType; // Type of the option. uint8_t mLength; // Length of the option in unit of 8 octets, including the `mType` and `mLength` fields. } OT_TOOL_PACKED_END; /** * This class represents the Prefix Information Option. * * See section 4.6.2 of RFC 4861 for definition of this option [https://tools.ietf.org/html/rfc4861#section-4.6.2] * */ OT_TOOL_PACKED_BEGIN class PrefixInfoOption : public Option, private Clearable { public: static constexpr Type kType = kTypePrefixInfo; ///< Prefix Information Option Type. /** * This method initializes the Prefix Info option with proper type and length and sets all other fields to zero. * */ void Init(void); /** * This method indicates whether or not the on-link flag is set. * * @retval TRUE The on-link flag is set. * @retval FALSE The on-link flag is not set. * */ bool IsOnLinkFlagSet(void) const { return (mFlags & kOnLinkFlagMask) != 0; } /** * This method sets the on-link (L) flag. * */ void SetOnLinkFlag(void) { mFlags |= kOnLinkFlagMask; } /** * This method clears the on-link (L) flag. * */ void ClearOnLinkFlag(void) { mFlags &= ~kOnLinkFlagMask; } /** * This method indicates whether or not the autonomous address-configuration (A) flag is set. * * @retval TRUE The auto address-config flag is set. * @retval FALSE The auto address-config flag is not set. * */ bool IsAutoAddrConfigFlagSet(void) const { return (mFlags & kAutoConfigFlagMask) != 0; } /** * This method sets the autonomous address-configuration (A) flag. * */ void SetAutoAddrConfigFlag(void) { mFlags |= kAutoConfigFlagMask; } /** * This method clears the autonomous address-configuration (A) flag. * */ void ClearAutoAddrConfigFlag(void) { mFlags &= ~kAutoConfigFlagMask; } /** * This method sets the valid lifetime of the prefix in seconds. * * @param[in] aValidLifetime The valid lifetime in seconds. * */ void SetValidLifetime(uint32_t aValidLifetime) { mValidLifetime = HostSwap32(aValidLifetime); } /** * THis method gets the valid lifetime of the prefix in seconds. * * @returns The valid lifetime in seconds. * */ uint32_t GetValidLifetime(void) const { return HostSwap32(mValidLifetime); } /** * This method sets the preferred lifetime of the prefix in seconds. * * @param[in] aPreferredLifetime The preferred lifetime in seconds. * */ void SetPreferredLifetime(uint32_t aPreferredLifetime) { mPreferredLifetime = HostSwap32(aPreferredLifetime); } /** * THis method returns the preferred lifetime of the prefix in seconds. * * @returns The preferred lifetime in seconds. * */ uint32_t GetPreferredLifetime(void) const { return HostSwap32(mPreferredLifetime); } /** * This method sets the prefix. * * @param[in] aPrefix The prefix contained in this option. * */ void SetPrefix(const Prefix &aPrefix); /** * This method gets the prefix in this option. * * @param[out] aPrefix Reference to a `Prefix` to return the prefix. * */ void GetPrefix(Prefix &aPrefix) const; /** * This method indicates whether or not the option is valid. * * @retval TRUE The option is valid * @retval FALSE The option is not valid. * */ bool IsValid(void) const; PrefixInfoOption(void) = delete; private: // Prefix Information Option // // 0 1 2 3 // 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 // +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ // | Type | Length | Prefix Length |L|A| Reserved1 | // +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ // | Valid Lifetime | // +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ // | Preferred Lifetime | // +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ // | Reserved2 | // +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ // | | // + + // | | // + Prefix + // | | // + + // | | // +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ static constexpr uint8_t kAutoConfigFlagMask = 0x40; // Autonomous address-configuration flag. static constexpr uint8_t kOnLinkFlagMask = 0x80; // On-link flag. uint8_t mPrefixLength; // The prefix length in bits. uint8_t mFlags; // The flags field. uint32_t mValidLifetime; // The valid lifetime of the prefix. uint32_t mPreferredLifetime; // The preferred lifetime of the prefix. uint32_t mReserved2; // The reserved field. Address mPrefix; // The prefix. } OT_TOOL_PACKED_END; static_assert(sizeof(PrefixInfoOption) == 32, "invalid PrefixInfoOption structure"); /** * This class represents the Route Information Option. * * See section 2.3 of RFC 4191 for definition of this option. [https://tools.ietf.org/html/rfc4191#section-2.3] * */ OT_TOOL_PACKED_BEGIN class RouteInfoOption : public Option, private Clearable { public: static constexpr uint16_t kMinSize = kLengthUnit; ///< Minimum size (in bytes) of a Route Info Option static constexpr Type kType = kTypeRouteInfo; ///< Route Information Option Type. /** * This method initializes the option setting the type and clearing (setting to zero) all other fields. * */ void Init(void); /** * This method sets the route preference. * * @param[in] aPreference The route preference. * */ void SetPreference(RoutePreference aPreference); /** * This method gets the route preference. * * @returns The route preference. * */ RoutePreference GetPreference(void) const; /** * This method sets the lifetime of the route in seconds. * * @param[in] aLifetime The lifetime of the route in seconds. * */ void SetRouteLifetime(uint32_t aLifetime) { mRouteLifetime = HostSwap32(aLifetime); } /** * This method gets Route Lifetime in seconds. * * @returns The Route Lifetime in seconds. * */ uint32_t GetRouteLifetime(void) const { return HostSwap32(mRouteLifetime); } /** * This method sets the prefix and adjusts the option length based on the prefix length. * * @param[in] aPrefix The prefix contained in this option. * */ void SetPrefix(const Prefix &aPrefix); /** * This method gets the prefix in this option. * * @param[out] aPrefix Reference to a `Prefix` to return the prefix. * */ void GetPrefix(Prefix &aPrefix) const; /** * This method tells whether this option is valid. * * @returns A boolean indicates whether this option is valid. * */ bool IsValid(void) const; /** * This static method calculates the minimum option length for a given prefix length. * * The option length (which is in unit of 8 octets) can be 1, 2, or 3 depending on the prefix length. It can be 1 * for a zero prefix length, 2 if the prefix length is not greater than 64, and 3 otherwise. * * @param[in] aPrefixLength The prefix length (in bits). * * @returns The option length (in unit of 8 octet) for @p aPrefixLength. * */ static uint8_t OptionLengthForPrefix(uint8_t aPrefixLength); /** * This static method calculates the minimum option size (in bytes) for a given prefix length. * * @param[in] aPrefixLength The prefix length (in bits). * * @returns The option size (in bytes) for @p aPrefixLength. * */ static uint16_t OptionSizeForPrefix(uint8_t aPrefixLength) { return kLengthUnit * OptionLengthForPrefix(aPrefixLength); } RouteInfoOption(void) = delete; private: // Route Information Option // // 0 1 2 3 // 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 // +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ // | Type | Length | Prefix Length |Resvd|Prf|Resvd| // +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ // | Route Lifetime | // +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ // | Prefix (Variable Length) | // . . // . . // +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ static constexpr uint8_t kPreferenceOffset = 3; static constexpr uint8_t kPreferenceMask = 3 << kPreferenceOffset; uint8_t * GetPrefixBytes(void) { return AsNonConst(AsConst(this)->GetPrefixBytes()); } const uint8_t *GetPrefixBytes(void) const { return reinterpret_cast(this) + sizeof(*this); } uint8_t mPrefixLength; // The prefix length in bits. uint8_t mResvdPrf; // The preference. uint32_t mRouteLifetime; // The lifetime in seconds. // Followed by prefix bytes (variable length). } OT_TOOL_PACKED_END; static_assert(sizeof(RouteInfoOption) == 8, "invalid RouteInfoOption structure"); /** * This class represents a Router Advertisement message. * */ class RouterAdvertMessage { public: /** * This class implements the RA message header. * * See section 2.2 of RFC 4191 [https://datatracker.ietf.org/doc/html/rfc4191] * */ OT_TOOL_PACKED_BEGIN class Header : public Equatable
, private Clearable
{ public: /** * This constructor initializes the Router Advertisement message with * zero router lifetime, reachable time and retransmission timer. * */ Header(void) { SetToDefault(); } /** * This method sets the RA message to default values. * */ void SetToDefault(void); /** * This method sets the checksum value. * * @param[in] aChecksum The checksum value. * */ void SetChecksum(uint16_t aChecksum) { mChecksum = HostSwap16(aChecksum); } /** * This method sets the Router Lifetime in seconds. * * @param[in] aRouterLifetime The router lifetime in seconds. * */ void SetRouterLifetime(uint16_t aRouterLifetime) { mRouterLifetime = HostSwap16(aRouterLifetime); } /** * This method gets the Router Lifetime (in seconds). * * Router Lifetime set to zero indicates that the sender is not a default router. * * @returns The router lifetime in seconds. * */ uint16_t GetRouterLifetime(void) const { return HostSwap16(mRouterLifetime); } /** * This method sets the default router preference. * * @param[in] aPreference The router preference. * */ void SetDefaultRouterPreference(RoutePreference aPreference); /** * This method gets the default router preference. * * @returns The router preference. * */ RoutePreference GetDefaultRouterPreference(void) const; private: // Router Advertisement Message // // 0 1 2 3 // 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 // +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ // | Type | Code | Checksum | // +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ // | Cur Hop Limit |M|O|H|Prf|Resvd| Router Lifetime | // +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ // | Reachable Time | // +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ // | Retrans Timer | // +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ // | Options ... // +-+-+-+-+-+-+-+-+-+-+-+- static constexpr uint8_t kPreferenceOffset = 3; static constexpr uint8_t kPreferenceMask = 3 << kPreferenceOffset; uint8_t mType; uint8_t mCode; uint16_t mChecksum; uint8_t mCurHopLimit; uint8_t mFlags; uint16_t mRouterLifetime; uint32_t mReachableTime; uint32_t mRetransTimer; } OT_TOOL_PACKED_END; static_assert(sizeof(Header) == 16, "Invalid RA `Header`"); typedef Data Icmp6Packet; ///< A data buffer containing an ICMPv6 packet. /** * This constructor initializes the RA message from a received packet data buffer. * * @param[in] aPacket A received packet data. * */ explicit RouterAdvertMessage(const Icmp6Packet &aPacket) : mData(aPacket) , mMaxLength(0) { } /** * This template constructor initializes the RA message with a given header using a given buffer to store the RA * message. * * @tparam kBufferSize The size of the buffer used to store the RA message. * * @param[in] aHeader The RA message header. * @param[in] aBuffer The data buffer to store the RA message in. * */ template RouterAdvertMessage(const Header &aHeader, uint8_t (&aBuffer)[kBufferSize]) : mMaxLength(kBufferSize) { static_assert(kBufferSize >= sizeof(Header), "Buffer for RA msg is too small"); memcpy(aBuffer, &aHeader, sizeof(Header)); mData.Init(aBuffer, sizeof(Header)); } /** * This method gets the RA message as an `Icmp6Packet`. * * @returns The RA message as an `Icmp6Packet`. * */ const Icmp6Packet &GetAsPacket(void) const { return mData; } /** * This method indicates whether or not the RA message is valid. * * @retval TRUE If the RA message is valid. * @retval FALSE If the RA message is not valid. * */ bool IsValid(void) const { return (mData.GetBytes() != nullptr) && (mData.GetLength() >= sizeof(Header)); } /** * This method gets the RA message's header. * * @returns The RA message's header. * */ const Header &GetHeader(void) const { return *reinterpret_cast(mData.GetBytes()); } /** * This method appends a Prefix Info Option to the RA message. * * The appended Prefix Info Option will have both on-link (L) and autonomous address-configuration (A) flags set. * * @param[in] aPrefix The prefix. * @param[in] aValidLifetime The valid lifetime in seconds. * @param[in] aPreferredLifetime The preferred lifetime in seconds. * * @retval kErrorNone Option is appended successfully. * @retval kErrorNoBufs No more space in the buffer to append the option. * */ Error AppendPrefixInfoOption(const Prefix &aPrefix, uint32_t aValidLifetime, uint32_t aPreferredLifetime); /** * This method appends a Route Info Option to the RA message. * * @param[in] aPrefix The prefix. * @param[in] aRouteLifetime The route lifetime in seconds. * @param[in] aPreference The route preference. * * @retval kErrorNone Option is appended successfully. * @retval kErrorNoBufs No more space in the buffer to append the option. * */ Error AppendRouteInfoOption(const Prefix &aPrefix, uint32_t aRouteLifetime, RoutePreference aPreference); /** * This method indicates whether or not the RA message contains any options. * * @retval TRUE If the RA message contains at least one option. * @retval FALSE If the RA message contains no options. * */ bool ContainsAnyOptions(void) const { return (mData.GetLength() > sizeof(Header)); } // The following methods are intended to support range-based `for` // loop iteration over `Option`s in the RA message. Option::Iterator begin(void) const { return Option::Iterator(GetOptionStart(), GetDataEnd()); } Option::Iterator end(void) const { return Option::Iterator(); } private: const uint8_t *GetOptionStart(void) const { return (mData.GetBytes() + sizeof(Header)); } const uint8_t *GetDataEnd(void) const { return mData.GetBytes() + mData.GetLength(); } Option * AppendOption(uint16_t aOptionSize); Data mData; uint16_t mMaxLength; }; /** * This class implements the Router Solicitation message. * * See section 4.1 of RFC 4861 for definition of this message. * https://tools.ietf.org/html/rfc4861#section-4.1 * */ OT_TOOL_PACKED_BEGIN class RouterSolicitMessage { public: /** * This constructor initializes the Router Solicitation message. * */ RouterSolicitMessage(void); private: Icmp::Header mHeader; // The common ICMPv6 header. } OT_TOOL_PACKED_END; static_assert(sizeof(RouterSolicitMessage) == 8, "invalid RouterSolicitMessage structure"); } // namespace Nd } // namespace Ip6 } // namespace ot #endif // ND6_HPP_