diff --git a/include/openthread/instance.h b/include/openthread/instance.h
index a5f01c0db..ec252aa40 100644
--- a/include/openthread/instance.h
+++ b/include/openthread/instance.h
@@ -53,7 +53,7 @@ extern "C" {
* @note This number versions both OpenThread platform and user APIs.
*
*/
-#define OPENTHREAD_API_VERSION (316)
+#define OPENTHREAD_API_VERSION (317)
/**
* @addtogroup api-instance
diff --git a/include/openthread/ip6.h b/include/openthread/ip6.h
index 5c38e3121..c04bdc2ca 100644
--- a/include/openthread/ip6.h
+++ b/include/openthread/ip6.h
@@ -610,12 +610,27 @@ bool otIp6ArePrefixesEqual(const otIp6Prefix *aFirst, const otIp6Prefix *aSecond
* @param[in] aString A pointer to a NULL-terminated string.
* @param[out] aAddress A pointer to an IPv6 address.
*
- * @retval OT_ERROR_NONE Successfully parsed the string.
- * @retval OT_ERROR_INVALID_ARGS Failed to parse the string.
+ * @retval OT_ERROR_NONE Successfully parsed @p aString and updated @p aAddress.
+ * @retval OT_ERROR_PARSE Failed to parse @p aString as an IPv6 address.
*
*/
otError otIp6AddressFromString(const char *aString, otIp6Address *aAddress);
+/**
+ * This function converts a human-readable IPv6 prefix string into a binary representation.
+ *
+ * The @p aString parameter should be a string in the format "
/", where `` is an IPv6
+ * address and `` is a prefix length.
+ *
+ * @param[in] aString A pointer to a NULL-terminated string.
+ * @param[out] aPrefix A pointer to an IPv6 prefix.
+ *
+ * @retval OT_ERROR_NONE Successfully parsed the string as an IPv6 prefix and updated @p aPrefix.
+ * @retval OT_ERROR_PARSE Failed to parse @p aString as an IPv6 prefix.
+ *
+ */
+otError otIp6PrefixFromString(const char *aString, otIp6Prefix *aPrefix);
+
#define OT_IP6_ADDRESS_STRING_SIZE 40 ///< Recommended size for string representation of an IPv6 address.
/**
diff --git a/src/core/api/ip6_api.cpp b/src/core/api/ip6_api.cpp
index 18769f7d0..98da9489e 100644
--- a/src/core/api/ip6_api.cpp
+++ b/src/core/api/ip6_api.cpp
@@ -188,6 +188,11 @@ otError otIp6AddressFromString(const char *aString, otIp6Address *aAddress)
return AsCoreType(aAddress).FromString(aString);
}
+otError otIp6PrefixFromString(const char *aString, otIp6Prefix *aPrefix)
+{
+ return AsCoreType(aPrefix).FromString(aString);
+}
+
void otIp6AddressToString(const otIp6Address *aAddress, char *aBuffer, uint16_t aSize)
{
AssertPointerIsNotNull(aBuffer);
diff --git a/src/core/net/ip6_address.cpp b/src/core/net/ip6_address.cpp
index 38b941963..710496eb9 100644
--- a/src/core/net/ip6_address.cpp
+++ b/src/core/net/ip6_address.cpp
@@ -157,6 +157,39 @@ bool Prefix::IsValidNat64PrefixLength(uint8_t aLength)
(aLength == 96);
}
+Error Prefix::FromString(const char *aString)
+{
+ constexpr char kSlashChar = '/';
+ constexpr char kNullChar = '\0';
+
+ Error error = kErrorParse;
+ const char *slashPosition;
+ uint16_t plen = 0;
+
+ VerifyOrExit(aString != nullptr);
+
+ slashPosition = StringFind(aString, kSlashChar);
+ VerifyOrExit(slashPosition != nullptr);
+
+ SuccessOrExit(AsCoreType(&mPrefix).ParseFrom(aString, kSlashChar));
+
+ VerifyOrExit(slashPosition[1] != kNullChar);
+
+ for (const char *cur = slashPosition + 1; *cur != kNullChar; cur++)
+ {
+ VerifyOrExit((*cur >= '0') && (*cur <= '9'));
+ plen *= 10;
+ plen += static_cast(*cur - '0');
+ VerifyOrExit(plen <= kMaxLength);
+ }
+
+ SetLength(static_cast(plen));
+ error = kErrorNone;
+
+exit:
+ return error;
+}
+
Prefix::InfoString Prefix::ToString(void) const
{
InfoString string;
@@ -491,11 +524,17 @@ void Address::SynthesizeFromIp4Address(const Prefix &aPrefix, const Ip4::Address
}
Error Address::FromString(const char *aString)
+{
+ constexpr char kNullChar = '\0';
+
+ return ParseFrom(aString, kNullChar);
+}
+
+Error Address::ParseFrom(const char *aString, char aTerminatorChar)
{
constexpr uint8_t kInvalidIndex = 0xff;
constexpr char kColonChar = ':';
constexpr char kDotChar = '.';
- constexpr char kNullChar = '\0';
Error error = kErrorParse;
uint8_t index = 0;
@@ -513,7 +552,7 @@ Error Address::FromString(const char *aString)
colonIndex = index;
}
- while (*aString != kNullChar)
+ while (*aString != aTerminatorChar)
{
const char *start = aString;
uint32_t value = 0;
@@ -560,7 +599,7 @@ Error Address::FromString(const char *aString)
break;
}
- VerifyOrExit((*aString == kColonChar) || (*aString == kNullChar));
+ VerifyOrExit((*aString == kColonChar) || (*aString == aTerminatorChar));
VerifyOrExit(index < endIndex);
mFields.m16[index++] = HostSwap16(static_cast(value));
@@ -594,7 +633,7 @@ Error Address::FromString(const char *aString)
{
Ip4::Address ip4Addr;
- SuccessOrExit(error = ip4Addr.FromString(aString));
+ SuccessOrExit(error = ip4Addr.FromString(aString, aTerminatorChar));
memcpy(GetArrayEnd(mFields.m8) - Ip4::Address::kSize, ip4Addr.GetBytes(), Ip4::Address::kSize);
}
diff --git a/src/core/net/ip6_address.hpp b/src/core/net/ip6_address.hpp
index 25df8f889..3323690fb 100644
--- a/src/core/net/ip6_address.hpp
+++ b/src/core/net/ip6_address.hpp
@@ -315,6 +315,17 @@ public:
*/
bool IsValidNat64(void) const { return IsValidNat64PrefixLength(mLength); }
+ /**
+ * This method parses a given IPv6 prefix string and sets the prefix.
+ *
+ * @param[in] aString A null-terminated string, with format "/"
+ *
+ * @retval kErrorNone Successfully parsed the IPv6 prefix from @p aString.
+ * @retval kErrorParse Failed to parse the IPv6 prefix from @p aString.
+ *
+ */
+ Error FromString(const char *aString);
+
/**
* This method converts the prefix to a string.
*
@@ -1028,6 +1039,8 @@ private:
static void CopyBits(uint8_t *aDst, const uint8_t *aSrc, uint8_t aNumBits);
+ Error ParseFrom(const char *aString, char aTerminatorChar);
+
} OT_TOOL_PACKED_END;
/**
diff --git a/src/core/utils/parse_cmdline.cpp b/src/core/utils/parse_cmdline.cpp
index 5261ce0b5..3f30c8337 100644
--- a/src/core/utils/parse_cmdline.cpp
+++ b/src/core/utils/parse_cmdline.cpp
@@ -250,30 +250,7 @@ Error ParseAsIp4Address(const char *aString, otIp4Address &aAddress)
Error ParseAsIp6Prefix(const char *aString, otIp6Prefix &aPrefix)
{
- enum : uint8_t
- {
- kMaxIp6AddressStringSize = 45,
- };
-
- Error error = kErrorInvalidArgs;
- char string[kMaxIp6AddressStringSize];
- const char *prefixLengthStr;
-
- VerifyOrExit(aString != nullptr);
-
- prefixLengthStr = StringFind(aString, '/');
- VerifyOrExit(prefixLengthStr != nullptr);
-
- VerifyOrExit(prefixLengthStr - aString < static_cast(sizeof(string)));
-
- memcpy(string, aString, static_cast(prefixLengthStr - aString));
- string[prefixLengthStr - aString] = '\0';
-
- SuccessOrExit(static_cast(aPrefix.mPrefix).FromString(string));
- error = ParseAsUint8(prefixLengthStr + 1, aPrefix.mLength);
-
-exit:
- return error;
+ return (aString != nullptr) ? otIp6PrefixFromString(aString, &aPrefix) : kErrorInvalidArgs;
}
#endif // #if OPENTHREAD_FTD || OPENTHREAD_MTD
diff --git a/tests/unit/test_ip_address.cpp b/tests/unit/test_ip_address.cpp
index c19b7d2ec..96a2a70f7 100644
--- a/tests/unit/test_ip_address.cpp
+++ b/tests/unit/test_ip_address.cpp
@@ -29,6 +29,7 @@
#include
#include "common/encoding.hpp"
+#include "common/string.hpp"
#include "net/ip4_types.hpp"
#include "net/ip6_address.hpp"
@@ -166,6 +167,56 @@ void TestIp6AddressFromString(void)
{
checkAddressFromString(&testVector);
}
+
+ // Validate parsing all test vectors now as an IPv6 prefix.
+
+ for (Ip6AddressTestVector &testVector : testVectors)
+ {
+ constexpr uint16_t kMaxString = 80;
+
+ ot::Ip6::Prefix prefix;
+ char string[kMaxString];
+ uint16_t length;
+
+ length = ot::StringLength(testVector.mString, kMaxString);
+ memcpy(string, testVector.mString, length);
+ VerifyOrQuit(length + sizeof("/128") <= kMaxString);
+ strcpy(&string[length], "/128");
+
+ printf("%s\n", string);
+
+ VerifyOrQuit(prefix.FromString(string) == testVector.mError);
+
+ if (testVector.mError == ot::kErrorNone)
+ {
+ VerifyOrQuit(memcmp(prefix.GetBytes(), testVector.mAddr, sizeof(ot::Ip6::Address)) == 0);
+ VerifyOrQuit(prefix.GetLength() == 128);
+ }
+ }
+}
+
+void TestIp6PrefixFromString(void)
+{
+ ot::Ip6::Prefix prefix;
+
+ SuccessOrQuit(prefix.FromString("::/128"));
+ VerifyOrQuit(prefix.GetLength() == 128);
+
+ SuccessOrQuit(prefix.FromString("::/0128"));
+ VerifyOrQuit(prefix.GetLength() == 128);
+
+ SuccessOrQuit(prefix.FromString("::/5"));
+ VerifyOrQuit(prefix.GetLength() == 5);
+
+ SuccessOrQuit(prefix.FromString("::/0"));
+ VerifyOrQuit(prefix.GetLength() == 0);
+
+ VerifyOrQuit(prefix.FromString("::") == ot::kErrorParse);
+ VerifyOrQuit(prefix.FromString("::/") == ot::kErrorParse);
+ VerifyOrQuit(prefix.FromString("::/129") == ot::kErrorParse);
+ VerifyOrQuit(prefix.FromString(":: /12") == ot::kErrorParse);
+ VerifyOrQuit(prefix.FromString("::/a1") == ot::kErrorParse);
+ VerifyOrQuit(prefix.FromString("::/12 ") == ot::kErrorParse);
}
void TestIp4AddressFromString(void)
@@ -651,6 +702,7 @@ int main(void)
TestIp6AddressSetPrefix();
TestIp4AddressFromString();
TestIp6AddressFromString();
+ TestIp6PrefixFromString();
TestIp6Prefix();
TestIp4Ip6Translation();
TestIp4Cidr();