[net] add 'Checksum' class (#5434)

This commit adds `Checksum` class which implements IP checksum
calculation and verification (all related code from other modules is
now moved to `Checksum` class). The new checksum calculation allows
any message chunk size (removes a limitation with previous model
of requiring even sized chunks). This commit also adds a unit test
`test_checksum` covering the behavior of `Checksum` methods.
This commit is contained in:
Abtin Keshavarzian
2020-09-01 14:27:50 -07:00
committed by GitHub
parent 398aaa5256
commit d16d202179
19 changed files with 661 additions and 202 deletions
+1
View File
@@ -228,6 +228,7 @@ LOCAL_SRC_FILES := \
src/core/meshcop/meshcop_tlvs.cpp \
src/core/meshcop/panid_query_client.cpp \
src/core/meshcop/timestamp.cpp \
src/core/net/checksum.cpp \
src/core/net/dhcp6_client.cpp \
src/core/net/dhcp6_server.cpp \
src/core/net/dns_client.cpp \
+2
View File
@@ -440,6 +440,8 @@ openthread_core_files = [
"meshcop/panid_query_client.hpp",
"meshcop/timestamp.cpp",
"meshcop/timestamp.hpp",
"net/checksum.cpp",
"net/checksum.hpp",
"net/dhcp6.hpp",
"net/dhcp6_client.cpp",
"net/dhcp6_client.hpp",
+1
View File
@@ -156,6 +156,7 @@ set(COMMON_SOURCES
meshcop/meshcop_tlvs.cpp
meshcop/panid_query_client.cpp
meshcop/timestamp.cpp
net/checksum.cpp
net/dhcp6_client.cpp
net/dhcp6_server.cpp
net/dns_client.cpp
+2
View File
@@ -198,6 +198,7 @@ SOURCES_COMMON = \
meshcop/meshcop_tlvs.cpp \
meshcop/panid_query_client.cpp \
meshcop/timestamp.cpp \
net/checksum.cpp \
net/dhcp6_client.cpp \
net/dhcp6_server.cpp \
net/dns_client.cpp \
@@ -415,6 +416,7 @@ HEADERS_COMMON = \
meshcop/meshcop_tlvs.hpp \
meshcop/panid_query_client.hpp \
meshcop/timestamp.hpp \
net/checksum.hpp \
net/dhcp6.hpp \
net/dhcp6_client.hpp \
net/dhcp6_server.hpp \
+1 -35
View File
@@ -38,6 +38,7 @@
#include "common/instance.hpp"
#include "common/locator-getters.hpp"
#include "common/logging.hpp"
#include "net/checksum.hpp"
#include "net/ip6.hpp"
namespace ot {
@@ -631,41 +632,6 @@ void Message::SetLinkInfo(const ThreadLinkInfo &aLinkInfo)
#endif
}
uint16_t Message::UpdateChecksum(uint16_t aChecksum, uint16_t aValue)
{
uint16_t result = aChecksum + aValue;
return result + (result < aChecksum);
}
uint16_t Message::UpdateChecksum(uint16_t aChecksum, const void *aBuf, uint16_t aLength)
{
const uint8_t *bytes = reinterpret_cast<const uint8_t *>(aBuf);
for (int i = 0; i < aLength; i++)
{
aChecksum = UpdateChecksum(aChecksum, (i & 1) ? bytes[i] : static_cast<uint16_t>(bytes[i] << 8));
}
return aChecksum;
}
uint16_t Message::UpdateChecksum(uint16_t aChecksum, uint16_t aOffset, uint16_t aLength) const
{
Chunk chunk;
OT_ASSERT(aOffset + aLength <= GetLength());
GetFirstChunk(aOffset, aLength, chunk);
while (chunk.GetLength() > 0)
{
aChecksum = Message::UpdateChecksum(aChecksum, chunk.GetData(), chunk.GetLength());
GetNextChunk(aLength, chunk);
}
return aChecksum;
}
bool Message::IsTimeSync(void) const
{
#if OPENTHREAD_CONFIG_TIME_SYNC_ENABLE
+1 -35
View File
@@ -228,6 +228,7 @@ protected:
*/
class Message : public Buffer
{
friend class Checksum;
friend class MessagePool;
friend class MessageQueue;
friend class PriorityQueue;
@@ -836,41 +837,6 @@ public:
*/
void SetLinkInfo(const ThreadLinkInfo &aLinkInfo);
/**
* This static method updates a checksum.
*
* @param[in] aChecksum The checksum value to update.
* @param[in] aValue The 16-bit value to update @p aChecksum with.
*
* @returns The updated checksum.
*
*/
static uint16_t UpdateChecksum(uint16_t aChecksum, uint16_t aValue);
/**
* This static method updates a checksum.
*
* @param[in] aChecksum The checksum value to update.
* @param[in] aBuf A pointer to a buffer.
* @param[in] aLength The number of bytes in @p aBuf.
*
* @returns The updated checksum.
*
*/
static uint16_t UpdateChecksum(uint16_t aChecksum, const void *aBuf, uint16_t aLength);
/**
* This method is used to update a checksum value.
*
* @param[in] aChecksum Initial checksum value.
* @param[in] aOffset Byte offset within the message to begin checksum computation.
* @param[in] aLength Number of bytes to compute the checksum over.
*
* @retval The updated checksum value.
*
*/
uint16_t UpdateChecksum(uint16_t aChecksum, uint16_t aOffset, uint16_t aLength) const;
/**
* This method returns a pointer to the message queue (if any) where this message is queued.
*
+155
View File
@@ -0,0 +1,155 @@
/*
* Copyright (c) 2020, The OpenThread Authors.
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* 3. Neither the name of the copyright holder nor the
* names of its contributors may be used to endorse or promote products
* derived from this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
* POSSIBILITY OF SUCH DAMAGE.
*/
/**
* @file
* This file implements checksum calculation.
*/
#include "checksum.hpp"
#include "common/code_utils.hpp"
#include "common/message.hpp"
#include "net/icmp6.hpp"
#include "net/udp6.hpp"
namespace ot {
void Checksum::AddUint8(uint8_t aUint8)
{
uint16_t newValue = mValue;
// BigEndian encoding: Even index is MSB and odd index is LSB.
newValue += mAtOddIndex ? aUint8 : (static_cast<uint16_t>(aUint8) << 8);
// Calculate one's complement sum.
if (newValue < mValue)
{
newValue++;
}
mValue = newValue;
mAtOddIndex = !mAtOddIndex;
}
void Checksum::AddUint16(uint16_t aUint16)
{
// BigEndian encoding
AddUint8(static_cast<uint8_t>(aUint16 >> 8));
AddUint8(static_cast<uint8_t>(aUint16 & 0xff));
}
void Checksum::AddData(const uint8_t *aBuffer, uint16_t aLength)
{
for (uint16_t i = 0; i < aLength; i++)
{
AddUint8(aBuffer[i]);
}
}
void Checksum::WriteToMessage(uint16_t aOffset, Message &aMessage) const
{
uint16_t checksum = GetValue();
if (checksum != 0xffff)
{
checksum = ~checksum;
}
checksum = Encoding::BigEndian::HostSwap16(checksum);
aMessage.Write(aOffset, sizeof(checksum), &checksum);
}
void Checksum::Calculate(const Ip6::Address &aSource,
const Ip6::Address &aDestination,
uint8_t aIpProto,
const Message & aMessage)
{
Message::Chunk chunk;
uint16_t length = aMessage.GetLength() - aMessage.GetOffset();
// Pseudo-header for checksum calculation (RFC-2460).
AddData(aSource.GetBytes(), sizeof(Ip6::Address));
AddData(aDestination.GetBytes(), sizeof(Ip6::Address));
AddUint16(length);
AddUint16(static_cast<uint16_t>(aIpProto));
// Add message content (from offset to the end) to checksum.
aMessage.GetFirstChunk(aMessage.GetOffset(), length, chunk);
while (chunk.GetLength() > 0)
{
AddData(chunk.GetData(), chunk.GetLength());
aMessage.GetNextChunk(length, chunk);
}
}
otError Checksum::VerifyMessageChecksum(const Message &aMessage, const Ip6::MessageInfo &aMessageInfo, uint8_t aIpProto)
{
Checksum checksum;
checksum.Calculate(aMessageInfo.GetPeerAddr(), aMessageInfo.GetSockAddr(), aIpProto, aMessage);
return (checksum.GetValue() == kValidRxChecksum) ? OT_ERROR_NONE : OT_ERROR_DROP;
}
void Checksum::UpdateMessageChecksum(Message & aMessage,
const Ip6::Address &aSource,
const Ip6::Address &aDestination,
uint8_t aIpProto)
{
uint16_t headerOffset;
Checksum checksum;
switch (aIpProto)
{
case Ip6::kProtoUdp:
headerOffset = Ip6::Udp::Header::kChecksumFieldOffset;
break;
case Ip6::kProtoIcmp6:
headerOffset = Ip6::Icmp::Header::kChecksumFieldOffset;
break;
default:
ExitNow();
}
checksum.Calculate(aSource, aDestination, aIpProto, aMessage);
checksum.WriteToMessage(aMessage.GetOffset() + headerOffset, aMessage);
exit:
return;
}
} // namespace ot
+117
View File
@@ -0,0 +1,117 @@
/*
* Copyright (c) 2020, The OpenThread Authors.
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* 3. Neither the name of the copyright holder nor the
* names of its contributors may be used to endorse or promote products
* derived from this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
* POSSIBILITY OF SUCH DAMAGE.
*/
/**
* @file
* This file includes definitions for checksum calculation.
*/
#ifndef CHECKSUM_HPP_
#define CHECKSUM_HPP_
#include "openthread-core-config.h"
#include <stdint.h>
#include "common/message.hpp"
#include "net/ip6_address.hpp"
#include "net/ip6_headers.hpp"
#include "net/socket.hpp"
namespace ot {
/**
* This class implements IP checksum calculation and verification.
*
*/
class Checksum
{
friend class ChecksumTester;
public:
/**
* This static method verifies the checksum in a given message (if UDP/ICMP6).
*
* @param[in] aMessage The message to verify its checksum. The `aMessage.GetOffset()` should point to start
* UDP/ICMP6 header.
* @param[in] aMessageInfo The message info associated with @p aMessage.
* @param[in] aIpProto The Internet Protocol value.
*
* @retval OT_ERROR_NONE The checksum is valid if UDP/ICMP6 protocol, or not a UDP/ICMP6 protocol.
* @retval OT_ERROR_DROP The check is not valid and message should be dropped.
*
*/
static otError 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).
*
* @param[inout] 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.
* @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 Ip6::Address &aSource,
const Ip6::Address &aDestination,
uint8_t aIpProto);
private:
Checksum(void)
: mValue(0)
, mAtOddIndex(false)
{
}
uint16_t GetValue(void) const { return mValue; }
void AddUint8(uint8_t aUint8);
void AddUint16(uint16_t aUint16);
void AddData(const uint8_t *aBuffer, uint16_t aLength);
void WriteToMessage(uint16_t aOffset, Message &aMessage) const;
void Calculate(const Ip6::Address &aSource,
const Ip6::Address &aDestination,
uint8_t aIpProto,
const Message & aMessage);
enum : uint16_t
{
kValidRxChecksum = 0xffff,
};
uint16_t mValue;
bool mAtOddIndex;
};
} // namespace ot
#endif // CHECKSUM_HPP_
+4 -25
View File
@@ -39,10 +39,9 @@
#include "common/locator-getters.hpp"
#include "common/logging.hpp"
#include "common/message.hpp"
#include "net/checksum.hpp"
#include "net/ip6.hpp"
using ot::Encoding::BigEndian::HostSwap16;
namespace ot {
namespace Ip6 {
@@ -139,20 +138,13 @@ exit:
otError Icmp::HandleMessage(Message &aMessage, MessageInfo &aMessageInfo)
{
otError error = OT_ERROR_NONE;
uint16_t payloadLength;
Header icmp6Header;
uint16_t checksum;
otError error = OT_ERROR_NONE;
Header icmp6Header;
VerifyOrExit(aMessage.Read(aMessage.GetOffset(), sizeof(icmp6Header), &icmp6Header) == sizeof(icmp6Header),
error = OT_ERROR_PARSE);
payloadLength = aMessage.GetLength() - aMessage.GetOffset();
// verify checksum
checksum = Ip6::ComputePseudoheaderChecksum(aMessageInfo.GetPeerAddr(), aMessageInfo.GetSockAddr(), payloadLength,
kProtoIcmp6);
checksum = aMessage.UpdateChecksum(checksum, aMessage.GetOffset(), payloadLength);
VerifyOrExit(checksum == 0xffff, error = OT_ERROR_PARSE);
SuccessOrExit(error = Checksum::VerifyMessageChecksum(aMessage, aMessageInfo, kProtoIcmp6));
if (icmp6Header.GetType() == Header::kTypeEchoRequest)
{
@@ -244,18 +236,5 @@ exit:
return error;
}
void Icmp::UpdateChecksum(Message &aMessage, uint16_t aChecksum)
{
aChecksum = aMessage.UpdateChecksum(aChecksum, aMessage.GetOffset(), aMessage.GetLength() - aMessage.GetOffset());
if (aChecksum != 0xffff)
{
aChecksum = ~aChecksum;
}
aChecksum = HostSwap16(aChecksum);
aMessage.Write(aMessage.GetOffset() + Header::kChecksumFieldOffset, sizeof(aChecksum), &aChecksum);
}
} // namespace Ip6
} // namespace ot
-9
View File
@@ -297,15 +297,6 @@ public:
*/
otError HandleMessage(Message &aMessage, MessageInfo &aMessageInfo);
/**
* This method updates the ICMPv6 checksum.
*
* @param[in] aMessage A reference to the ICMPv6 message.
* @param[in] aChecksum The pseudo-header checksum value.
*
*/
void UpdateChecksum(Message &aMessage, uint16_t aChecksum);
/**
* This method indicates whether or not ICMPv6 Echo processing is enabled.
*
+2 -36
View File
@@ -40,6 +40,7 @@
#include "common/logging.hpp"
#include "common/message.hpp"
#include "common/random.hpp"
#include "net/checksum.hpp"
#include "net/icmp6.hpp"
#include "net/ip6_address.hpp"
#include "net/ip6_filter.hpp"
@@ -170,26 +171,6 @@ exit:
return error;
}
uint16_t Ip6::UpdateChecksum(uint16_t aChecksum, const Address &aAddress)
{
return Message::UpdateChecksum(aChecksum, aAddress.mFields.m8, sizeof(aAddress));
}
uint16_t Ip6::ComputePseudoheaderChecksum(const Address &aSource,
const Address &aDestination,
uint16_t aLength,
uint8_t aProto)
{
uint16_t checksum;
checksum = Message::UpdateChecksum(0, aLength);
checksum = Message::UpdateChecksum(checksum, static_cast<uint16_t>(aProto));
checksum = UpdateChecksum(checksum, aSource);
checksum = UpdateChecksum(checksum, aDestination);
return checksum;
}
void Ip6::SetReceiveDatagramCallback(otIp6ReceiveCallback aCallback, void *aCallbackContext)
{
mReceiveIp6DatagramCallback = aCallback;
@@ -458,7 +439,6 @@ otError Ip6::SendDatagram(Message &aMessage, MessageInfo &aMessageInfo, uint8_t
otError error = OT_ERROR_NONE;
Header header;
uint16_t payloadLength = aMessage.GetLength();
uint16_t checksum;
header.Init();
header.SetDscp(PriorityToDscp(aMessage.GetPriority()));
@@ -495,21 +475,7 @@ otError Ip6::SendDatagram(Message &aMessage, MessageInfo &aMessageInfo, uint8_t
SuccessOrExit(error = aMessage.Prepend(&header, sizeof(header)));
checksum = ComputePseudoheaderChecksum(header.GetSource(), header.GetDestination(), payloadLength, aIpProto);
switch (aIpProto)
{
case kProtoUdp:
mUdp.UpdateChecksum(aMessage, checksum);
break;
case kProtoIcmp6:
mIcmp.UpdateChecksum(aMessage, checksum);
break;
default:
break;
}
Checksum::UpdateMessageChecksum(aMessage, header.GetSource(), header.GetDestination(), aIpProto);
if (aMessageInfo.GetPeerAddr().IsMulticastLargerThanRealmLocal())
{
+2 -19
View File
@@ -225,22 +225,6 @@ public:
*/
otError HandleDatagram(Message &aMessage, Netif *aNetif, const void *aLinkMessageInfo, bool aFromNcpHost);
/**
* This static method computes the pseudoheader checksum.
*
* @param[in] aSource A reference to the IPv6 source address.
* @param[in] aDestination A reference to the IPv6 destination address.
* @param[in] aLength The IPv6 Payload Length value.
* @param[in] aProto The IPv6 Next Header value.
*
* @returns The pseudoheader checksum.
*
*/
static uint16_t ComputePseudoheaderChecksum(const Address &aSource,
const Address &aDestination,
uint16_t aLength,
uint8_t aProto);
/**
* This method registers a callback to provide received raw IPv6 datagrams.
*
@@ -343,9 +327,8 @@ private:
static void HandleSendQueue(Tasklet &aTasklet);
void HandleSendQueue(void);
static uint8_t PriorityToDscp(Message::Priority aPriority);
static otError GetDatagramPriority(const uint8_t *aData, uint16_t aDataLen, Message::Priority &aPriority);
static uint16_t UpdateChecksum(uint16_t aChecksum, const Address &aAddress);
static uint8_t PriorityToDscp(Message::Priority aPriority);
static otError GetDatagramPriority(const uint8_t *aData, uint16_t aDataLen, Message::Priority &aPriority);
void EnqueueDatagram(Message &aMessage);
otError ProcessReceiveCallback(Message & aMessage,
+8
View File
@@ -525,6 +525,14 @@ public:
*/
typedef String<kIp6AddressStringSize> InfoString;
/**
* This method gets the IPv6 address as a pointer to a byte array.
*
* @returns A pointer to a byte array containing the IPv6 address.
*
*/
const uint8_t *GetBytes(void) const { return mFields.m8; }
/**
* This method indicates whether or not the IPv6 address is the Unspecified Address.
*
+16
View File
@@ -254,6 +254,14 @@ public:
*/
Address &GetSource(void) { return mSource; }
/**
* This method returns the IPv6 Source address.
*
* @returns A reference to the IPv6 Source address.
*
*/
const Address &GetSource(void) const { return mSource; }
/**
* This method sets the IPv6 Source address.
*
@@ -270,6 +278,14 @@ public:
*/
Address &GetDestination(void) { return mDestination; }
/**
* This method returns the IPv6 Destination address.
*
* @returns A reference to the IPv6 Destination address.
*
*/
const Address &GetDestination(void) const { return mDestination; }
/**
* This method sets the IPv6 Destination address.
*
+8 -33
View File
@@ -41,10 +41,9 @@
#include "common/encoding.hpp"
#include "common/instance.hpp"
#include "common/locator-getters.hpp"
#include "net/checksum.hpp"
#include "net/ip6.hpp"
using ot::Encoding::BigEndian::HostSwap16;
namespace ot {
namespace Ip6 {
@@ -364,27 +363,16 @@ exit:
otError Udp::HandleMessage(Message &aMessage, MessageInfo &aMessageInfo)
{
otError error = OT_ERROR_NONE;
Header udpHeader;
uint16_t payloadLength;
uint16_t checksum;
payloadLength = aMessage.GetLength() - aMessage.GetOffset();
// check length
VerifyOrExit(payloadLength >= sizeof(Header), error = OT_ERROR_PARSE);
// verify checksum
checksum = Ip6::ComputePseudoheaderChecksum(aMessageInfo.GetPeerAddr(), aMessageInfo.GetSockAddr(), payloadLength,
kProtoUdp);
checksum = aMessage.UpdateChecksum(checksum, aMessage.GetOffset(), payloadLength);
#ifndef FUZZING_BUILD_MODE_UNSAFE_FOR_PRODUCTION
VerifyOrExit(checksum == 0xffff, error = OT_ERROR_DROP);
#endif
otError error = OT_ERROR_NONE;
Header udpHeader;
VerifyOrExit(aMessage.Read(aMessage.GetOffset(), sizeof(udpHeader), &udpHeader) == sizeof(udpHeader),
error = OT_ERROR_PARSE);
#ifndef FUZZING_BUILD_MODE_UNSAFE_FOR_PRODUCTION
SuccessOrExit(error = Checksum::VerifyMessageChecksum(aMessage, aMessageInfo, kProtoUdp));
#endif
aMessage.MoveOffset(sizeof(udpHeader));
aMessageInfo.mPeerPort = udpHeader.GetSourcePort();
aMessageInfo.mSockPort = udpHeader.GetDestinationPort();
@@ -420,19 +408,6 @@ exit:
return;
}
void Udp::UpdateChecksum(Message &aMessage, uint16_t aChecksum)
{
aChecksum = aMessage.UpdateChecksum(aChecksum, aMessage.GetOffset(), aMessage.GetLength() - aMessage.GetOffset());
if (aChecksum != 0xffff)
{
aChecksum = ~aChecksum;
}
aChecksum = HostSwap16(aChecksum);
aMessage.Write(aMessage.GetOffset() + Header::kChecksumFieldOffset, sizeof(aChecksum), &aChecksum);
}
bool Udp::IsMlePort(uint16_t aPort) const
{
bool isMlePort = (aPort == Mle::kUdpPort);
-9
View File
@@ -528,15 +528,6 @@ public:
*/
void HandlePayload(Message &aMessage, MessageInfo &aMessageInfo);
/**
* This method updates the UDP checksum.
*
* @param[in] aMessage A reference to the UDP message.
* @param[in] aChecksum The pseudo-header checksum value.
*
*/
void UpdateChecksum(Message &aMessage, uint16_t aChecksum);
/**
* This method returns the head of UDP Sockets list.
*
+24 -1
View File
@@ -77,6 +77,29 @@ add_executable(test-child
test_child.cpp
)
add_executable(test-checksum
${COMMON_SOURCES}
test_checksum.cpp
)
target_include_directories(test-checksum
PRIVATE
${COMMON_INCLUDES}
)
target_compile_options(test-checksum
PRIVATE
${COMMON_COMPILE_OPTIONS}
)
target_link_libraries(test-checksum
PRIVATE
${COMMON_LIBS}
)
add_test(NAME test-checksum COMMAND test-checksum)
target_include_directories(test-child
PRIVATE
${COMMON_INCLUDES}
@@ -536,7 +559,7 @@ target_link_libraries(test-timer
add_test(NAME test-timer COMMAND test-timer)
set_target_properties(
test-aes test-child test-child-table test-flash test-heap test-hmac-sha256 test-ip6-address test-link-quality test-linked-list test-lowpan test-mac-frame test-message test-message-queue test-multicast-listeners-table test-netif test-network-data test-pool test-priority-queue test-pskc test-steering-data test-string test-timer
test-aes test-checksum test-child test-child-table test-flash test-heap test-hmac-sha256 test-ip6-address test-link-quality test-linked-list test-lowpan test-mac-frame test-message test-message-queue test-multicast-listeners-table test-netif test-network-data test-pool test-priority-queue test-pskc test-steering-data test-string test-timer
PROPERTIES
C_STANDARD 99
CXX_STANDARD 11
+4
View File
@@ -107,6 +107,7 @@ check_PROGRAMS = \
if OPENTHREAD_ENABLE_FTD
check_PROGRAMS += \
test-aes \
test-checksum \
test-child \
test-child-table \
test-flash \
@@ -171,6 +172,9 @@ COMMON_SOURCES = test_platform.cpp test_util.cpp
test_aes_LDADD = $(COMMON_LDADD)
test_aes_SOURCES = $(COMMON_SOURCES) test_aes.cpp
test_checksum_LDADD = $(COMMON_LDADD)
test_checksum_SOURCES = $(COMMON_SOURCES) test_checksum.cpp
test_child_LDADD = $(COMMON_LDADD)
test_child_SOURCES = $(COMMON_SOURCES) test_child.cpp
+313
View File
@@ -0,0 +1,313 @@
/*
* Copyright (c) 2020, The OpenThread Authors.
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* 3. Neither the name of the copyright holder nor the
* names of its contributors may be used to endorse or promote products
* derived from this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
* POSSIBILITY OF SUCH DAMAGE.
*/
#include "common/encoding.hpp"
#include "common/instance.hpp"
#include "common/message.hpp"
#include "common/random.hpp"
#include "net/checksum.hpp"
#include "net/icmp6.hpp"
#include "net/udp6.hpp"
#include "test_platform.h"
#include "test_util.hpp"
namespace ot {
uint16_t CalculateChecksum(const void *aBuffer, uint16_t aLength)
{
// Calculates checksum over a given buffer data. This implementation
// is inspired by the algorithm from RFC-1071.
uint32_t sum = 0;
const uint8_t *bytes = reinterpret_cast<const uint8_t *>(aBuffer);
while (aLength >= sizeof(uint16_t))
{
sum += Encoding::BigEndian::ReadUint16(bytes);
bytes += sizeof(uint16_t);
aLength -= sizeof(uint16_t);
}
if (aLength > 0)
{
sum += (static_cast<uint32_t>(bytes[0]) << 8);
}
// Fold 32-bit sum to 16 bits.
while (sum >> 16)
{
sum = (sum & 0xffff) + (sum >> 16);
}
return static_cast<uint16_t>(sum & 0xffff);
}
uint16_t CalculateChecksum(const Ip6::Address &aSource,
const Ip6::Address &aDestination,
uint8_t aIpProto,
const Message & aMessage)
{
// This method calculates the checksum over an IPv6 message.
enum : uint16_t
{
kMaxPayload = 1024,
};
OT_TOOL_PACKED_BEGIN
struct PseudoHeader
{
Ip6::Address mSource;
Ip6::Address mDestination;
uint32_t mPayloadLength;
uint32_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::HostSwap32(aIpProto);
data.mPseudoHeader.mPayloadLength = Encoding::BigEndian::HostSwap32(payloadLength);
VerifyOrQuit(aMessage.Read(aMessage.GetOffset(), payloadLength, data.mPayload) == payloadLength,
"Message::Read() failed");
return CalculateChecksum(&data, sizeof(PseudoHeader) + payloadLength);
}
void CorruptMessage(Message &aMessage)
{
// Change a random bit in the message.
uint16_t byteOffset;
uint8_t bitOffset;
uint8_t byte;
byteOffset = Random::NonCrypto::GetUint16InRange(0, aMessage.GetLength());
VerifyOrQuit(aMessage.Read(byteOffset, sizeof(uint8_t), &byte) == sizeof(uint8_t), "Read failed");
bitOffset = Random::NonCrypto::GetUint8InRange(0, CHAR_BIT);
byte ^= (1 << bitOffset);
aMessage.Write(byteOffset, sizeof(uint8_t), &byte);
}
void TestUdpMessageChecksum(void)
{
enum : uint16_t
{
kMinSize = sizeof(Ip6::Udp::Header),
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";
Instance *instance = static_cast<Instance *>(testInitInstance());
VerifyOrQuit(instance != nullptr, "Null OpenThread instance\n");
for (uint16_t size = kMinSize; size <= kMaxSize; size++)
{
Message * message = instance->Get<Ip6::Ip6>().NewMessage(sizeof(Ip6::Udp::Header));
Ip6::Udp::Header udpHeader;
Ip6::MessageInfo messageInfo;
VerifyOrQuit(message != nullptr, "Ip6::NewMesssage() failed");
SuccessOrQuit(message->SetLength(size), "Message::SetLength() failed");
// Write UDP header with a random payload.
Random::NonCrypto::FillBuffer(reinterpret_cast<uint8_t *>(&udpHeader), sizeof(udpHeader));
udpHeader.SetChecksum(0);
message->Write(0, sizeof(udpHeader), &udpHeader);
if (size > sizeof(udpHeader))
{
uint8_t buffer[kMaxSize];
uint16_t payloadSize = size - sizeof(udpHeader);
Random::NonCrypto::FillBuffer(buffer, payloadSize);
message->Write(sizeof(udpHeader), payloadSize, &buffer[0]);
}
SuccessOrQuit(messageInfo.GetSockAddr().FromString(kSourceAddress), "FromString() failed");
SuccessOrQuit(messageInfo.GetPeerAddr().FromString(kDestAddress), "FromString() failed");
// Verify that the `Checksum::UpdateMessageChecksum` correctly
// updates the checksum field in the UDP header on the message.
Checksum::UpdateMessageChecksum(*message, messageInfo.GetSockAddr(), messageInfo.GetPeerAddr(), Ip6::kProtoUdp);
VerifyOrQuit(message->Read(message->GetOffset(), sizeof(udpHeader), &udpHeader) == sizeof(udpHeader),
"Message::Read() failed");
VerifyOrQuit(udpHeader.GetChecksum() != 0, "Failed to update checksum");
// Verify that the calculated UDP checksum is valid.
VerifyOrQuit(
CalculateChecksum(messageInfo.GetSockAddr(), messageInfo.GetPeerAddr(), Ip6::kProtoUdp, *message) == 0xffff,
"UDP checksum does not match expected value");
// Verify that `Checksum::VerifyMessageChecksum()` accepts the
// message and its calculated checksum.
SuccessOrQuit(Checksum::VerifyMessageChecksum(*message, messageInfo, Ip6::kProtoUdp),
"Checksum::VerifyMessageChecksum() failed");
// Corrupt the message and verify that checksum is no longer accepted.
CorruptMessage(*message);
VerifyOrQuit(Checksum::VerifyMessageChecksum(*message, messageInfo, Ip6::kProtoUdp) != OT_ERROR_NONE,
"Checksum passed on corrupted message");
message->Free();
}
}
void TestIcmp6MessageChecksum(void)
{
enum : uint16_t
{
kMinSize = sizeof(Ip6::Icmp::Header),
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";
Instance *instance = static_cast<Instance *>(testInitInstance());
VerifyOrQuit(instance != nullptr, "Null OpenThread instance\n");
for (uint16_t size = kMinSize; size <= kMaxSize; size++)
{
Message * message = instance->Get<Ip6::Ip6>().NewMessage(sizeof(Ip6::Icmp::Header));
Ip6::Icmp::Header icmp6Header;
Ip6::MessageInfo messageInfo;
VerifyOrQuit(message != nullptr, "Ip6::NewMesssage() failed");
SuccessOrQuit(message->SetLength(size), "Message::SetLength() failed");
// Write ICMP6 header with a random payload.
Random::NonCrypto::FillBuffer(reinterpret_cast<uint8_t *>(&icmp6Header), sizeof(icmp6Header));
icmp6Header.SetChecksum(0);
message->Write(0, sizeof(icmp6Header), &icmp6Header);
if (size > sizeof(icmp6Header))
{
uint8_t buffer[kMaxSize];
uint16_t payloadSize = size - sizeof(icmp6Header);
Random::NonCrypto::FillBuffer(buffer, payloadSize);
message->Write(sizeof(icmp6Header), payloadSize, &buffer[0]);
}
SuccessOrQuit(messageInfo.GetSockAddr().FromString(kSourceAddress), "FromString() failed");
SuccessOrQuit(messageInfo.GetPeerAddr().FromString(kDestAddress), "FromString() failed");
// Verify that the `Checksum::UpdateMessageChecksum` correctly
// updates the checksum field in the ICMP6 header on the message.
Checksum::UpdateMessageChecksum(*message, messageInfo.GetSockAddr(), messageInfo.GetPeerAddr(),
Ip6::kProtoIcmp6);
VerifyOrQuit(message->Read(message->GetOffset(), sizeof(icmp6Header), &icmp6Header) == sizeof(icmp6Header),
"Message::Read() failed");
VerifyOrQuit(icmp6Header.GetChecksum() != 0, "Failed to update checksum");
// Verify that the calculated ICMP6 checksum is valid.
VerifyOrQuit(CalculateChecksum(messageInfo.GetSockAddr(), messageInfo.GetPeerAddr(), Ip6::kProtoIcmp6,
*message) == 0xffff,
"UDP checksum does not match expected value");
// Verify that `Checksum::VerifyMessageChecksum()` accepts the
// message and its calculated checksum.
SuccessOrQuit(Checksum::VerifyMessageChecksum(*message, messageInfo, Ip6::kProtoIcmp6),
"Checksum::VerifyMessageChecksum() failed");
// Corrupt the message and verify that checksum is no longer accepted.
CorruptMessage(*message);
VerifyOrQuit(Checksum::VerifyMessageChecksum(*message, messageInfo, Ip6::kProtoIcmp6) != OT_ERROR_NONE,
"Checksum passed on corrupted message");
message->Free();
}
}
class ChecksumTester
{
public:
static void TestExampleVector(void)
{
// Example from RFC 1071
const uint8_t kTestVector[] = {0x00, 0x01, 0xf2, 0x03, 0xf4, 0xf5, 0xf6, 0xf7};
const uint16_t kTestVectorChecksum = 0xddf2;
Checksum checksum;
VerifyOrQuit(checksum.GetValue() == 0, "Incorrect initial checksum value");
checksum.AddData(kTestVector, sizeof(kTestVector));
VerifyOrQuit(checksum.GetValue() == kTestVectorChecksum, "Checksum::AddData() failed");
VerifyOrQuit(checksum.GetValue() == CalculateChecksum(kTestVector, sizeof(kTestVector)),
"Checksum::AddData() failed");
}
};
} // namespace ot
int main(void)
{
ot::ChecksumTester::TestExampleVector();
ot::TestUdpMessageChecksum();
ot::TestIcmp6MessageChecksum();
printf("All tests passed\n");
return 0;
}