diff --git a/src/core/net/ip6_address.cpp b/src/core/net/ip6_address.cpp index 7f50c941c..b83b2adc5 100644 --- a/src/core/net/ip6_address.cpp +++ b/src/core/net/ip6_address.cpp @@ -254,6 +254,15 @@ bool InterfaceIdentifier::IsAnycastServiceLocator(void) const return (IsLocator() && (locator >= Mle::kAloc16ServiceStart) && (locator <= Mle::kAloc16ServiceEnd)); } +void InterfaceIdentifier::ApplyPrefix(const Prefix &aPrefix) +{ + if (aPrefix.GetLength() > NetworkPrefix::kLength) + { + Address::CopyBits(mFields.m8, aPrefix.GetBytes() + NetworkPrefix::kSize, + aPrefix.GetLength() - NetworkPrefix::kLength); + } +} + InterfaceIdentifier::InfoString InterfaceIdentifier::ToString(void) const { InfoString string; @@ -377,35 +386,36 @@ void Address::SetPrefix(const NetworkPrefix &aNetworkPrefix) void Address::SetPrefix(const Prefix &aPrefix) { - SetPrefix(0, aPrefix.GetBytes(), aPrefix.GetLength()); + CopyBits(mFields.m8, aPrefix.GetBytes(), aPrefix.GetLength()); } -void Address::SetPrefix(uint8_t aOffset, const uint8_t *aPrefix, uint8_t aPrefixLength) +void Address::CopyBits(uint8_t *aDst, const uint8_t *aSrc, uint8_t aNumBits) { - uint8_t bytes = aPrefixLength / CHAR_BIT; - uint8_t extraBits = aPrefixLength % CHAR_BIT; + // This method copies `aNumBits` from `aSrc` into `aDst` handling + // the case where `aNumBits` may not be a multiple of 8. It leaves the + // remaining bits beyond `aNumBits` in `aDst` unchanged. - OT_ASSERT(aPrefixLength <= (sizeof(Address) - aOffset) * CHAR_BIT); + uint8_t numBytes = aNumBits / CHAR_BIT; + uint8_t extraBits = aNumBits % CHAR_BIT; - memcpy(mFields.m8 + aOffset, aPrefix, bytes); + memcpy(aDst, aSrc, numBytes); if (extraBits > 0) { - uint8_t index = aOffset + bytes; - uint8_t mask = ((0x80 >> (extraBits - 1)) - 1); + uint8_t mask = ((0x80 >> (extraBits - 1)) - 1); // `mask` has its higher (msb) `extraBits` bits as `0` and the remaining as `1`. // Example with `extraBits` = 3: // ((0x80 >> 2) - 1) = (0b0010_0000 - 1) = 0b0001_1111 - mFields.m8[index] &= mask; - mFields.m8[index] |= (aPrefix[index] & ~mask); + aDst[numBytes] &= mask; + aDst[numBytes] |= (aSrc[numBytes] & ~mask); } } void Address::SetMulticastNetworkPrefix(const uint8_t *aPrefix, uint8_t aPrefixLength) { - SetPrefix(kMulticastNetworkPrefixOffset, aPrefix, aPrefixLength); + CopyBits(&mFields.m8[kMulticastNetworkPrefixOffset], aPrefix, aPrefixLength); mFields.m8[kMulticastNetworkPrefixLengthOffset] = aPrefixLength; } diff --git a/src/core/net/ip6_address.hpp b/src/core/net/ip6_address.hpp index 064e282b6..9ac191b68 100644 --- a/src/core/net/ip6_address.hpp +++ b/src/core/net/ip6_address.hpp @@ -539,6 +539,17 @@ public: */ void SetLocator(uint16_t aLocator) { mFields.m16[3] = HostSwap16(aLocator); } + /** + * This method applies a prefix to IID. + * + * If the prefix length is longer than 64 bits, the prefix bits after 64 are written into the IID. This method only + * changes the bits in IID up the prefix length and keeps the rest of the bits in IID as before. + * + * @param[in] aPrefix An IPv6 prefix. + * + */ + void ApplyPrefix(const Prefix &aPrefix); + /** * This method converts an Interface Identifier to a string. * @@ -561,6 +572,7 @@ OT_TOOL_PACKED_BEGIN class Address : public otIp6Address, public Equatable
, public Clearable
{ friend class Prefix; + friend class InterfaceIdentifier; public: static constexpr uint8_t kAloc16Mask = InterfaceIdentifier::kAloc16Mask; ///< The mask for ALOC16. @@ -839,7 +851,7 @@ public: * @param[in] aPrefixLength The prefix length (in bits). * */ - void SetPrefix(const uint8_t *aPrefix, uint8_t aPrefixLength) { SetPrefix(0, aPrefix, aPrefixLength); } + void SetPrefix(const uint8_t *aPrefix, uint8_t aPrefixLength) { CopyBits(mFields.m8, aPrefix, aPrefixLength); } /** * This method sets the IPv6 address prefix to the given Network Prefix. @@ -1007,7 +1019,9 @@ public: bool operator<(const Address &aOther) const { return memcmp(mFields.m8, aOther.mFields.m8, sizeof(Address)) < 0; } private: - void SetPrefix(uint8_t aOffset, const uint8_t *aPrefix, uint8_t aPrefixLength); + static constexpr uint8_t kMulticastNetworkPrefixLengthOffset = 3; // Prefix-Based Multicast Address (RFC3306) + static constexpr uint8_t kMulticastNetworkPrefixOffset = 4; // Prefix-Based Multicast Address (RFC3306) + void SetToLocator(const NetworkPrefix &aNetworkPrefix, uint16_t aLocator); void ToString(StringWriter &aWriter) const; void AppendHexWords(StringWriter &aWriter, uint8_t aLength) const; @@ -1018,8 +1032,8 @@ private: static const Address &GetRealmLocalAllRoutersMulticast(void); static const Address &GetRealmLocalAllMplForwarders(void); - static constexpr uint8_t kMulticastNetworkPrefixLengthOffset = 3; // Prefix-Based Multicast Address (RFC3306). - static constexpr uint8_t kMulticastNetworkPrefixOffset = 4; // Prefix-Based Multicast Address (RFC3306). + static void CopyBits(uint8_t *aDst, const uint8_t *aSrc, uint8_t aNumBits); + } OT_TOOL_PACKED_END; /** diff --git a/tests/unit/test_ip_address.cpp b/tests/unit/test_ip_address.cpp index b25cef9b6..945dec091 100644 --- a/tests/unit/test_ip_address.cpp +++ b/tests/unit/test_ip_address.cpp @@ -215,6 +215,27 @@ bool CheckPrefix(const ot::Ip6::Address &aAddress, const uint8_t *aPrefix, uint8 return matches; } +bool CheckPrefixInIid(const ot::Ip6::InterfaceIdentifier &aIid, const uint8_t *aPrefix, uint8_t aPrefixLength) +{ + // Check the IID to contain the prefix bits (applicable when prefix length is longer than 64). + + bool matches = true; + + for (uint8_t bit = 64; bit < aPrefixLength; bit++) + { + uint8_t index = bit / CHAR_BIT; + uint8_t mask = (0x80 >> (bit % CHAR_BIT)); + + if ((aIid.mFields.m8[index - 8] & mask) != (aPrefix[index] & mask)) + { + matches = false; + break; + } + } + + return matches; +} + bool CheckInterfaceId(const ot::Ip6::Address &aAddress1, const ot::Ip6::Address &aAddress2, uint8_t aPrefixLength) { // Check whether all the bits after aPrefixLength of the two given IPv6 Address match or not. @@ -248,6 +269,7 @@ void TestIp6AddressSetPrefix(void) ot::Ip6::Address address; ot::Ip6::Address allZeroAddress; ot::Ip6::Address allOneAddress; + ot::Ip6::Prefix ip6Prefix; allZeroAddress.Clear(); memset(&allOneAddress, 0xff, sizeof(allOneAddress)); @@ -259,18 +281,33 @@ void TestIp6AddressSetPrefix(void) for (uint8_t prefixLength = 0; prefixLength <= sizeof(ot::Ip6::Address) * CHAR_BIT; prefixLength++) { + ip6Prefix.Clear(); + ip6Prefix.Set(prefix, prefixLength); + address = allZeroAddress; - address.SetPrefix(prefix, prefixLength); + address.SetPrefix(ip6Prefix); printf(" prefix-len:%-3d --> %s\n", prefixLength, address.ToString().AsCString()); VerifyOrQuit(CheckPrefix(address, prefix, prefixLength), "Prefix does not match after SetPrefix()"); VerifyOrQuit(CheckInterfaceId(address, allZeroAddress, prefixLength), "SetPrefix changed bits beyond the prefix length"); address = allOneAddress; - address.SetPrefix(prefix, prefixLength); + address.SetPrefix(ip6Prefix); VerifyOrQuit(CheckPrefix(address, prefix, prefixLength), "Prefix does not match after SetPrefix()"); VerifyOrQuit(CheckInterfaceId(address, allOneAddress, prefixLength), "SetPrefix changed bits beyond the prefix length"); + + address = allZeroAddress; + address.GetIid().ApplyPrefix(ip6Prefix); + VerifyOrQuit(CheckPrefixInIid(address.GetIid(), prefix, prefixLength), "IID is not correct"); + VerifyOrQuit(CheckInterfaceId(address, allZeroAddress, prefixLength), + "Iid:ApplyPrefrix() changed bits beyond the prefix length"); + + address = allOneAddress; + address.GetIid().ApplyPrefix(ip6Prefix); + VerifyOrQuit(CheckPrefixInIid(address.GetIid(), prefix, prefixLength), "IID is not correct"); + VerifyOrQuit(CheckInterfaceId(address, allOneAddress, prefixLength), + "Iid:ApplyPrefrix() changed bits beyond the prefix length"); } } }