[nat64] implement CLI functions for NAT64 (#8058)

This commit introduces `nat64` command and 4 new subcommands
(`configuredcidr`, `configuredprefix`, `mappings`, `counters`)

nat64 cidr -- Get the configured CIDR for NAT64 translator.
nat64 mappings -- Get the mappings of NAT64 translator.
nat64 counters -- Get the packet counters and error counters of NAT64
                  translator.

This commit also introduces related API for the above commands, and
`otIp4AddressToString` & `otIp4CidrToString` for the CLI to format the
IPv4 address and CIDR.
This commit is contained in:
Song GUO
2022-09-13 09:39:50 -07:00
committed by GitHub
parent 531339b65d
commit b66d521d1d
9 changed files with 843 additions and 12 deletions
+1 -1
View File
@@ -53,7 +53,7 @@ extern "C" {
* @note This number versions both OpenThread platform and user APIs.
*
*/
#define OPENTHREAD_API_VERSION (241)
#define OPENTHREAD_API_VERSION (242)
/**
* @addtogroup api-instance
+185 -1
View File
@@ -88,13 +88,151 @@ typedef struct otIp4Cidr
uint8_t mLength;
} otIp4Cidr;
/**
* Represents the counters for NAT64.
*
*/
typedef struct otNat64Counters
{
uint64_t m4To6Packets; ///< Number of packets translated from IPv4 to IPv6.
uint64_t m4To6Bytes; ///< Sum of size of packets translated from IPv4 to IPv6.
uint64_t m6To4Packets; ///< Number of packets translated from IPv6 to IPv4.
uint64_t m6To4Bytes; ///< Sum of size of packets translated from IPv6 to IPv4.
} otNat64Counters;
/**
* Represents the counters for the protocols supported by NAT64.
*
*/
typedef struct otNat64ProtocolCounters
{
otNat64Counters mTotal; ///< Counters for sum of all protocols.
otNat64Counters mIcmp; ///< Counters for ICMP and ICMPv6.
otNat64Counters mUdp; ///< Counters for UDP.
otNat64Counters mTcp; ///< Counters for TCP.
} otNat64ProtocolCounters;
/**
* Packet drop reasons.
*
*/
typedef enum otNat64DropReason
{
OT_NAT64_DROP_REASON_UNKNOWN = 0, ///< Packet drop for unknown reasons.
OT_NAT64_DROP_REASON_ILLEGAL_PACKET, ///< Packet drop due to failed to parse the datagram.
OT_NAT64_DROP_REASON_UNSUPPORTED_PROTO, ///< Packet drop due to unsupported IP protocol.
OT_NAT64_DROP_REASON_NO_MAPPING, ///< Packet drop due to no mappings found or mapping pool exhausted.
//---
OT_NAT64_DROP_REASON_COUNT,
} otNat64DropReason;
/**
* Represents the counters of dropped packets due to errors when handling NAT64 packets.
*
*/
typedef struct otNat64ErrorCounters
{
uint64_t mCount4To6[OT_NAT64_DROP_REASON_COUNT]; ///< Errors translating IPv4 packets.
uint64_t mCount6To4[OT_NAT64_DROP_REASON_COUNT]; ///< Errors translating IPv6 packets.
} otNat64ErrorCounters;
/**
* Gets NAT64 translator counters.
*
* The counter is counted since the instance initialized.
*
* Available when `OPENTHREAD_CONFIG_NAT64_TRANSLATOR_ENABLE` is enabled.
*
* @param[in] aInstance A pointer to an OpenThread instance.
* @param[out] aCounters A pointer to an `otNat64Counters` where the counters of NAT64 translator will be placed.
*
*/
void otNat64GetCounters(otInstance *aInstance, otNat64ProtocolCounters *aCounters);
/**
* Gets the NAT64 translator error counters.
*
* The counters are initialized to zero when the OpenThread instance is initialized.
*
* @param[in] aInstance A pointer to an OpenThread instance.
* @param[out] aCounters A pointer to an `otNat64Counters` where the counters of NAT64 translator will be placed.
*
*/
void otNat64GetErrorCounters(otInstance *aInstance, otNat64ErrorCounters *aCounters);
/**
* Represents an address mapping record for NAT64.
*
* @note The counters will be reset for each mapping session even for the same address pair. Applications can use `mId`
* to identify different sessions to calculate the packets correctly.
*
*/
typedef struct otNat64AddressMapping
{
uint64_t mId; ///< The unique id for a mapping session.
otIp4Address mIp4; ///< The IPv4 address of the mapping.
otIp6Address mIp6; ///< The IPv6 address of the mapping.
uint32_t mRemainingTimeMs; ///< Remaining time before expiry in milliseconds.
otNat64ProtocolCounters mCounters;
} otNat64AddressMapping;
/**
* Used to iterate through NAT64 address mappings.
*
* The fields in this type are opaque (intended for use by OpenThread core only) and therefore should not be
* accessed or used by caller.
*
* Before using an iterator, it MUST be initialized using `otNat64AddressMappingIteratorInit()`.
*
*/
typedef struct otNat64AddressMappingIterator
{
void *mPtr;
} otNat64AddressMappingIterator;
/**
* Initializes an `otNat64AddressMappingIterator`.
*
* An iterator MUST be initialized before it is used.
*
* An iterator can be initialized again to restart from the beginning of the mapping info.
*
* @param[in] aInstance The OpenThread instance.
* @param[out] aIterator A pointer to the iterator to initialize.
*
*/
void otNat64InitAddressMappingIterator(otInstance *aInstance, otNat64AddressMappingIterator *aIterator);
/**
* Gets the next AddressMapping info (using an iterator).
*
* Available when `OPENTHREAD_CONFIG_NAT64_TRANSLATOR_ENABLE` is enabled.
*
* @param[in] aInstance A pointer to an OpenThread instance.
* @param[in,out] aIterator A pointer to the iterator. On success the iterator will be updated to point to next
* NAT64 address mapping record. To get the first entry the iterator should be set to
* OT_NAT64_ADDRESS_MAPPING_ITERATOR_INIT.
* @param[out] aMapping A pointer to an `otNat64AddressMapping` where information of next NAT64 address
* mapping record is placed (on success).
*
* @retval OT_ERROR_NONE Successfully found the next NAT64 address mapping info (@p aMapping was successfully
* updated).
* @retval OT_ERROR_NOT_FOUND No subsequent NAT64 address mapping info was found.
*
*/
otError otNat64GetNextAddressMapping(otInstance * aInstance,
otNat64AddressMappingIterator *aIterator,
otNat64AddressMapping * aMapping);
/**
* Allocate a new message buffer for sending an IPv4 message to the NAT64 translator.
*
* Message buffers allocated by this function will have 20 bytes (difference between the size of IPv6 headers
* and IPv4 header sizes) reserved.
*
* This function is available only when `OPENTHREAD_CONFIG_NAT64_TRANSLATOR_ENABLE` is enabled.
* Available when `OPENTHREAD_CONFIG_NAT64_TRANSLATOR_ENABLE` is enabled.
*
* @note If @p aSettings is `NULL`, the link layer security is enabled and the message priority is set to
* OT_MESSAGE_PRIORITY_NORMAL by default.
@@ -174,6 +312,17 @@ typedef void (*otNat64ReceiveIp4Callback)(otMessage *aMessage, void *aContext);
*/
void otNat64SetReceiveIp4Callback(otInstance *aInstance, otNat64ReceiveIp4Callback aCallback, void *aContext);
/**
* Gets the IPv4 CIDR configured in the NAT64 translator.
*
* Available when `OPENTHREAD_CONFIG_NAT64_TRANSLATOR_ENABLE` is enabled.
*
* @param[in] aInstance A pointer to an OpenThread instance.
* @param[out] aCidr A pointer to an otIp4Cidr. Where the CIDR will be filled.
*
*/
otError otNat64GetCidr(otInstance *aInstance, otIp4Cidr *aCidr);
/**
* Test if two IPv4 addresses are the same.
*
@@ -200,6 +349,41 @@ bool otIp4IsAddressEqual(const otIp4Address *aFirst, const otIp4Address *aSecond
*/
void otIp4ExtractFromIp6Address(uint8_t aPrefixLength, const otIp6Address *aIp6Address, otIp4Address *aIp4Address);
#define OT_IP4_ADDRESS_STRING_SIZE 17 ///< Length of 000.000.000.000 plus a suffix NUL
/**
* Converts the address to a string.
*
* The string format uses quad-dotted notation of four bytes in the address (e.g., "127.0.0.1").
*
* If the resulting string does not fit in @p aBuffer (within its @p aSize characters), the string will be
* truncated but the outputted string is always null-terminated.
*
* @param[in] aAddress A pointer to an IPv4 address (MUST NOT be NULL).
* @param[out] aBuffer A pointer to a char array to output the string (MUST NOT be `nullptr`).
* @param[in] aSize The size of @p aBuffer (in bytes).
*
*/
void otIp4AddressToString(const otIp4Address *aAddress, char *aBuffer, uint16_t aSize);
#define OT_IP4_CIDR_STRING_SIZE 20 ///< Length of 000.000.000.000/00 plus a suffix NUL
/**
* Converts the IPv4 CIDR to a string.
*
* The string format uses quad-dotted notation of four bytes in the address with the length of prefix (e.g.,
* "127.0.0.1/32").
*
* If the resulting string does not fit in @p aBuffer (within its @p aSize characters), the string will be
* truncated but the outputted string is always null-terminated.
*
* @param[in] aCidr A pointer to an IPv4 CIDR (MUST NOT be NULL).
* @param[out] aBuffer A pointer to a char array to output the string (MUST NOT be `nullptr`).
* @param[in] aSize The size of @p aBuffer (in bytes).
*
*/
void otIp4CidrToString(const otIp4Cidr *aCidr, char *aBuffer, uint16_t aSize);
/**
* @}
*
+56
View File
@@ -70,6 +70,7 @@ Done
- [mlr](#mlr-reg-ipaddr--timeout)
- [mode](#mode)
- [multiradio](#multiradio)
- [nat64](#nat64-cidr)
- [neighbor](#neighbor-list)
- [netdata](README_NETDATA.md)
- [netstat](#netstat)
@@ -1835,6 +1836,61 @@ This command is only available when device supports more than one radio link.
Done
```
### nat64 cidr
Gets the IPv4 configured CIDR in the NAT64 translator.
This command is only available when device enables NAT64 translator.
```bash
> nat64 cidr
192.168.64.0/24
Done
```
### nat64 mappings
Get the NAT64 translator mappings.
This command is only available when device enables NAT64 translator.
```bash
> nat64 mappings
| | Address | | 4 to 6 | 6 to 4 |
+----------+---------------------------+--------+--------------+--------------+
| ID | IPv6 | IPv4 | Expiry | Pkts | Bytes | Pkts | Bytes |
+----------+------------+--------------+--------+------+-------+------+-------+
| 00021cb9 | fdc7::df79 | 192.168.64.2 | 7196s | 6 | 456 | 11 | 1928 |
| | TCP | 0 | 0 | 0 | 0 |
| | UDP | 1 | 136 | 16 | 1608 |
| | ICMP | 5 | 320 | 5 | 320 |
```
### nat64 counters
Get the NAT64 translator packet and error counters.
This command is only available when device enables NAT64 translator.
```bash
> nat64 counters
| | 4 to 6 | 6 to 4 |
+---------------+-------------------------+-------------------------+
| Protocol | Pkts | Bytes | Pkts | Bytes |
+---------------+----------+--------------+----------+--------------+
| Total | 11 | 704 | 11 | 704 |
| TCP | 0 | 0 | 0 | 0 |
| UDP | 0 | 0 | 0 | 0 |
| ICMP | 11 | 704 | 11 | 704 |
| Errors | Pkts | Pkts |
+---------------+-------------------------+-------------------------+
| Total | 8 | 4 |
| Illegal Pkt | 0 | 0 |
| Unsup Proto | 0 | 0 |
| No Mapping | 2 | 0 |
Done
```
### neighbor list
List RLOC16 of neighbors.
+213
View File
@@ -84,6 +84,9 @@
#if OPENTHREAD_CONFIG_RADIO_LINK_TREL_ENABLE
#include <openthread/trel.h>
#endif
#if OPENTHREAD_CONFIG_NAT64_TRANSLATOR_ENABLE
#include <openthread/nat64.h>
#endif
#include "common/new.hpp"
#include "common/string.hpp"
@@ -708,6 +711,213 @@ exit:
}
#endif // OPENTHREAD_CONFIG_BORDER_ROUTING_ENABLE
#if OPENTHREAD_CONFIG_NAT64_TRANSLATOR_ENABLE
template <> otError Interpreter::Process<Cmd("nat64")>(Arg aArgs[])
{
otError error = OT_ERROR_NONE;
if (aArgs[0].IsEmpty())
{
ExitNow(error = OT_ERROR_INVALID_COMMAND);
}
/**
* @cli nat64 cidr
* @code
* nat64 cidr
* 192.168.64.0/24
* Done
* @endcode
* @par api_copy
* #otNat64GetCidr
*
*/
else if (aArgs[0] == "cidr")
{
otIp4Cidr cidr;
char cidrString[OT_IP4_CIDR_STRING_SIZE];
SuccessOrExit(error = otNat64GetCidr(GetInstancePtr(), &cidr));
otIp4CidrToString(&cidr, cidrString, sizeof(cidrString));
OutputLine("%s", cidrString);
}
/**
* @cli nat64 mappings
* @code
* nat64 mappings
* | | Address | | 4 to 6 | 6 to 4 |
* +----------+---------------------------+--------+--------------+--------------+
* | ID | IPv6 | IPv4 | Expiry | Pkts | Bytes | Pkts | Bytes |
* +----------+------------+--------------+--------+------+-------+------+-------+
* | 00021cb9 | fdc7::df79 | 192.168.64.2 | 7196s | 6 | 456 | 11 | 1928 |
* | | TCP | 0 | 0 | 0 | 0 |
* | | UDP | 1 | 136 | 16 | 1608 |
* | | ICMP | 5 | 320 | 5 | 320 |
* @endcode
* @par api_copy
* #otNat64GetNextAddressMapping
*
*/
else if (aArgs[0] == "mappings")
{
otNat64AddressMappingIterator iterator;
otNat64AddressMapping mapping;
static const char *const kNat64StatusLevel1Title[] = {"", "Address", "", "4 to 6", "6 to 4"};
static const uint8_t kNat64StatusLevel1ColumnWidths[] = {
18, 61, 8, 25, 25,
};
static const char *const kNat64StatusTableHeader[] = {
"ID", "IPv6", "IPv4", "Expiry", "Pkts", "Bytes", "Pkts", "Bytes",
};
static const uint8_t kNat64StatusTableColumnWidths[] = {
18, 42, 18, 8, 10, 14, 10, 14,
};
OutputTableHeader(kNat64StatusLevel1Title, kNat64StatusLevel1ColumnWidths);
OutputTableHeader(kNat64StatusTableHeader, kNat64StatusTableColumnWidths);
otNat64InitAddressMappingIterator(GetInstancePtr(), &iterator);
while (otNat64GetNextAddressMapping(GetInstancePtr(), &iterator, &mapping) == OT_ERROR_NONE)
{
char ip4AddressString[OT_IP4_ADDRESS_STRING_SIZE];
char ip6AddressString[OT_IP6_PREFIX_STRING_SIZE];
otIp6AddressToString(&mapping.mIp6, ip6AddressString, sizeof(ip6AddressString));
otIp4AddressToString(&mapping.mIp4, ip4AddressString, sizeof(ip4AddressString));
OutputFormat("| %016llx ", mapping.mId);
OutputFormat("| %40s ", ip6AddressString);
OutputFormat("| %16s ", ip4AddressString);
OutputFormat("| %5llus ", mapping.mRemainingTimeMs / 1000);
OutputFormat("| %8llu ", mapping.mCounters.mTotal.m4To6Packets);
OutputFormat("| %12llu ", mapping.mCounters.mTotal.m4To6Bytes);
OutputFormat("| %8llu ", mapping.mCounters.mTotal.m6To4Packets);
OutputFormat("| %12llu ", mapping.mCounters.mTotal.m6To4Bytes);
OutputLine("|");
OutputFormat("| %016s ", "");
OutputFormat("| %68s ", "TCP");
OutputFormat("| %8llu ", mapping.mCounters.mTcp.m4To6Packets);
OutputFormat("| %12llu ", mapping.mCounters.mTcp.m4To6Bytes);
OutputFormat("| %8llu ", mapping.mCounters.mTcp.m6To4Packets);
OutputFormat("| %12llu ", mapping.mCounters.mTcp.m6To4Bytes);
OutputLine("|");
OutputFormat("| %016s ", "");
OutputFormat("| %68s ", "UDP");
OutputFormat("| %8llu ", mapping.mCounters.mUdp.m4To6Packets);
OutputFormat("| %12llu ", mapping.mCounters.mUdp.m4To6Bytes);
OutputFormat("| %8llu ", mapping.mCounters.mUdp.m6To4Packets);
OutputFormat("| %12llu ", mapping.mCounters.mUdp.m6To4Bytes);
OutputLine("|");
OutputFormat("| %016s ", "");
OutputFormat("| %68s ", "ICMP");
OutputFormat("| %8llu ", mapping.mCounters.mIcmp.m4To6Packets);
OutputFormat("| %12llu ", mapping.mCounters.mIcmp.m4To6Bytes);
OutputFormat("| %8llu ", mapping.mCounters.mIcmp.m6To4Packets);
OutputFormat("| %12llu ", mapping.mCounters.mIcmp.m6To4Bytes);
OutputLine("|");
}
}
/**
* @cli nat64 counters
* @code
* nat64 counters
* | | 4 to 6 | 6 to 4 |
* +---------------+-------------------------+-------------------------+
* | Protocol | Pkts | Bytes | Pkts | Bytes |
* +---------------+----------+--------------+----------+--------------+
* | Total | 11 | 704 | 11 | 704 |
* | TCP | 0 | 0 | 0 | 0 |
* | UDP | 0 | 0 | 0 | 0 |
* | ICMP | 11 | 704 | 11 | 704 |
* | Errors | Pkts | Pkts |
* +---------------+-------------------------+-------------------------+
* | Total | 8 | 4 |
* | Illegal Pkt | 0 | 0 |
* | Unsup Proto | 0 | 0 |
* | No Mapping | 2 | 0 |
* Done
* @endcode
* @par
* Gets the NAT64 translator packet and error counters.
* @par
* Available when `OPENTHREAD_CONFIG_NAT64_TRANSLATOR_ENABLE` is enabled.
* @sa otNat64GetCounters
* @sa otNat64GetErrorCounters
*
*/
else if (aArgs[0] == "counters")
{
static const char *const kNat64CounterTableHeader[] = {
"",
"4 to 6",
"6 to 4",
};
static const uint8_t kNat64CounterTableHeaderColumns[] = {15, 25, 25};
static const char *const kNat64CounterTableSubHeader[] = {
"Protocol", "Pkts", "Bytes", "Pkts", "Bytes",
};
static const uint8_t kNat64CounterTableSubHeaderColumns[] = {
15, 10, 14, 10, 14,
};
static const char *const kNat64CounterTableErrorSubHeader[] = {
"Errors",
"Pkts",
"Pkts",
};
static const uint8_t kNat64CounterTableErrorSubHeaderColumns[] = {
15,
25,
25,
};
static const char *const kNat64CounterErrorType[] = {
"Unknown",
"Illegal Pkt",
"Unsup Proto",
"No Mapping",
};
otNat64ProtocolCounters counters;
otNat64ErrorCounters errorCounters;
OutputTableHeader(kNat64CounterTableHeader, kNat64CounterTableHeaderColumns);
OutputTableHeader(kNat64CounterTableSubHeader, kNat64CounterTableSubHeaderColumns);
otNat64GetCounters(GetInstancePtr(), &counters);
otNat64GetErrorCounters(GetInstancePtr(), &errorCounters);
OutputLine("| %13s | %8llu | %12llu | %8llu | %12llu |", "Total", counters.mTotal.m4To6Packets,
counters.mTotal.m4To6Bytes, counters.mTotal.m6To4Packets, counters.mTotal.m6To4Bytes);
OutputLine("| %13s | %8llu | %12llu | %8llu | %12llu |", "TCP", counters.mTcp.m4To6Packets,
counters.mTcp.m4To6Bytes, counters.mTcp.m6To4Packets, counters.mTcp.m6To4Bytes);
OutputLine("| %13s | %8llu | %12llu | %8llu | %12llu |", "UDP", counters.mUdp.m4To6Packets,
counters.mUdp.m4To6Bytes, counters.mUdp.m6To4Packets, counters.mUdp.m6To4Bytes);
OutputLine("| %13s | %8llu | %12llu | %8llu | %12llu |", "ICMP", counters.mIcmp.m4To6Packets,
counters.mIcmp.m4To6Bytes, counters.mIcmp.m6To4Packets, counters.mIcmp.m6To4Bytes);
OutputTableHeader(kNat64CounterTableErrorSubHeader, kNat64CounterTableErrorSubHeaderColumns);
for (uint8_t i = 0; i < OT_NAT64_DROP_REASON_COUNT; i++)
{
OutputLine("| %13s | %23llu | %23llu |", kNat64CounterErrorType[i], errorCounters.mCount4To6[i],
errorCounters.mCount6To4[i]);
}
}
else
{
ExitNow(error = OT_ERROR_INVALID_COMMAND);
}
exit:
return error;
}
#endif // OPENTHREAD_CONFIG_NAT64_TRANSLATOR_ENABLE
#if (OPENTHREAD_CONFIG_THREAD_VERSION >= OT_THREAD_VERSION_1_2)
template <> otError Interpreter::Process<Cmd("bbr")>(Arg aArgs[])
{
@@ -6437,6 +6647,9 @@ otError Interpreter::ProcessCommand(Arg aArgs[])
#endif
CmdEntry("mode"),
CmdEntry("multiradio"),
#if OPENTHREAD_CONFIG_NAT64_TRANSLATOR_ENABLE
CmdEntry("nat64"),
#endif
#if OPENTHREAD_FTD
CmdEntry("neighbor"),
#endif
+46
View File
@@ -66,6 +66,38 @@ void otNat64SetReceiveIp4Callback(otInstance *aInstance, otNat64ReceiveIp4Callba
{
AsCoreType(aInstance).Get<Ip6::Ip6>().SetNat64ReceiveIp4DatagramCallback(aCallback, aContext);
}
void otNat64InitAddressMappingIterator(otInstance *aInstance, otNat64AddressMappingIterator *aIterator)
{
AssertPointerIsNotNull(aIterator);
AsCoreType(aInstance).Get<Nat64::Translator>().InitAddressMappingIterator(*aIterator);
}
otError otNat64GetNextAddressMapping(otInstance * aInstance,
otNat64AddressMappingIterator *aIterator,
otNat64AddressMapping * aMapping)
{
AssertPointerIsNotNull(aIterator);
AssertPointerIsNotNull(aMapping);
return AsCoreType(aInstance).Get<Nat64::Translator>().GetNextAddressMapping(*aIterator, *aMapping);
}
void otNat64GetCounters(otInstance *aInstance, otNat64ProtocolCounters *aCounters)
{
AsCoreType(aInstance).Get<Nat64::Translator>().GetCounters(AsCoreType(aCounters));
}
void otNat64GetErrorCounters(otInstance *aInstance, otNat64ErrorCounters *aCounters)
{
AsCoreType(aInstance).Get<Nat64::Translator>().GetErrorCounters(AsCoreType(aCounters));
}
otError otNat64GetCidr(otInstance *aInstance, otIp4Cidr *aCidr)
{
return AsCoreType(aInstance).Get<Nat64::Translator>().GetIp4Cidr(AsCoreType(aCidr));
}
#endif // OPENTHREAD_CONFIG_NAT64_TRANSLATOR_ENABLE
bool otIp4IsAddressEqual(const otIp4Address *aFirst, const otIp4Address *aSecond)
@@ -77,3 +109,17 @@ void otIp4ExtractFromIp6Address(uint8_t aPrefixLength, const otIp6Address *aIp6A
{
AsCoreType(aIp4Address).ExtractFromIp6Address(aPrefixLength, AsCoreType(aIp6Address));
}
void otIp4AddressToString(const otIp4Address *aAddress, char *aBuffer, uint16_t aSize)
{
AssertPointerIsNotNull(aBuffer);
AsCoreType(aAddress).ToString(aBuffer, aSize);
}
void otIp4CidrToString(const otIp4Cidr *aCidr, char *aBuffer, uint16_t aSize)
{
AssertPointerIsNotNull(aBuffer);
AsCoreType(aCidr).ToString(aBuffer, aSize);
}
+26 -2
View File
@@ -127,20 +127,44 @@ void Address::SynthesizeFromCidrAndHost(const Cidr &aCidr, const uint32_t aHost)
mFields.m32 = (aCidr.mAddress.mFields.m32 & aCidr.SubnetMask()) | (HostSwap32(aHost) & aCidr.HostMask());
}
void Address::ToString(StringWriter &aWriter) const
{
aWriter.Append("%d.%d.%d.%d", mFields.m8[0], mFields.m8[1], mFields.m8[2], mFields.m8[3]);
}
void Address::ToString(char *aBuffer, uint16_t aSize) const
{
StringWriter writer(aBuffer, aSize);
ToString(writer);
}
Address::InfoString Address::ToString(void) const
{
InfoString string;
string.Append("%d.%d.%d.%d", mFields.m8[0], mFields.m8[1], mFields.m8[2], mFields.m8[3]);
ToString(string);
return string;
}
void Cidr::ToString(StringWriter &aWriter) const
{
aWriter.Append("%s/%d", AsCoreType(&mAddress).ToString().AsCString(), mLength);
}
void Cidr::ToString(char *aBuffer, uint16_t aSize) const
{
StringWriter writer(aBuffer, aSize);
ToString(writer);
}
Cidr::InfoString Cidr::ToString(void) const
{
InfoString string;
string.Append("%s/%d", AsCoreType(&mAddress).ToString().AsCString(), mLength);
ToString(string);
return string;
}
+34
View File
@@ -161,6 +161,20 @@ public:
*/
Error FromString(const char *aString);
/**
* This method converts the address to a string.
*
* The string format uses quad-dotted notation of four bytes in the address (e.g., "127.0.0.1").
*
* If the resulting string does not fit in @p aBuffer (within its @p aSize characters), the string will be
* truncated but the outputted string is always null-terminated.
*
* @param[out] aBuffer A pointer to a char array to output the string (MUST NOT be `nullptr`).
* @param[in] aSize The size of @p aBuffer (in bytes).
*
*/
void ToString(char *aBuffer, uint16_t aSize) const;
/**
* This method converts the IPv4 address to a string.
*
@@ -170,6 +184,9 @@ public:
*
*/
InfoString ToString(void) const;
private:
void ToString(StringWriter &aWriter) const;
} OT_TOOL_PACKED_END;
/**
@@ -189,6 +206,21 @@ public:
*/
typedef String<Address::kAddressStringSize + kCidrSuffixSize> InfoString;
/**
* This method converts the IPv4 CIDR to a string.
*
* The string format uses quad-dotted notation of four bytes in the address with the length of prefix (e.g.,
* "127.0.0.1/32").
*
* If the resulting string does not fit in @p aBuffer (within its @p aSize characters), the string will be
* truncated but the outputted string is always null-terminated.
*
* @param[out] aBuffer A pointer to a char array to output the string (MUST NOT be `nullptr`).
* @param[in] aSize The size of @p aBuffer (in bytes).
*
*/
void ToString(char *aBuffer, uint16_t aSize) const;
/**
* This method converts the IPv4 CIDR to a string.
*
@@ -237,6 +269,8 @@ private:
}
uint32_t SubnetMask(void) const { return ~HostMask(); }
void ToString(StringWriter &aWriter) const;
};
/**
+138 -8
View File
@@ -52,6 +52,8 @@ Translator::Translator(Instance &aInstance)
: InstanceLocator(aInstance)
, mMappingExpirer(aInstance, MappingExpirerHandler)
{
Random::NonCrypto::FillBuffer(reinterpret_cast<uint8_t *>(&mNextMappingId), sizeof(mNextMappingId));
mNat64Prefix.Clear();
mIp4Cidr.Clear();
mMappingExpirer.Start(kAddressMappingIdleTimeoutMsec);
@@ -91,10 +93,11 @@ exit:
Translator::Result Translator::TranslateFromIp6(Message &aMessage)
{
Result res = kDrop;
Ip6::Header ip6Header;
Ip4::Header ip4Header;
AddressMapping *mapping = nullptr;
Result res = kDrop;
ErrorCounters::Reason dropReason = ErrorCounters::kUnknown;
Ip6::Header ip6Header;
Ip4::Header ip4Header;
AddressMapping * mapping = nullptr;
if (mIp4Cidr.mLength == 0 || !mNat64Prefix.IsValidNat64())
{
@@ -105,6 +108,7 @@ Translator::Result Translator::TranslateFromIp6(Message &aMessage)
if (ip6Header.ParseFrom(aMessage) != kErrorNone)
{
LogWarn("outgoing datagram is not a valid IPv6 datagram, drop");
dropReason = ErrorCounters::Reason::kIllegalPacket;
ExitNow(res = kDrop);
}
@@ -117,6 +121,7 @@ Translator::Result Translator::TranslateFromIp6(Message &aMessage)
if (mapping == nullptr)
{
LogWarn("failed to get a mapping for %s (mapping pool full?)", ip6Header.GetSource().ToString().AsCString());
dropReason = ErrorCounters::Reason::kNoMapping;
ExitNow(res = kDrop);
}
@@ -145,6 +150,7 @@ Translator::Result Translator::TranslateFromIp6(Message &aMessage)
res = kForward;
break;
default:
dropReason = ErrorCounters::Reason::kUnsupportedProto;
ExitNow(res = kDrop);
}
@@ -161,17 +167,24 @@ Translator::Result Translator::TranslateFromIp6(Message &aMessage)
ExitNow(res = kDrop);
}
aMessage.SetType(Message::kTypeIp4);
mCounters.Count6To4Packet(ip6Header.GetNextHeader(), ip6Header.GetPayloadLength());
mapping->mCounters.Count6To4Packet(ip6Header.GetNextHeader(), ip6Header.GetPayloadLength());
exit:
if (res == Result::kDrop)
{
mErrorCounters.Count6To4(dropReason);
}
return res;
}
Translator::Result Translator::TranslateToIp6(Message &aMessage)
{
Result res = Result::kDrop;
Ip6::Header ip6Header;
Ip4::Header ip4Header;
AddressMapping *mapping = nullptr;
Result res = Result::kDrop;
ErrorCounters::Reason dropReason = ErrorCounters::kUnknown;
Ip6::Header ip6Header;
Ip4::Header ip4Header;
AddressMapping * mapping = nullptr;
// Ip6::Header::ParseFrom may return an error value when the incoming message is an IPv4 datagram.
// If the message is already an IPv6 datagram, forward it directly.
@@ -193,6 +206,7 @@ Translator::Result Translator::TranslateToIp6(Message &aMessage)
if (ip4Header.ParseFrom(aMessage) != kErrorNone)
{
LogWarn("incoming message is neither IPv4 nor an IPv6 datagram, drop");
dropReason = ErrorCounters::Reason::kIllegalPacket;
ExitNow(res = kDrop);
}
@@ -200,6 +214,7 @@ Translator::Result Translator::TranslateToIp6(Message &aMessage)
if (mapping == nullptr)
{
LogWarn("no mapping found for the IPv4 address");
dropReason = ErrorCounters::Reason::kNoMapping;
ExitNow(res = kDrop);
}
@@ -230,6 +245,7 @@ Translator::Result Translator::TranslateToIp6(Message &aMessage)
res = kForward;
break;
default:
dropReason = ErrorCounters::Reason::kUnsupportedProto;
ExitNow(res = kDrop);
}
@@ -245,8 +261,15 @@ Translator::Result Translator::TranslateToIp6(Message &aMessage)
ExitNow(res = kDrop);
}
aMessage.SetType(Message::kTypeIp6);
mCounters.Count4To6Packet(ip4Header.GetProtocol(), ip4Header.GetTotalLength() - sizeof(ip4Header));
mapping->mCounters.Count4To6Packet(ip4Header.GetProtocol(), ip4Header.GetTotalLength() - sizeof(ip4Header));
exit:
if (res == Result::kDrop)
{
mErrorCounters.Count4To6(dropReason);
}
return res;
}
@@ -259,6 +282,25 @@ Translator::AddressMapping::InfoString Translator::AddressMapping::ToString(void
return string;
}
void Translator::AddressMapping::CopyTo(otNat64AddressMapping &aMapping, TimeMilli aNow) const
{
aMapping.mId = mId;
aMapping.mIp4 = mIp4;
aMapping.mIp6 = mIp6;
aMapping.mCounters = mCounters;
// We are removing expired mappings lazily, and an expired mapping might become active again before actually
// removed. Report the mapping to be "just expired" to avoid confusion.
if (mExpiry < aNow)
{
aMapping.mRemainingTimeMs = 0;
}
else
{
aMapping.mRemainingTimeMs = mExpiry - aNow;
}
}
void Translator::ReleaseMapping(AddressMapping &aMapping)
{
IgnoreError(mIp4AddressPool.PushBack(aMapping.mIp4));
@@ -300,6 +342,7 @@ Translator::AddressMapping *Translator::AllocateMapping(const Ip6::Address &aIp6
VerifyOrExit(mapping != nullptr);
mActiveAddressMappings.Push(*mapping);
mapping->mId = ++mNextMappingId;
mapping->mIp6 = aIp6Addr;
// PopBack must return a valid address since it is not empty.
mapping->mIp4 = *mIp4AddressPool.PopBack();
@@ -460,6 +503,93 @@ void Translator::MappingExpirerHandler(Timer &aTimer)
aTimer.Get<Translator>().mMappingExpirer.Start(kAddressMappingIdleTimeoutMsec);
}
void Translator::InitAddressMappingIterator(AddressMappingIterator &aIterator)
{
aIterator.mPtr = mActiveAddressMappings.GetHead();
}
Error Translator::GetNextAddressMapping(AddressMappingIterator &aIterator, otNat64AddressMapping &aMapping)
{
Error err = kErrorNotFound;
TimeMilli now = TimerMilli::GetNow();
AddressMapping *item = static_cast<AddressMapping *>(aIterator.mPtr);
VerifyOrExit(item != nullptr);
item->CopyTo(aMapping, now);
aIterator.mPtr = item->GetNext();
err = kErrorNone;
exit:
return err;
}
Error Translator::GetIp4Cidr(Ip4::Cidr &aCidr)
{
Error err = kErrorNone;
VerifyOrExit(mIp4Cidr.mLength > 0, err = kErrorNotFound);
aCidr = mIp4Cidr;
exit:
return err;
}
Error Translator::GetIp6Prefix(Ip6::Prefix &aPrefix)
{
Error err = kErrorNone;
VerifyOrExit(mNat64Prefix.mLength > 0, err = kErrorNotFound);
aPrefix = mNat64Prefix;
exit:
return err;
}
void Translator::ProtocolCounters::Count6To4Packet(uint8_t aProtocol, uint64_t aPacketSize)
{
switch (aProtocol)
{
case Ip6::kProtoUdp:
mUdp.m6To4Packets++;
mUdp.m6To4Bytes += aPacketSize;
break;
case Ip6::kProtoTcp:
mTcp.m6To4Packets++;
mTcp.m6To4Bytes += aPacketSize;
break;
case Ip6::kProtoIcmp6:
mIcmp.m6To4Packets++;
mIcmp.m6To4Bytes += aPacketSize;
break;
}
mTotal.m6To4Packets++;
mTotal.m6To4Bytes += aPacketSize;
}
void Translator::ProtocolCounters::Count4To6Packet(uint8_t aProtocol, uint64_t aPacketSize)
{
switch (aProtocol)
{
case Ip4::kProtoUdp:
mUdp.m4To6Packets++;
mUdp.m4To6Bytes += aPacketSize;
break;
case Ip4::kProtoTcp:
mTcp.m4To6Packets++;
mTcp.m4To6Bytes += aPacketSize;
break;
case Ip4::kProtoIcmp:
mIcmp.m4To6Packets++;
mIcmp.m4To6Bytes += aPacketSize;
break;
}
mTotal.m4To6Packets++;
mTotal.m4To6Bytes += aPacketSize;
}
} // namespace Nat64
} // namespace ot
+144
View File
@@ -61,6 +61,8 @@ public:
OPENTHREAD_CONFIG_NAT64_IDLE_TIMEOUT_SECONDS * Time::kOneSecondInMsec;
static constexpr uint32_t kAddressMappingPoolSize = OPENTHREAD_CONFIG_NAT64_MAX_MAPPINGS;
typedef otNat64AddressMappingIterator AddressMappingIterator; ///< Address mapping Iterator.
/**
* The possible results of NAT64 translation.
*
@@ -74,6 +76,64 @@ public:
kDrop, ///< The caller should drop the datagram silently.
};
/**
* Represents the counters for the protocols supported by NAT64.
*
*/
class ProtocolCounters : public otNat64ProtocolCounters, public Clearable<ProtocolCounters>
{
public:
/**
* Adds the packet to the counter for the given IPv6 protocol.
*
* @param[in] aProtocol The protocol of the packet.
* @param[in] aPacketSize The size of the packet.
*
*/
void Count6To4Packet(uint8_t aProtocol, uint64_t aPacketSize);
/**
* Adds the packet to the counter for the given IPv4 protocol.
*
* @param[in] aProtocol The protocol of the packet.
* @param[in] aPacketSize The size of the packet.
*
*/
void Count4To6Packet(uint8_t aProtocol, uint64_t aPacketSize);
};
/**
* Represents the counters of dropped packets due to errors when handling NAT64 packets.
*
*/
class ErrorCounters : public otNat64ErrorCounters, public Clearable<otNat64ErrorCounters>
{
public:
enum Reason : uint8_t
{
kUnknown = OT_NAT64_DROP_REASON_UNKNOWN,
kIllegalPacket = OT_NAT64_DROP_REASON_ILLEGAL_PACKET,
kUnsupportedProto = OT_NAT64_DROP_REASON_UNSUPPORTED_PROTO,
kNoMapping = OT_NAT64_DROP_REASON_NO_MAPPING,
};
/**
* Adds the counter for the given reason when translating an IPv4 datagram.
*
* @param[in] aReason The reason of packet drop.
*
*/
void Count4To6(Reason aReason) { mCount4To6[aReason]++; }
/**
* Adds the counter for the given reason when translating an IPv6 datagram.
*
* @param[in] aReason The reason of packet drop.
*
*/
void Count6To4(Reason aReason) { mCount6To4[aReason]++; }
};
/**
* This constructor initializes the NAT64 translator.
*
@@ -163,6 +223,76 @@ public:
*/
void SetNat64Prefix(const Ip6::Prefix &aNat64Prefix);
/**
* Initializes an `otNat64AddressMappingIterator`.
*
* An iterator MUST be initialized before it is used.
*
* An iterator can be initialized again to restart from the beginning of the mapping info.
*
* @param[out] aIterator An iterator to initialize.
*
*/
void InitAddressMappingIterator(AddressMappingIterator &aIterator);
/**
* Gets the next AddressMapping info (using an iterator).
*
* @param[in,out] aIterator The iterator. On success the iterator will be updated to point to next NAT64
* address mapping record. To get the first entry the iterator should be set to
* OT_NAT64_ADDRESS_MAPPING_ITERATOR_INIT.
* @param[out] aMapping An `otNat64AddressMapping` where information of next NAT64 address mapping record
* is placed (on success).
*
* @retval kErrorNone Successfully found the next NAT64 address mapping info (@p aMapping was successfully
* updated).
* @retval kErrorNotFound No subsequent NAT64 address mapping info was found.
*
*/
Error GetNextAddressMapping(AddressMappingIterator &aIterator, otNat64AddressMapping &aMapping);
/**
* Gets the NAT64 translator counters.
*
* The counters are initialized to zero when the OpenThread instance is initialized.
*
* @param[out] aCounters A `ProtocolCounters` where the counters of NAT64 translator will be placed.
*
*/
void GetCounters(ProtocolCounters &aCounters) const { aCounters = mCounters; }
/**
* Gets the NAT64 translator error counters.
*
* The counters are initialized to zero when the OpenThread instance is initialized.
*
* @param[out] aCounters An `ErrorCounters` where the counters of NAT64 translator will be placed.
*
*/
void GetErrorCounters(ErrorCounters &aCounters) const { aCounters = mErrorCounters; }
/**
* Gets the configured CIDR in the NAT64 translator.
*
* @param[out] aCidr The `Ip4::Cidr` Where the configured CIDR will be placed.
*
* @retval kErrorNone @p aCidr is set to the configured CIDR.
* @retval kErrorNotFound The translator is not configured with an IPv4 CIDR.
*
*/
Error GetIp4Cidr(Ip4::Cidr &aCidr);
/**
* Gets the configured IPv6 prefix in the NAT64 translator.
*
* @param[out] aPrefix The `Ip6::Prefix` where the configured NAT64 prefix will be placed.
*
* @retval kErrorNone @p aPrefix is set to the configured prefix.
* @retval kErrorNotFound The translator is not configured with an IPv6 prefix.
*
*/
Error GetIp6Prefix(Ip6::Prefix &aPrefix);
private:
class AddressMapping : public LinkedListEntry<AddressMapping>
{
@@ -174,11 +304,16 @@ private:
void Touch(TimeMilli aNow) { mExpiry = aNow + kAddressMappingIdleTimeoutMsec; }
InfoString ToString(void);
void CopyTo(otNat64AddressMapping &aMapping, TimeMilli aNow) const;
uint64_t mId; // The unique id for a mapping session.
Ip4::Address mIp4;
Ip6::Address mIp6;
TimeMilli mExpiry; // The timestamp when this mapping expires, in milliseconds.
ProtocolCounters mCounters;
private:
bool Matches(const Ip4::Address &aIp4) const { return mIp4 == aIp4; }
bool Matches(const Ip6::Address &aIp6) const { return mIp6 == aIp6; }
@@ -198,6 +333,8 @@ private:
static void MappingExpirerHandler(Timer &aTimer);
uint64_t mNextMappingId;
Array<Ip4::Address, kAddressMappingPoolSize> mIp4AddressPool;
Pool<AddressMapping, kAddressMappingPoolSize> mAddressMappingPool;
LinkedList<AddressMapping> mActiveAddressMappings;
@@ -206,9 +343,16 @@ private:
Ip4::Cidr mIp4Cidr;
TimerMilli mMappingExpirer;
ProtocolCounters mCounters;
ErrorCounters mErrorCounters;
};
} // namespace Nat64
DefineCoreType(otNat64ProtocolCounters, Nat64::Translator::ProtocolCounters);
DefineCoreType(otNat64ErrorCounters, Nat64::Translator::ErrorCounters);
} // namespace ot
#endif // OPENTHREAD_CONFIG_NAT64_TRANSLATOR_ENABLE