[nat64] add functions for processing IPv4 packets (#7824)

With NAT64, we need to handle the IPv4 packets inside the border
router functions in OpenThread core.

This commit introduces a few new classes for NAT64:
- Add `Ip4::Cidr` (for specifying CIDR block of translated packets)
- Updated Ip4 address format -- use a union of (m8[4], m16[2], m32)
  instead of 4 bytes.
- Extend `Checksum` for supporting ICMP in Ip4 (and TCP4 / UDP4) and
  IPv4 header.
This commit is contained in:
Song GUO
2022-07-16 01:54:20 +08:00
committed by GitHub
parent 13416b15fb
commit 03c9b9389d
21 changed files with 1465 additions and 199 deletions
+1 -1
View File
@@ -298,7 +298,7 @@ LOCAL_SRC_FILES := \
src/core/net/dns_types.cpp \
src/core/net/dnssd_server.cpp \
src/core/net/icmp6.cpp \
src/core/net/ip4_address.cpp \
src/core/net/ip4_types.cpp \
src/core/net/ip6.cpp \
src/core/net/ip6_address.cpp \
src/core/net/ip6_filter.cpp \
+1
View File
@@ -70,6 +70,7 @@ openthread_headers = \
openthread/logging.h \
openthread/message.h \
openthread/multi_radio.h \
openthread/nat64.h \
openthread/ncp.h \
openthread/netdata.h \
openthread/netdata_publisher.h \
+1
View File
@@ -74,6 +74,7 @@ source_set("openthread") {
"logging.h",
"message.h",
"multi_radio.h",
"nat64.h",
"ncp.h",
"netdata.h",
"netdata_publisher.h",
+1 -1
View File
@@ -53,7 +53,7 @@ extern "C" {
* @note This number versions both OpenThread platform and user APIs.
*
*/
#define OPENTHREAD_API_VERSION (226)
#define OPENTHREAD_API_VERSION (227)
/**
* @addtogroup api-instance
@@ -1,5 +1,5 @@
/*
* Copyright (c) 2021, The OpenThread Authors.
* Copyright (c) 2022, The OpenThread Authors.
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
@@ -28,69 +28,72 @@
/**
* @file
* This file implements IPv4 address related functionality.
* @brief
* This file defines the OpenThread API for NAT64 on a border router.
*/
#include "ip4_address.hpp"
#ifndef OPENTHREAD_NAT64_H_
#define OPENTHREAD_NAT64_H_
#include "common/code_utils.hpp"
#include "common/numeric_limits.hpp"
#include <openthread/message.h>
namespace ot {
namespace Ip4 {
#ifdef __cplusplus
extern "C" {
#endif
Error Address::FromString(const char *aString)
/**
* @addtogroup api-nat64
*
* @brief This module includes functions and structs for the NAT64 function on the border router. These functions are
* only available when `OPENTHREAD_CONFIG_BORDER_ROUTING_NAT64_ENABLE` is enabled.
*
* @{
*
*/
#define OT_IP4_ADDRESS_SIZE 4 ///< Size of an IPv4 address (bytes)
/**
* @struct otIp4Address
*
* This structure represents an IPv4 address.
*
*/
OT_TOOL_PACKED_BEGIN
struct otIp4Address
{
constexpr char kSeperatorChar = '.';
constexpr char kNullChar = '\0';
Error error = kErrorParse;
for (uint8_t index = 0;; index++)
union OT_TOOL_PACKED_FIELD
{
uint16_t value = 0;
uint8_t hasFirstDigit = false;
uint8_t m8[OT_IP4_ADDRESS_SIZE]; ///< 8-bit fields
uint32_t m32; ///< 32-bit representation
} mFields;
} OT_TOOL_PACKED_END;
for (char digitChar = *aString;; ++aString, digitChar = *aString)
{
if ((digitChar < '0') || (digitChar > '9'))
{
break;
}
/**
* This structure represents an IPv4 address.
*
*/
typedef struct otIp4Address otIp4Address;
value = static_cast<uint16_t>((value * 10) + static_cast<uint8_t>(digitChar - '0'));
VerifyOrExit(value <= NumericLimits<uint8_t>::kMax);
hasFirstDigit = true;
}
VerifyOrExit(hasFirstDigit);
mBytes[index] = static_cast<uint8_t>(value);
if (index == sizeof(Address) - 1)
{
break;
}
VerifyOrExit(*aString == kSeperatorChar);
aString++;
}
VerifyOrExit(*aString == kNullChar);
error = kErrorNone;
exit:
return error;
}
Address::InfoString Address::ToString(void) const
/**
* @struct otIp4Cidr
*
* This structure represents an IPv4 CIDR block.
*
*/
typedef struct otIp4Cidr
{
InfoString string;
otIp4Address mAddress;
uint8_t mLength;
} otIp4Cidr;
string.Append("%d.%d.%d.%d", mBytes[0], mBytes[1], mBytes[2], mBytes[3]);
/**
* @}
*
*/
return string;
#ifdef __cplusplus
}
#endif
} // namespace Ip4
} // namespace ot
#endif // OPENTHREAD_NAT64_H_
+2 -2
View File
@@ -548,8 +548,8 @@ openthread_core_files = [
"net/dnssd_server.hpp",
"net/icmp6.cpp",
"net/icmp6.hpp",
"net/ip4_address.cpp",
"net/ip4_address.hpp",
"net/ip4_types.cpp",
"net/ip4_types.hpp",
"net/ip6.cpp",
"net/ip6.hpp",
"net/ip6_address.cpp",
+1 -1
View File
@@ -164,7 +164,7 @@ set(COMMON_SOURCES
net/dns_types.cpp
net/dnssd_server.cpp
net/icmp6.cpp
net/ip4_address.cpp
net/ip4_types.cpp
net/ip6.cpp
net/ip6_address.cpp
net/ip6_filter.cpp
+2 -2
View File
@@ -254,7 +254,7 @@ SOURCES_COMMON = \
net/dns_types.cpp \
net/dnssd_server.cpp \
net/icmp6.cpp \
net/ip4_address.cpp \
net/ip4_types.cpp \
net/ip6.cpp \
net/ip6_address.cpp \
net/ip6_filter.cpp \
@@ -559,7 +559,7 @@ HEADERS_COMMON = \
net/dns_types.hpp \
net/dnssd_server.hpp \
net/icmp6.hpp \
net/ip4_address.hpp \
net/ip4_types.hpp \
net/ip6.hpp \
net/ip6_address.hpp \
net/ip6_filter.hpp \
+76
View File
@@ -36,6 +36,7 @@
#include "common/code_utils.hpp"
#include "common/message.hpp"
#include "net/icmp6.hpp"
#include "net/ip4_types.hpp"
#include "net/tcp6.hpp"
#include "net/udp6.hpp"
@@ -115,6 +116,35 @@ void Checksum::Calculate(const Ip6::Address &aSource,
}
}
void Checksum::Calculate(const Ip4::Address &aSource,
const Ip4::Address &aDestination,
uint8_t aIpProto,
const Message & aMessage)
{
Message::Chunk chunk;
uint16_t length = aMessage.GetLength() - aMessage.GetOffset();
// Pseudo-header for checksum calculation (RFC-768/792/793).
// Note: ICMP checksum won't count the presudo header like TCP and UDP.
if (aIpProto != Ip4::kProtoIcmp)
{
AddData(aSource.GetBytes(), sizeof(Ip4::Address));
AddData(aDestination.GetBytes(), sizeof(Ip4::Address));
AddUint16(static_cast<uint16_t>(aIpProto));
AddUint16(length);
}
// Add message content (from offset to the end) to checksum.
aMessage.GetFirstChunk(aMessage.GetOffset(), length, chunk);
while (chunk.GetLength() > 0)
{
AddData(chunk.GetBytes(), chunk.GetLength());
aMessage.GetNextChunk(length, chunk);
}
}
Error Checksum::VerifyMessageChecksum(const Message &aMessage, const Ip6::MessageInfo &aMessageInfo, uint8_t aIpProto)
{
Checksum checksum;
@@ -150,6 +180,8 @@ void Checksum::UpdateMessageChecksum(Message & aMessage,
ExitNow();
}
// Clear the checksum before calculating it.
aMessage.Write<uint16_t>(aMessage.GetOffset() + headerOffset, 0);
checksum.Calculate(aSource, aDestination, aIpProto, aMessage);
checksum.WriteToMessage(aMessage.GetOffset() + headerOffset, aMessage);
@@ -157,4 +189,48 @@ exit:
return;
}
void Checksum::UpdateMessageChecksum(Message & aMessage,
const Ip4::Address &aSource,
const Ip4::Address &aDestination,
uint8_t aIpProto)
{
uint16_t headerOffset;
Checksum checksum;
switch (aIpProto)
{
case Ip4::kProtoTcp:
headerOffset = Ip4::Tcp::Header::kChecksumFieldOffset;
break;
case Ip4::kProtoUdp:
headerOffset = Ip4::Udp::Header::kChecksumFieldOffset;
break;
case Ip4::kProtoIcmp:
headerOffset = Ip4::Icmp::Header::kChecksumFieldOffset;
break;
default:
ExitNow();
}
// Clear the checksum before calculating it.
aMessage.Write<uint16_t>(aMessage.GetOffset() + headerOffset, 0);
checksum.Calculate(aSource, aDestination, aIpProto, aMessage);
checksum.WriteToMessage(aMessage.GetOffset() + headerOffset, aMessage);
exit:
return;
}
void Checksum::UpdateIp4HeaderChecksum(Ip4::Header &aHeader)
{
Checksum checksum;
aHeader.SetChecksum(0);
checksum.AddData(reinterpret_cast<const uint8_t *>(&aHeader), sizeof(aHeader));
aHeader.SetChecksum(~checksum.GetValue());
}
} // namespace ot
+32 -3
View File
@@ -39,6 +39,7 @@
#include <stdint.h>
#include "common/message.hpp"
#include "net/ip4_types.hpp"
#include "net/ip6_address.hpp"
#include "net/ip6_headers.hpp"
#include "net/socket.hpp"
@@ -69,11 +70,11 @@ public:
static Error VerifyMessageChecksum(const Message &aMessage, const Ip6::MessageInfo &aMessageInfo, uint8_t aIpProto);
/**
* This static method calculates and then updates the checksum in a given message (if UDP/ICMP6).
* This static method calculates and then updates the checksum in a given message (if TCP/UDP/ICMPv6).
*
* @param[in,out] aMessage The message to update the checksum in. The `aMessage.GetOffset()` should point to start
* of the UDP/ICMP6 header. On exit the checksum field in UDP/ICMP6 header in the message
* is updated.
* of the TCP/UDP/ICMPv6 header. On exit the checksum field in TCP/UDP/ICMPv6 header in the
* message is updated.
* @param[in] aSource The source address.
* @param[in] aDestination The destination address.
* @param[in] aIpProto The Internet Protocol value.
@@ -84,6 +85,30 @@ public:
const Ip6::Address &aDestination,
uint8_t aIpProto);
/**
* This static method calculates and then updates the checksum in a given IPv4 message (if TCP/UDP/ICMP(v4)).
*
* @param[in,out] aMessage The message to update the checksum in. The `aMessage.GetOffset()` should point to start
* of the TCP/UDP/ICMP(v4) header. On exit the checksum field in TCP/UDP/ICMP(v4) header in
* the message is updated.
* @param[in] aSource The source address.
* @param[in] aDestination The destination address.
* @param[in] aIpProto The Internet Protocol value.
*
*/
static void UpdateMessageChecksum(Message & aMessage,
const Ip4::Address &aSource,
const Ip4::Address &aDestination,
uint8_t aIpProto);
/**
* This static method calculates and then updates the checksum field in the IPv4 header.
*
* @param[in,out] aHeader The IPv4 header to update the checksum in.
*
*/
static void UpdateIp4HeaderChecksum(Ip4::Header &aHeader);
private:
Checksum(void)
: mValue(0)
@@ -100,6 +125,10 @@ private:
const Ip6::Address &aDestination,
uint8_t aIpProto,
const Message & aMessage);
void Calculate(const Ip4::Address &aSource,
const Ip4::Address &aDestination,
uint8_t aIpProto,
const Message & aMessage);
static constexpr uint16_t kValidRxChecksum = 0xffff;
+1 -1
View File
@@ -46,7 +46,7 @@
#include "common/equatable.hpp"
#include "common/message.hpp"
#include "crypto/ecdsa.hpp"
#include "net/ip4_address.hpp"
#include "net/ip4_types.hpp"
#include "net/ip6_address.hpp"
namespace ot {
-112
View File
@@ -1,112 +0,0 @@
/*
* Copyright (c) 2021, 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 IPv4 addresses.
*/
#ifndef IP4_ADDRESS_HPP_
#define IP4_ADDRESS_HPP_
#include "openthread-core-config.h"
#include "common/clearable.hpp"
#include "common/equatable.hpp"
#include "common/error.hpp"
#include "common/string.hpp"
namespace ot {
namespace Ip4 {
/**
* This class represents an IPv4 address.
*
*/
OT_TOOL_PACKED_BEGIN
class Address : public Equatable<Address>, public Clearable<Address>
{
public:
static constexpr uint16_t kSize = 4; ///< Size of an IPv4 Address (in bytes).
static constexpr uint16_t kAddressStringSize = 17; ///< String size used by `ToString()`.
/**
* This type defines the fixed-length `String` object returned from `ToString()`.
*
*/
typedef String<kAddressStringSize> InfoString;
/**
* This method gets the IPv4 address as a pointer to a byte array.
*
* @returns A pointer to a byte array containing the IPv4 address.
*
*/
const uint8_t *GetBytes(void) const { return mBytes; }
/**
* This method sets the IPv4 address from a given byte array.
*
* @param[in] aBuffer Pointer to an array containing the IPv4 address. `kSize` bytes from the buffer
* are copied to form the IPv4 address.
*
*/
void SetBytes(const uint8_t *aBuffer) { memcpy(mBytes, aBuffer, kSize); }
/**
* This method parses an IPv4 address string.
*
* The string MUST follow the quad-dotted notation of four decimal values (ranging from 0 to 255 each). For
* example, "127.0.0.1"
*
* @param[in] aString A pointer to the null-terminated string.
*
* @retval kErrorNone Successfully parsed the IPv4 address string.
* @retval kErrorParse Failed to parse the IPv4 address string.
*
*/
Error FromString(const char *aString);
/**
* This method converts the IPv4 address to a string.
*
* The string format uses quad-dotted notation of four bytes in the address (e.g., "127.0.0.1").
*
* @returns An `InfoString` representing the IPv4 address.
*
*/
InfoString ToString(void) const;
private:
uint8_t mBytes[kSize];
} OT_TOOL_PACKED_END;
} // namespace Ip4
} // namespace ot
#endif // IP4_ADDRESS_HPP_
+175
View File
@@ -0,0 +1,175 @@
/*
* Copyright (c) 2022, 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 implements IP4 headers processing.
*/
#include "ip4_types.hpp"
#include "ip6_address.hpp"
namespace ot {
namespace Ip4 {
Error Address::FromString(const char *aString)
{
constexpr char kSeperatorChar = '.';
constexpr char kNullChar = '\0';
Error error = kErrorParse;
for (uint8_t index = 0;; index++)
{
uint16_t value = 0;
uint8_t hasFirstDigit = false;
for (char digitChar = *aString;; ++aString, digitChar = *aString)
{
if ((digitChar < '0') || (digitChar > '9'))
{
break;
}
value = static_cast<uint16_t>((value * 10) + static_cast<uint8_t>(digitChar - '0'));
VerifyOrExit(value <= NumericLimits<uint8_t>::kMax);
hasFirstDigit = true;
}
VerifyOrExit(hasFirstDigit);
mFields.m8[index] = static_cast<uint8_t>(value);
if (index == sizeof(Address) - 1)
{
break;
}
VerifyOrExit(*aString == kSeperatorChar);
aString++;
}
VerifyOrExit(*aString == kNullChar);
error = kErrorNone;
exit:
return error;
}
void Address::ExtractFromIp6Address(uint8_t aPrefixLength, const Ip6::Address &aIp6Address)
{
// The prefix length must be 32, 40, 48, 56, 64, 96. IPv4 bytes are added
// after the prefix, skipping over the bits 64 to 71 (byte at `kSkipIndex`)
// which must be set to zero. The suffix is set to zero (per RFC 6502).
//
// +--+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+
// |PL| 0-------------32--40--48--56--64--72--80--88--96--104---------|
// +--+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+
// |32| prefix |v4(32) | u | suffix |
// +--+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+
// |40| prefix |v4(24) | u |(8)| suffix |
// +--+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+
// |48| prefix |v4(16) | u | (16) | suffix |
// +--+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+
// |56| prefix |(8)| u | v4(24) | suffix |
// +--+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+
// |64| prefix | u | v4(32) | suffix |
// +--+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+
// |96| prefix | v4(32) |
// +--+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+
constexpr uint8_t kSkipIndex = 8;
uint8_t ip6Index;
OT_ASSERT(Ip6::Prefix::IsValidNat64PrefixLength(aPrefixLength));
ip6Index = aPrefixLength / CHAR_BIT;
for (uint8_t i = 0; i < Ip4::Address::kSize; i++)
{
if (ip6Index == kSkipIndex)
{
ip6Index++;
}
mFields.m8[i] = aIp6Address.GetBytes()[ip6Index++];
}
}
void Address::SynthesizeFromCidrAndHost(const Cidr &aCidr, const uint32_t aHost)
{
mFields.m32 = (aCidr.mAddress.mFields.m32 & aCidr.SubnetMask()) | (HostSwap32(aHost) & aCidr.HostMask());
}
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]);
return string;
}
Cidr::InfoString Cidr::ToString(void) const
{
InfoString string;
string.Append("%s/%d", AsCoreType(&mAddress).ToString().AsCString(), mLength);
return string;
}
bool Cidr::operator==(const Cidr &aOther) const
{
return (mLength == aOther.mLength) &&
(Ip6::Prefix::MatchLength(GetBytes(), aOther.GetBytes(), Ip4::Address::kSize) >= mLength);
}
void Cidr::Set(const uint8_t *aAddress, uint8_t aLength)
{
memcpy(mAddress.mFields.m8, aAddress, Ip4::Address::kSize);
mLength = aLength;
}
Error Header::ParseFrom(const Message &aMessage)
{
Error error = kErrorParse;
SuccessOrExit(aMessage.Read(0, *this));
VerifyOrExit(IsValid());
VerifyOrExit(GetTotalLength() == aMessage.GetLength());
error = kErrorNone;
exit:
return error;
}
} // namespace Ip4
} // namespace ot
+672
View File
@@ -0,0 +1,672 @@
/*
* Copyright (c) 2022, 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 IPv4 packet processing.
*/
#ifndef IP4_TYPES_HPP_
#define IP4_TYPES_HPP_
#include "openthread-core-config.h"
#include <stddef.h>
#include <openthread/nat64.h>
#include "common/clearable.hpp"
#include "common/encoding.hpp"
#include "common/message.hpp"
#include "net/ip6_types.hpp"
#include "net/netif.hpp"
#include "net/socket.hpp"
#include "net/tcp6.hpp"
#include "net/udp6.hpp"
namespace ot {
namespace Ip6 {
// Forward declaration for ExtractFromIp4Address
class Address;
} // namespace Ip6
/**
* @namespace ot::Ip4
*
* @brief
* This namespace includes definitions for IPv4 networking used by NAT64.
*
*/
namespace Ip4 {
using Encoding::BigEndian::HostSwap16;
using Encoding::BigEndian::HostSwap32;
using Ecn = Ip6::Ecn;
/**
* @addtogroup core-ipv4
*
* @brief
* This module includes definitions for the IPv4 network layer.
*
*/
/**
* @addtogroup core-ip4-ip4
*
* @brief
* This module includes definitions for IPv4 networking used by NAT64.
*
* @{
*
*/
// Forward declaration for Address::SynthesizeFromCidrAndHost
class Cidr;
/**
* This class represents an IPv4 address.
*
*/
OT_TOOL_PACKED_BEGIN
class Address : public otIp4Address, public Equatable<Address>, public Clearable<Address>
{
public:
static constexpr uint16_t kSize = 4; ///< Size of an IPv4 Address (in bytes).
static constexpr uint16_t kAddressStringSize = 17; ///< String size used by `ToString()`.
/**
* This type defines the fixed-length `String` object returned from `ToString()`.
*
*/
typedef String<kAddressStringSize> InfoString;
/**
* This method gets the IPv4 address as a pointer to a byte array.
*
* @returns A pointer to a byte array containing the IPv4 address.
*
*/
const uint8_t *GetBytes(void) const { return mFields.m8; }
/**
* This method sets the IPv4 address from a given byte array.
*
* @param[in] aBuffer Pointer to an array containing the IPv4 address. `kSize` bytes from the buffer
* are copied to form the IPv4 address.
*
*/
void SetBytes(const uint8_t *aBuffer) { memcpy(mFields.m8, aBuffer, kSize); }
/**
* This method sets the IPv4 address by performing NAT64 address translation from a given IPv6 address as specified
* in RFC 6052.
*
* The NAT64 @p aPrefixLength MUST be one of the following values: 32, 40, 48, 56, 64, or 96, otherwise the behavior
* of this method is undefined.
*
* @param[in] aPrefixLength The prefix length to use for IPv4/IPv6 translation.
* @param[in] aIp6Address The IPv6 address to translate to IPv4.
*
*/
void ExtractFromIp6Address(uint8_t aPrefixLength, const Ip6::Address &aIp6Address);
/**
* This method sets the IPv4 address from the given CIDR and the host field.
*
* @param[in] aCidr The CIDR for the IPv4 address.
* @param[in] aHost The host bits of the IPv4 address in host byte order. The aHost will be masked by host mask.
*
*/
void SynthesizeFromCidrAndHost(const Cidr &aCidr, uint32_t aHost);
/**
* This method parses an IPv4 address string.
*
* The string MUST follow the quad-dotted notation of four decimal values (ranging from 0 to 255 each). For
* example, "127.0.0.1"
*
* @param[in] aString A pointer to the null-terminated string.
*
* @retval kErrorNone Successfully parsed the IPv4 address string.
* @retval kErrorParse Failed to parse the IPv4 address string.
*
*/
Error FromString(const char *aString);
/**
* This method converts the IPv4 address to a string.
*
* The string format uses quad-dotted notation of four bytes in the address (e.g., "127.0.0.1").
*
* @returns An `InfoString` representing the IPv4 address.
*
*/
InfoString ToString(void) const;
} OT_TOOL_PACKED_END;
/**
* This class represents an IPv4 CIDR block.
*
*/
class Cidr : public otIp4Cidr, public Unequatable<Cidr>, public Clearable<Address>
{
friend class Address;
public:
static constexpr uint16_t kCidrSuffixSize = 3; ///< Suffix to represent CIDR (/dd).
/**
* This type defines the fixed-length `String` object returned from `ToString()`.
*
*/
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").
*
* @returns An `InfoString` representing the IPv4 cidr.
*
*/
InfoString ToString(void) const;
/**
* This method gets the prefix as a pointer to a byte array.
*
* @returns A pointer to a byte array containing the Prefix.
*
*/
const uint8_t *GetBytes(void) const { return mAddress.mFields.m8; }
/**
* This method overloads operator `==` to evaluate whether or not two prefixes are equal.
*
* @param[in] aOther The other prefix to compare with.
*
* @retval TRUE If the two prefixes are equal.
* @retval FALSE If the two prefixes are not equal.
*
*/
bool operator==(const Cidr &aOther) const;
/**
* This method sets the CIDR.
*
* @param[in] aAddress A pointer to buffer containing the CIDR bytes. The length of aAddress should be 4 bytes.
* @param[in] aLength The length of CIDR in bits.
*
*/
void Set(const uint8_t *aAddress, uint8_t aLength);
private:
uint32_t HostMask(void) const
{
// Note: Using LL suffix to make it a uint64 since /32 is a valid CIDR, and right shifting 32 bits is undefined
// for uint32.
return HostSwap32(0xffffffffLL >> mLength);
}
uint32_t SubnetMask(void) const { return ~HostMask(); }
};
/**
* This class implements IPv4 header generation and parsing.
*
*/
OT_TOOL_PACKED_BEGIN
class Header : public Clearable<Header>
{
public:
static constexpr uint8_t kVersionIhlOffset = 0;
static constexpr uint8_t kTrafficClassOffset = 1;
static constexpr uint8_t kTotalLengthOffset = 2;
static constexpr uint8_t kIdenficationOffset = 4;
static constexpr uint8_t kFlagsFragmentOffset = 6;
static constexpr uint8_t kTtlOffset = 8;
static constexpr uint8_t kProtocolOffset = 9;
static constexpr uint8_t kHeaderChecksumOffset = 10;
static constexpr uint8_t kSourceAddressOffset = 12;
static constexpr uint8_t kDestinationAddressOffset = 16;
/**
* This method indicates whether or not the header appears to be well-formed.
*
* @retval TRUE If the header appears to be well-formed.
* @retval FALSE If the header does not appear to be well-formed.
*
*/
bool IsValid(void) const { return IsVersion4(); }
/**
* This method initializes the Version to 4 and sets Traffic Class and Flow fields to zero.
*
* The other fields in the IPv4 header remain unchanged.
*
*/
void InitVersionIhl(void) { SetVersionIhl(kVersIhlInit); }
/**
* This method sets the version and Ihl of the IPv4 header.
*
* @param[in] aVersionIhl The octet for the version and Ihl field.
*
*/
void SetVersionIhl(uint8_t aVersionIhl) { mVersIhl = aVersionIhl; }
/**
* This method indicates whether or not the IPv4 Version is set to 6.
*
* @retval TRUE If the IPv4 Version is set to 4.
* @retval FALSE If the IPv4 Version is not set to 4.
*
*/
bool IsVersion4(void) const { return (mVersIhl & kVersionMask) == kVersion4; }
/**
* This method returns the octet for DSCP + ECN.
*
* @retval The octet for DSCP and ECN.
*
*/
uint8_t GetDscpEcn(void) const { return mDscpEcn; }
/**
* This method gets the 6-bit Differentiated Services Code Point (DSCP) from Traffic Class field.
*
* @returns The DSCP value.
*
*/
uint8_t GetDscp(void) const { return (mDscpEcn & kDscpMask) >> kDscpOffset; }
/**
* This method sets 6-bit Differentiated Services Code Point (DSCP) in IPv4 header.
*
* @param[in] aDscp The DSCP value.
*
*/
void SetDscp(uint8_t aDscp) { mDscpEcn = static_cast<uint8_t>((mDscpEcn & ~kDscpMask) | (aDscp << kDscpOffset)); }
/**
* This method gets the 2-bit Explicit Congestion Notification (ECN) from Traffic Class field.
*
* @returns The ECN value.
*
*/
Ecn GetEcn(void) const { return static_cast<Ecn>(mDscpEcn & kEcnMask); }
/**
* This method sets the 2-bit Explicit Congestion Notification (ECN) in IPv4 header..
*
* @param[in] aEcn The ECN value.
*
*/
void SetEcn(Ecn aEcn) { mDscpEcn = ((mDscpEcn & ~kEcnMask) | aEcn); }
/**
* This method returns the IPv4 Payload Length value.
*
* @returns The IPv4 Payload Length value.
*
*/
uint16_t GetTotalLength(void) const { return HostSwap16(mTotalLength); }
/**
* This method sets the IPv4 Payload Length value.
*
* @param[in] aLength The IPv4 Payload Length value.
*
*/
void SetTotalLength(uint16_t aLength) { mTotalLength = HostSwap16(aLength); }
/**
* This method returns the IPv4 payload protocol.
*
* @returns The IPv4 payload protocol value.
*
*/
uint8_t GetProtocol(void) const { return mProtocol; }
/**
* This method sets the IPv4 payload protocol.
*
* @param[in] aProtocol The IPv4 payload protocol.
*
*/
void SetProtocol(uint8_t aProtocol) { mProtocol = aProtocol; }
/**
* This method returns the IPv4 header checksum, the checksum is in host endian.
*
* @returns The checksum field in the IPv4 header.
*
*/
uint16_t GetChecksum(void) const { return HostSwap16(mHeaderChecksum); }
/**
* This method sets the IPv4 header checksum, the checksum is in host endian.
*
* @param[in] aChecksum The checksum for the IPv4 header.
*
*/
void SetChecksum(uint16_t aChecksum) { mHeaderChecksum = HostSwap16(aChecksum); }
/**
* This method returns the IPv4 Identification value.
*
* @returns The IPv4 Identification value.
*
*/
uint16_t GetIdentification(void) const { return HostSwap16(mIdentification); }
/**
* This method sets the IPv4 Identification value.
*
* @param[in] aIdentification The IPv4 Identification value.
*
*/
void SetIdentification(uint16_t aIdentification) { mIdentification = HostSwap16(aIdentification); }
/**
* This method returns the IPv4 Time-to-Live value.
*
* @returns The IPv4 Time-to-Live value.
*
*/
uint8_t GetTtl(void) const { return mTtl; }
/**
* This method sets the IPv4 Time-to-Live value.
*
* @param[in] aTtl The IPv4 Time-to-Live value.
*
*/
void SetTtl(uint8_t aTtl) { mTtl = aTtl; }
/**
* This method returns the IPv4 Source address.
*
* @returns A reference to the IPv4 Source address.
*
*/
Address &GetSource(void) { return mSource; }
/**
* This method returns the IPv4 Source address.
*
* @returns A reference to the IPv4 Source address.
*
*/
const Address &GetSource(void) const { return mSource; }
/**
* This method sets the IPv4 Source address.
*
* @param[in] aSource A reference to the IPv4 Source address.
*
*/
void SetSource(const Address &aSource) { mSource = aSource; }
/**
* This method returns the IPv4 Destination address.
*
* @returns A reference to the IPv4 Destination address.
*
*/
Address &GetDestination(void) { return mDestination; }
/**
* This method returns the IPv4 Destination address.
*
* @returns A reference to the IPv4 Destination address.
*
*/
const Address &GetDestination(void) const { return mDestination; }
/**
* This method sets the IPv4 Destination address.
*
* @param[in] aDestination A reference to the IPv4 Destination address.
*
*/
void SetDestination(const Address &aDestination) { mDestination = aDestination; }
/**
* This method parses and validates the IPv4 header from a given message.
*
* The header is read from @p aMessage at offset zero.
*
* @param[in] aMessage The IPv4 message.
*
* @retval kErrorNone Successfully parsed the IPv4 header from @p aMessage.
* @retval kErrorParse Malformed IPv4 header or message (e.g., message does not contained expected payload length).
*
*/
Error ParseFrom(const Message &aMessage);
/**
* This method returns the Df flag in the IPv4 header.
*
* @returns Whether don't fragment flag is set.
*
*/
bool GetDf(void) const { return HostSwap16(mFlagsFargmentOffset) & kFlagsDf; }
/**
* This method returns the Mf flag in the IPv4 header.
*
* @returns Whether more fragments flag is set.
*
*/
bool GetMf(void) const { return HostSwap16(mFlagsFargmentOffset) & kFlagsMf; }
/**
* This method returns the fragment offset in the IPv4 header.
*
* @returns The fragment offset of the IPv4 packet.
*
*/
uint16_t GetFragmentOffset(void) const { return HostSwap16(mFlagsFargmentOffset) & kFragmentOffsetMask; }
private:
// IPv4 header
//
// +---------------+---------------+---------------+---------------+
// |7 6 5 4 3 2 1 0|7 6 5 4 3 2 1 0|7 6 5 4 3 2 1 0|7 6 5 4 3 2 1 0|
// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
// |Version| IHL | DSCP |ECN| Total Length |
// | Identification |Flags| Fragment Offset |
// | TTL | Protocol | Header Checksum |
// | Source IP Address |
// | Dest IP Address |
// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
static constexpr uint8_t kVersion4 = 0x40; // Use with `mVersIhl`
static constexpr uint8_t kVersionMask = 0xf0; // Use with `mVersIhl`
static constexpr uint8_t kIhlMask = 0x0f; // Use with `mVersIhl`
static constexpr uint8_t kDscpOffset = 2; // Use with `mDscpEcn`
static constexpr uint16_t kDscpMask = 0xfc; // Use with `mDscpEcn`
static constexpr uint8_t kEcnOffset = 0; // Use with `mDscpEcn`
static constexpr uint8_t kEcnMask = 0x03; // Use with `mDscpEcn`
static constexpr uint16_t kFlagsMask = 0xe000; // Use with `mFlagsFragmentOffset`
static constexpr uint16_t kFlagsDf = 0x4000; // Use with `mFlagsFragmentOffset`
static constexpr uint16_t kFlagsMf = 0x2000; // Use with `mFlagsFragmentOffset`
static constexpr uint16_t kFragmentOffsetMask = 0x1fff; // Use with `mFlagsFragmentOffset`
static constexpr uint32_t kVersIhlInit = 0x45; // Version 4, Header length = 5x8 bytes.
uint8_t mVersIhl;
uint8_t mDscpEcn;
uint16_t mTotalLength;
uint16_t mIdentification;
uint16_t mFlagsFargmentOffset;
uint8_t mTtl;
uint8_t mProtocol;
uint16_t mHeaderChecksum;
Address mSource;
Address mDestination;
} OT_TOOL_PACKED_END;
/**
* This class implements ICMP(v4).
* Note: ICMP(v4) messages will only be generated / handled by NAT64. So only header defination is required.
*
*/
class Icmp
{
public:
/**
* This class represents an IPv4 ICMP header.
*
*/
OT_TOOL_PACKED_BEGIN
class Header : public Clearable<Header>
{
public:
static constexpr uint16_t kChecksumFieldOffset = 2;
// A few ICMP types, only the ICMP types work with NAT64 are listed here.
enum Type : uint8_t
{
kTypeEchoReply = 0,
kTypeDestinationUnreachable = 3,
kTypeEchoRequest = 8,
kTypeTimeExceeded = 11,
};
enum Code : uint8_t
{
kCodeNone = 0,
// Destination Unreachable codes
kCodeNetworkUnreachable = 0,
kCodeHostUnreachable = 1,
kCodeProtocolUnreachable = 2,
kCodePortUnreachable = 3,
kCodeSourceRouteFailed = 5,
kCodeNetworkUnknown = 6,
kCodeHostUnknown = 7,
};
/**
* This method returns the type of the ICMP message.
*
* @returns The type field of the ICMP message.
*
*/
uint8_t GetType(void) const { return mType; }
/**
* This method sets the type of the ICMP message.
*
* @param[in] aType The type of the ICMP message.
*
*/
void SetType(uint8_t aType) { mType = aType; }
/**
* This method returns the code of the ICMP message.
*
* @returns The code field of the ICMP message.
*
*/
uint8_t GetCode(void) const { return mCode; }
/**
* This method sets the code of the ICMP message.
*
* @param[in] aCode The code of the ICMP message.
*
*/
void SetCode(uint8_t aCode) { mCode = aCode; }
/**
* This method sets the checksum field in the ICMP message.
*
* @returns The checksum of the ICMP message.
*
*/
uint16_t GetChecksum(void) const { return HostSwap16(mChecksum); }
/**
* This method sets the checksum field in the ICMP message.
*
* @param[in] aChecksum The checksum of the ICMP message.
*
*/
void SetChecksum(uint16_t aChecksum) { mChecksum = HostSwap16(aChecksum); }
/**
* This method returns the rest of header field in the ICMP message.
*
* @returns The rest of header field in the ICMP message. The returned buffer has 4 octets.
*
*/
const uint8_t *GetRestOfHeader(void) const { return mRestOfHeader; }
/**
* This method sets the rest of header field in the ICMP message.
*
* @param[in] aRestOfHeader The rest of header field in the ICMP message. The buffer should have 4 octets.
*
*/
void SetRestOfHeader(const uint8_t *aRestOfheader)
{
memcpy(mRestOfHeader, aRestOfheader, sizeof(mRestOfHeader));
}
private:
uint8_t mType;
uint8_t mCode;
uint16_t mChecksum;
uint8_t mRestOfHeader[4];
} OT_TOOL_PACKED_END;
};
// Internet Protocol Numbers
static constexpr uint8_t kProtoTcp = Ip6::kProtoTcp; ///< Transmission Control Protocol
static constexpr uint8_t kProtoUdp = Ip6::kProtoUdp; ///< User Datagram
static constexpr uint8_t kProtoIcmp = 1; ///< ICMP for IPv4
using Tcp = Ip6::Tcp; // TCP in IPv4 is the same as TCP in IPv6
using Udp = Ip6::Udp; // UDP in IPv4 is the same as UDP in IPv6
/**
* @}
*
*/
} // namespace Ip4
DefineCoreType(otIp4Address, Ip4::Address);
DefineCoreType(otIp4Cidr, Ip4::Cidr);
} // namespace ot
#endif // IP4_TYPES_HPP_
+4 -4
View File
@@ -42,7 +42,7 @@
#include "common/instance.hpp"
#include "common/numeric_limits.hpp"
#include "common/random.hpp"
#include "net/ip4_address.hpp"
#include "net/ip4_types.hpp"
#include "net/netif.hpp"
using ot::Encoding::BigEndian::HostSwap32;
@@ -124,10 +124,10 @@ uint8_t Prefix::MatchLength(const uint8_t *aPrefixA, const uint8_t *aPrefixB, ui
return matchedLength;
}
bool Prefix::IsValidNat64(void) const
bool Prefix::IsValidNat64PrefixLength(uint8_t aLength)
{
return (mLength == 32) || (mLength == 40) || (mLength == 48) || (mLength == 56) || (mLength == 64) ||
(mLength == 96);
return (aLength == 32) || (aLength == 40) || (aLength == 48) || (aLength == 56) || (aLength == 64) ||
(aLength == 96);
}
Prefix::InfoString Prefix::ToString(void) const
+20 -2
View File
@@ -46,11 +46,16 @@
#include "common/equatable.hpp"
#include "common/string.hpp"
#include "mac/mac_types.hpp"
#include "net/ip4_address.hpp"
using ot::Encoding::BigEndian::HostSwap16;
namespace ot {
namespace Ip4 {
// Forward declaration for SynthesizeFromIp4Address
class Address;
} // namespace Ip4
namespace Ip6 {
/**
@@ -301,6 +306,19 @@ public:
*/
static uint8_t MatchLength(const uint8_t *aPrefixA, const uint8_t *aPrefixB, uint8_t aMaxSize);
/**
* This method indicates whether or not a given prefix length is valid for use as a NAT64 prefix.
*
* A NAT64 prefix must have one of the following lengths: 32, 40, 48, 56, 64, or 96 (per RFC 6502).
*
* @param[in] aLength The length of the prefix.
*
* @retval TRUE If the prefix has a valid length for use as a NAT64 prefix.
* @retval FALSE If the prefix does not have a valid length for use as a NAT64 prefix.
*
*/
static bool IsValidNat64PrefixLength(uint8_t aLength);
/**
* This method indicates whether or not the prefix has a valid length for use as a NAT64 prefix.
*
@@ -310,7 +328,7 @@ public:
* @retval FALSE If the prefix does not have a valid length for use as a NAT64 prefix.
*
*/
bool IsValidNat64(void) const;
bool IsValidNat64(void) const { return IsValidNat64PrefixLength(mLength); }
/**
* This method converts the prefix to a string.
+21
View File
@@ -431,6 +431,27 @@ target_link_libraries(ot-test-hmac-sha256
add_test(NAME ot-test-hmac-sha256 COMMAND ot-test-hmac-sha256)
add_executable(ot-test-ip4-header
test_ip4_header.cpp
)
target_include_directories(ot-test-ip4-header
PRIVATE
${COMMON_INCLUDES}
)
target_compile_options(ot-test-ip4-header
PRIVATE
${COMMON_COMPILE_OPTIONS}
)
target_link_libraries(ot-test-ip4-header
PRIVATE
${COMMON_LIBS}
)
add_test(NAME ot-test-ip4-header COMMAND ot-test-ip4-header)
add_executable(ot-test-ip6-header
test_ip6_header.cpp
)
+5
View File
@@ -128,6 +128,7 @@ check_PROGRAMS += \
ot-test-heap-string \
ot-test-hkdf-sha256 \
ot-test-hmac-sha256 \
ot-test-ip4-header \
ot-test-ip6-header \
ot-test-ip-address \
ot-test-link-quality \
@@ -262,6 +263,10 @@ ot_test_hmac_sha256_LDADD = $(COMMON_LDADD)
ot_test_hmac_sha256_LIBTOOLFLAGS = $(COMMON_LIBTOOLFLAGS)
ot_test_hmac_sha256_SOURCES = $(COMMON_SOURCES) test_hmac_sha256.cpp
ot_test_ip4_header_LDADD = $(COMMON_LDADD)
ot_test_ip4_header_LIBTOOLFLAGS = $(COMMON_LIBTOOLFLAGS)
ot_test_ip4_header_SOURCES = $(COMMON_SOURCES) test_ip4_header.cpp
ot_test_ip6_header_LDADD = $(COMMON_LDADD)
ot_test_ip6_header_LIBTOOLFLAGS = $(COMMON_LIBTOOLFLAGS)
ot_test_ip6_header_SOURCES = $(COMMON_SOURCES) test_ip6_header.cpp
+191 -15
View File
@@ -32,6 +32,7 @@
#include "common/random.hpp"
#include "net/checksum.hpp"
#include "net/icmp6.hpp"
#include "net/ip4_types.hpp"
#include "net/udp6.hpp"
#include "test_platform.h"
@@ -75,11 +76,7 @@ uint16_t CalculateChecksum(const Ip6::Address &aSource,
const Message & aMessage)
{
// This method calculates the checksum over an IPv6 message.
enum : uint16_t
{
kMaxPayload = 1024,
};
constexpr uint16_t kMaxPayload = 1024;
OT_TOOL_PACKED_BEGIN
struct PseudoHeader
@@ -112,6 +109,45 @@ uint16_t CalculateChecksum(const Ip6::Address &aSource,
return CalculateChecksum(&data, sizeof(PseudoHeader) + payloadLength);
}
uint16_t CalculateChecksum(const Ip4::Address &aSource,
const Ip4::Address &aDestination,
uint8_t aIpProto,
const Message & aMessage)
{
// This method calculates the checksum over an IPv4 message.
constexpr uint16_t kMaxPayload = 1024;
OT_TOOL_PACKED_BEGIN
struct PseudoHeader
{
Ip4::Address mSource;
Ip4::Address mDestination;
uint16_t mPayloadLength;
uint16_t mProtocol;
} OT_TOOL_PACKED_END;
OT_TOOL_PACKED_BEGIN
struct ChecksumData
{
PseudoHeader mPseudoHeader;
uint8_t mPayload[kMaxPayload];
} OT_TOOL_PACKED_END;
ChecksumData data;
uint16_t payloadLength;
payloadLength = aMessage.GetLength() - aMessage.GetOffset();
data.mPseudoHeader.mSource = aSource;
data.mPseudoHeader.mDestination = aDestination;
data.mPseudoHeader.mProtocol = Encoding::BigEndian::HostSwap16(aIpProto);
data.mPseudoHeader.mPayloadLength = Encoding::BigEndian::HostSwap16(payloadLength);
SuccessOrQuit(aMessage.Read(aMessage.GetOffset(), data.mPayload, payloadLength));
return CalculateChecksum(&data, sizeof(PseudoHeader) + payloadLength);
}
void CorruptMessage(Message &aMessage)
{
// Change a random bit in the message.
@@ -133,11 +169,8 @@ void CorruptMessage(Message &aMessage)
void TestUdpMessageChecksum(void)
{
enum : uint16_t
{
kMinSize = sizeof(Ip6::Udp::Header),
kMaxSize = kBufferSize * 3 + 24,
};
constexpr uint16_t kMinSize = sizeof(Ip6::Udp::Header);
constexpr uint16_t kMaxSize = kBufferSize * 3 + 24;
const char *kSourceAddress = "fd00:1122:3344:5566:7788:99aa:bbcc:ddee";
const char *kDestAddress = "fd01:2345:6789:abcd:ef01:2345:6789:abcd";
@@ -204,11 +237,8 @@ void TestUdpMessageChecksum(void)
void TestIcmp6MessageChecksum(void)
{
enum : uint16_t
{
kMinSize = sizeof(Ip6::Icmp::Header),
kMaxSize = kBufferSize * 3 + 24,
};
constexpr uint16_t kMinSize = sizeof(Ip6::Icmp::Header);
constexpr uint16_t kMaxSize = kBufferSize * 3 + 24;
const char *kSourceAddress = "fd00:feef:dccd:baab:9889:7667:5444:3223";
const char *kDestAddress = "fd01:abab:beef:cafe:1234:5678:9abc:0";
@@ -274,6 +304,149 @@ void TestIcmp6MessageChecksum(void)
}
}
void TestTcp4MessageChecksum(void)
{
constexpr size_t kMinSize = sizeof(Ip4::Tcp::Header);
constexpr size_t kMaxSize = kBufferSize * 3 + 24;
const char *kSourceAddress = "12.34.56.78";
const char *kDestAddress = "87.65.43.21";
Ip4::Address sourceAddress;
Ip4::Address destAddress;
Instance *instance = static_cast<Instance *>(testInitInstance());
VerifyOrQuit(instance != nullptr);
SuccessOrQuit(sourceAddress.FromString(kSourceAddress));
SuccessOrQuit(destAddress.FromString(kDestAddress));
for (uint16_t size = kMinSize; size <= kMaxSize; size++)
{
Message * message = instance->Get<Ip6::Ip6>().NewMessage(sizeof(Ip4::Tcp::Header));
Ip4::Tcp::Header tcpHeader;
VerifyOrQuit(message != nullptr, "Ip6::NewMesssage() failed");
SuccessOrQuit(message->SetLength(size));
// Write TCP header with a random payload.
Random::NonCrypto::FillBuffer(reinterpret_cast<uint8_t *>(&tcpHeader), sizeof(tcpHeader));
message->Write(0, tcpHeader);
if (size > sizeof(tcpHeader))
{
uint8_t buffer[kMaxSize];
uint16_t payloadSize = size - sizeof(tcpHeader);
Random::NonCrypto::FillBuffer(buffer, payloadSize);
message->WriteBytes(sizeof(tcpHeader), &buffer[0], payloadSize);
}
// Verify that the `Checksum::UpdateMessageChecksum` correctly
// updates the checksum field in the UDP header on the message.
Checksum::UpdateMessageChecksum(*message, sourceAddress, destAddress, Ip4::kProtoTcp);
SuccessOrQuit(message->Read(message->GetOffset(), tcpHeader));
VerifyOrQuit(tcpHeader.GetChecksum() != 0);
// Verify that the calculated UDP checksum is valid.
VerifyOrQuit(CalculateChecksum(sourceAddress, destAddress, Ip4::kProtoTcp, *message) == 0xffff);
message->Free();
}
}
void TestUdp4MessageChecksum(void)
{
constexpr uint16_t kMinSize = sizeof(Ip4::Udp::Header);
constexpr uint16_t kMaxSize = kBufferSize * 3 + 24;
const char *kSourceAddress = "12.34.56.78";
const char *kDestAddress = "87.65.43.21";
Ip4::Address sourceAddress;
Ip4::Address destAddress;
Instance *instance = static_cast<Instance *>(testInitInstance());
SuccessOrQuit(sourceAddress.FromString(kSourceAddress));
SuccessOrQuit(destAddress.FromString(kDestAddress));
VerifyOrQuit(instance != nullptr);
for (uint16_t size = kMinSize; size <= kMaxSize; size++)
{
Message * message = instance->Get<Ip6::Ip6>().NewMessage(sizeof(Ip4::Udp::Header));
Ip4::Udp::Header udpHeader;
VerifyOrQuit(message != nullptr, "Ip6::NewMesssage() failed");
SuccessOrQuit(message->SetLength(size));
// Write UDP header with a random payload.
Random::NonCrypto::FillBuffer(reinterpret_cast<uint8_t *>(&udpHeader), sizeof(udpHeader));
udpHeader.SetChecksum(0);
message->Write(0, udpHeader);
if (size > sizeof(udpHeader))
{
uint8_t buffer[kMaxSize];
uint16_t payloadSize = size - sizeof(udpHeader);
Random::NonCrypto::FillBuffer(buffer, payloadSize);
message->WriteBytes(sizeof(udpHeader), &buffer[0], payloadSize);
}
// Verify that the `Checksum::UpdateMessageChecksum` correctly
// updates the checksum field in the UDP header on the message.
Checksum::UpdateMessageChecksum(*message, sourceAddress, destAddress, Ip4::kProtoUdp);
SuccessOrQuit(message->Read(message->GetOffset(), udpHeader));
VerifyOrQuit(udpHeader.GetChecksum() != 0);
// Verify that the calculated UDP checksum is valid.
VerifyOrQuit(CalculateChecksum(sourceAddress, destAddress, Ip4::kProtoUdp, *message) == 0xffff);
message->Free();
}
}
void TestIcmp4MessageChecksum(void)
{
// A captured ICMP echo request (ping) message. Checksum field is set to zero.
const uint8_t kExampleIcmpMessage[] = "\x08\x00\x00\x00\x67\x2e\x00\x00\x62\xaf\xf1\x61\x00\x04\xfc\x24"
"\x08\x09\x0a\x0b\x0c\x0d\x0e\x0f\x10\x11\x12\x13\x14\x15\x16\x17"
"\x18\x19\x1a\x1b\x1c\x1d\x1e\x1f\x20\x21\x22\x23\x24\x25\x26\x27"
"\x28\x29\x2a\x2b\x2c\x2d\x2e\x2f\x30\x31\x32\x33\x34\x35\x36\x37";
uint16_t kChecksumForExampleMessage = 0x5594;
Instance *instance = static_cast<Instance *>(testInitInstance());
Message * message = instance->Get<Ip6::Ip6>().NewMessage(sizeof(kExampleIcmpMessage));
Ip4::Address source;
Ip4::Address dest;
uint8_t mPayload[sizeof(kExampleIcmpMessage)];
Ip4::Icmp::Header icmpHeader;
message->AppendBytes(kExampleIcmpMessage, sizeof(kExampleIcmpMessage));
// Random IPv4 address, ICMP message checksum does not include a presudo header like TCP and UDP.
source.mFields.m32 = 0x12345678;
dest.mFields.m32 = 0x87654321;
Checksum::UpdateMessageChecksum(*message, source, dest, Ip4::kProtoIcmp);
message->Read(0, icmpHeader);
VerifyOrQuit(icmpHeader.GetChecksum() == kChecksumForExampleMessage);
SuccessOrQuit(message->Read(message->GetOffset(), mPayload, sizeof(mPayload)));
VerifyOrQuit(CalculateChecksum(mPayload, sizeof(mPayload)) == 0xffff);
}
class ChecksumTester
{
public:
@@ -300,6 +473,9 @@ int main(void)
ot::ChecksumTester::TestExampleVector();
ot::TestUdpMessageChecksum();
ot::TestIcmp6MessageChecksum();
ot::TestTcp4MessageChecksum();
ot::TestUdp4MessageChecksum();
ot::TestIcmp4MessageChecksum();
printf("All tests passed\n");
return 0;
}
+132
View File
@@ -0,0 +1,132 @@
/*
* Copyright (c) 2022, 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.
*/
#include "common/encoding.hpp"
#include "net/ip4_types.hpp"
#include "test_util.hpp"
using ot::Encoding::BigEndian::ReadUint16;
namespace ot {
namespace Ip4 {
void VerifyEcnDscp(const Header &aHeader, uint8_t aDscp, Ecn aEcn)
{
uint8_t expectedDscpEcn = static_cast<uint8_t>((aDscp << 2) + aEcn);
printf("%02x {dscp:%d, ecn:%d}\n", aHeader.GetDscpEcn(), aHeader.GetDscp(), aHeader.GetEcn());
VerifyOrQuit(aHeader.GetDscp() == aDscp);
VerifyOrQuit(aHeader.GetEcn() == aEcn);
VerifyOrQuit(aHeader.GetDscpEcn() == expectedDscpEcn);
}
void TestIp4Header(void)
{
static constexpr uint16_t kTotalLength = 84;
static constexpr uint8_t kTtl = 64;
const uint8_t kDscps[] = {0x0, 0x1, 0x3, 0xf, 0x30, 0x2f, 0x3f};
const Ecn kEcns[] = {Ecn::kEcnNotCapable, Ecn::kEcnCapable0, Ecn::kEcnCapable1, Ecn::kEcnMarked};
const uint8_t kExampleIp4Header[] = "\x45\x00\x00\x54\x23\xed\x00\x00\x40\x01\x41\xd1\x0a\x00\x00\xeb"
"\x0a\x00\x00\x01";
Header header;
Address source;
Address destination;
const uint8_t *headerBytes = reinterpret_cast<const uint8_t *>(&header);
SuccessOrQuit(source.FromString("10.0.0.235"), "Address::FromString() failed");
SuccessOrQuit(destination.FromString("10.0.0.1"), "Address::FromString() failed");
header.InitVersionIhl();
header.Clear();
header.InitVersionIhl();
VerifyOrQuit(header.IsValid());
VerifyOrQuit(header.GetTotalLength() == 0);
VerifyOrQuit(header.GetProtocol() == 0);
VerifyOrQuit(header.GetTtl() == 0);
VerifyOrQuit(header.GetSource().mFields.m32 == 0);
VerifyOrQuit(header.GetDestination().mFields.m32 == 0);
VerifyOrQuit(header.GetFragmentOffset() == 0);
header.SetTotalLength(kTotalLength);
header.SetProtocol(Ip4::kProtoIcmp);
header.SetTtl(kTtl);
header.SetSource(source);
header.SetDestination(destination);
VerifyOrQuit(header.IsValid());
VerifyOrQuit(header.GetProtocol() == kProtoIcmp);
VerifyOrQuit(header.GetTtl() == kTtl);
VerifyOrQuit(header.GetSource() == source);
VerifyOrQuit(header.GetDestination() == destination);
// Verify the offsets to different fields.
VerifyOrQuit(ReadUint16(headerBytes + Header::kTotalLengthOffset) == kTotalLength, "kTotalLength is incorrect");
VerifyOrQuit(headerBytes[Header::kProtocolOffset] == kProtoIcmp, "kProtocol is incorrect");
VerifyOrQuit(headerBytes[Header::kTtlOffset] == kTtl, "kTtl is incorrect");
VerifyOrQuit(memcmp(&headerBytes[Header::kSourceAddressOffset], &source, sizeof(source)) == 0,
"Source address is incorrect");
VerifyOrQuit(memcmp(&headerBytes[Header::kDestinationAddressOffset], &destination, sizeof(destination)) == 0,
"Destination address is incorrect");
for (uint8_t dscp : kDscps)
{
for (Ecn ecn : kEcns)
{
printf("Expecting {dscp:%-2d, ecn:%d} => ", dscp, ecn);
header.SetEcn(ecn);
header.SetDscp(dscp);
VerifyEcnDscp(header, dscp, ecn);
}
}
memcpy(&header, kExampleIp4Header, sizeof(header));
VerifyOrQuit(header.IsValid());
VerifyOrQuit(memcmp(&headerBytes[Header::kSourceAddressOffset], &source, sizeof(source)) == 0,
"Source address is incorrect");
VerifyOrQuit(memcmp(&headerBytes[Header::kDestinationAddressOffset], &destination, sizeof(destination)) == 0,
"Destination address is incorrect");
VerifyOrQuit(ReadUint16(headerBytes + Header::kTotalLengthOffset) == kTotalLength, "kTotalLength is incorrect");
VerifyOrQuit(headerBytes[Header::kProtocolOffset] == kProtoIcmp, "kProtocol is incorrect");
VerifyOrQuit(headerBytes[Header::kTtlOffset] == kTtl, "kTtl is incorrect");
}
} // namespace Ip4
} // namespace ot
int main(void)
{
ot::Ip4::TestIp4Header();
printf("All tests passed\n");
return 0;
}
+71 -2
View File
@@ -1,5 +1,5 @@
/*
* Copyright (c) 2019, The OpenThread Authors.
* Copyright (c) 2019-2022, The OpenThread Authors.
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
@@ -28,7 +28,8 @@
#include <limits.h>
#include "net/ip4_address.hpp"
#include "common/encoding.hpp"
#include "net/ip4_types.hpp"
#include "net/ip6_address.hpp"
#include "test_util.h"
@@ -444,6 +445,73 @@ void TestIp4Ip6Translation(void)
VerifyOrQuit(address == expectedAddress, "Ip6::SynthesizeFromIp4Address() failed");
}
for (const TestCase &testCase : kTestCases)
{
const ot::Ip4::Address expectedAddress = ip4Address;
ot::Ip4::Address address;
ot::Ip6::Address ip6Address;
SuccessOrQuit(ip6Address.FromString(testCase.mIp6Address));
address.ExtractFromIp6Address(testCase.mLength, ip6Address);
printf("Ipv6Address: %-36s IPv4Addr: %-12s Expected: %s\n", testCase.mIp6Address,
address.ToString().AsCString(), expectedAddress.ToString().AsCString());
VerifyOrQuit(address == expectedAddress, "Ip4::ExtractFromIp6Address() failed");
}
}
void TestIp4Cidr(void)
{
using ot::Encoding::BigEndian::HostSwap32;
struct TestCase
{
const char * mNetwork;
const uint8_t mLength;
const uint32_t mHost;
const char * mOutcome;
};
const TestCase kTestCases[] = {
{"172.16.12.34", 32, 0x12345678, "172.16.12.34"}, {"172.16.12.34", 31, 0x12345678, "172.16.12.34"},
{"172.16.12.34", 30, 0x12345678, "172.16.12.32"}, {"172.16.12.34", 29, 0x12345678, "172.16.12.32"},
{"172.16.12.34", 28, 0x12345678, "172.16.12.40"}, {"172.16.12.34", 27, 0x12345678, "172.16.12.56"},
{"172.16.12.34", 26, 0x12345678, "172.16.12.56"}, {"172.16.12.34", 25, 0x12345678, "172.16.12.120"},
{"172.16.12.34", 24, 0x12345678, "172.16.12.120"}, {"172.16.12.34", 23, 0x12345678, "172.16.12.120"},
{"172.16.12.34", 22, 0x12345678, "172.16.14.120"}, {"172.16.12.34", 21, 0x12345678, "172.16.14.120"},
{"172.16.12.34", 20, 0x12345678, "172.16.6.120"}, {"172.16.12.34", 19, 0x12345678, "172.16.22.120"},
{"172.16.12.34", 18, 0x12345678, "172.16.22.120"}, {"172.16.12.34", 17, 0x12345678, "172.16.86.120"},
{"172.16.12.34", 16, 0x12345678, "172.16.86.120"}, {"172.16.12.34", 15, 0x12345678, "172.16.86.120"},
{"172.16.12.34", 14, 0x12345678, "172.16.86.120"}, {"172.16.12.34", 13, 0x12345678, "172.20.86.120"},
{"172.16.12.34", 12, 0x12345678, "172.20.86.120"}, {"172.16.12.34", 11, 0x12345678, "172.20.86.120"},
{"172.16.12.34", 10, 0x12345678, "172.52.86.120"}, {"172.16.12.34", 9, 0x12345678, "172.52.86.120"},
{"172.16.12.34", 8, 0x12345678, "172.52.86.120"}, {"172.16.12.34", 7, 0x12345678, "172.52.86.120"},
{"172.16.12.34", 6, 0x12345678, "174.52.86.120"}, {"172.16.12.34", 5, 0x12345678, "170.52.86.120"},
{"172.16.12.34", 4, 0x12345678, "162.52.86.120"}, {"172.16.12.34", 3, 0x12345678, "178.52.86.120"},
{"172.16.12.34", 2, 0x12345678, "146.52.86.120"}, {"172.16.12.34", 1, 0x12345678, "146.52.86.120"},
{"172.16.12.34", 0, 0x12345678, "18.52.86.120"},
};
for (const TestCase &testCase : kTestCases)
{
ot::Ip4::Address network;
ot::Ip4::Cidr cidr;
ot::Ip4::Address generated;
network.FromString(testCase.mNetwork);
cidr.mAddress = network;
cidr.mLength = testCase.mLength;
generated.SynthesizeFromCidrAndHost(cidr, testCase.mHost);
printf("CIDR: %-18s HostID: %-8x Host: %-14s Expected: %s\n", cidr.ToString().AsCString(), testCase.mHost,
generated.ToString().AsCString(), testCase.mOutcome);
VerifyOrQuit(strcmp(generated.ToString().AsCString(), testCase.mOutcome) == 0,
"Ip4::Address::SynthesizeFromCidrAndHost() failed");
}
}
int main(void)
@@ -453,6 +521,7 @@ int main(void)
TestIp6AddressFromString();
TestIp6Prefix();
TestIp4Ip6Translation();
TestIp4Cidr();
printf("All tests passed\n");
return 0;
}