| /* |
| * |
| * Copyright (c) 2020-2021 Project CHIP Authors |
| * Copyright (c) 2019 Google LLC |
| * All rights reserved. |
| * |
| * Licensed under the Apache License, Version 2.0 (the "License"); |
| * you may not use this file except in compliance with the License. |
| * You may obtain a copy of the License at |
| * |
| * http://www.apache.org/licenses/LICENSE-2.0 |
| * |
| * Unless required by applicable law or agreed to in writing, software |
| * distributed under the License is distributed on an "AS IS" BASIS, |
| * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| * See the License for the specific language governing permissions and |
| * limitations under the License. |
| */ |
| |
| /** |
| * @file |
| * This file defines common preprocessor defintions, constants, |
| * functions, and globals for unit and functional tests for the |
| * Inet layer. |
| * |
| */ |
| |
| #include "TestInetLayerCommon.hpp" |
| |
| #include <stdlib.h> |
| #include <string.h> |
| #include <utility> |
| |
| #include <nlbyteorder.hpp> |
| |
| #include <inet/IPPacketInfo.h> |
| #include <lib/support/CodeUtils.h> |
| #include <lib/support/SafeInt.h> |
| |
| #include "TestInetCommon.h" |
| |
| using namespace ::chip; |
| using namespace ::chip::Inet; |
| using namespace ::chip::System; |
| |
| // Type Definitions |
| |
| struct ICMPEchoHeader |
| { |
| uint8_t mType; |
| uint8_t mCode; |
| uint16_t mChecksum; |
| uint16_t mID; |
| uint16_t mSequenceNumber; |
| } __attribute__((packed)); |
| |
| typedef struct ICMPEchoHeader ICMPv4EchoHeader; |
| typedef struct ICMPEchoHeader ICMPv6EchoHeader; |
| |
| // Global Variables |
| |
| static const uint8_t kICMPv4_EchoRequest = 8; |
| static const uint8_t kICMPv4_EchoReply = 0; |
| |
| static const uint8_t kICMPv6_EchoRequest = 128; |
| static const uint8_t kICMPv6_EchoReply = 129; |
| |
| // clang-format off |
| const uint8_t gICMPv4Types[kICMPv4_FilterTypes] = |
| { |
| kICMPv4_EchoRequest, |
| kICMPv4_EchoReply |
| }; |
| |
| const uint8_t gICMPv6Types[kICMPv6_FilterTypes] = |
| { |
| kICMPv6_EchoRequest, |
| kICMPv6_EchoReply |
| }; |
| // clang-format on |
| |
| bool gSendIntervalExpired = true; |
| |
| uint32_t gSendIntervalMs = 1000; |
| |
| const char * gInterfaceName = nullptr; |
| |
| InterfaceId gInterfaceId = InterfaceId::Null(); |
| |
| uint16_t gSendSize = 59; |
| |
| uint32_t gOptFlags = 0; |
| |
| namespace Common { |
| |
| bool IsReceiver() |
| { |
| return ((gOptFlags & kOptFlagListen) == kOptFlagListen); |
| } |
| |
| bool IsSender() |
| { |
| return (!IsReceiver()); |
| } |
| |
| bool IsTesting(const TestStatus & aTestStatus) |
| { |
| bool lStatus; |
| |
| lStatus = (!aTestStatus.mFailed && !aTestStatus.mSucceeded); |
| |
| return (lStatus); |
| } |
| |
| bool WasSuccessful(const TestStatus & aTestStatus) |
| { |
| bool lStatus = false; |
| |
| if (aTestStatus.mFailed) |
| lStatus = false; |
| else if (aTestStatus.mSucceeded) |
| lStatus = true; |
| |
| return (lStatus); |
| } |
| |
| static void FillDataBufferPattern(uint8_t * aBuffer, size_t aLength, size_t aPatternStartOffset, uint8_t aFirstValue) |
| { |
| for (size_t i = aPatternStartOffset; i < aLength; i++) |
| { |
| const uint8_t lValue = static_cast<uint8_t>(aFirstValue & 0xFF); |
| |
| aBuffer[i] = lValue; |
| |
| aFirstValue++; |
| } |
| } |
| |
| static bool CheckDataBufferPattern(const uint8_t * aBuffer, size_t aLength, size_t aPatternStartOffset, size_t aFirstValue) |
| { |
| for (size_t i = aPatternStartOffset; i < aLength; i++) |
| { |
| const uint8_t lValue = aBuffer[i]; |
| |
| if (lValue != static_cast<uint8_t>(aFirstValue)) |
| { |
| printf("Bad data value at offset %u (0x%04x): " |
| "expected 0x%02x, found 0x%02x\n", |
| static_cast<unsigned>(i), static_cast<unsigned>(i), static_cast<unsigned>(aFirstValue), lValue); |
| DumpMemory(aBuffer + aPatternStartOffset, static_cast<uint32_t>(aLength - aPatternStartOffset), "0x", 16); |
| return false; |
| } |
| |
| aFirstValue++; |
| } |
| |
| return true; |
| } |
| |
| static PacketBufferHandle MakeDataBuffer(size_t aDesiredLength, size_t aPatternStartOffset, uint8_t aFirstValue) |
| { |
| VerifyOrReturnError(aPatternStartOffset <= aDesiredLength, PacketBufferHandle()); |
| |
| PacketBufferHandle lBuffer = PacketBufferHandle::New(aDesiredLength); |
| VerifyOrReturnError(!lBuffer.IsNull(), lBuffer); |
| |
| aDesiredLength = min(lBuffer->MaxDataLength(), aDesiredLength); |
| |
| FillDataBufferPattern(lBuffer->Start(), aDesiredLength, aPatternStartOffset, aFirstValue); |
| |
| lBuffer->SetDataLength(aDesiredLength); |
| |
| return lBuffer; |
| } |
| |
| static PacketBufferHandle MakeDataBuffer(size_t aDesiredLength, size_t aPatternStartOffset) |
| { |
| constexpr uint8_t lFirstValue = 0; |
| return MakeDataBuffer(aDesiredLength, aPatternStartOffset, lFirstValue); |
| } |
| |
| template <typename tType> |
| static PacketBufferHandle MakeICMPDataBuffer(size_t aDesiredUserLength, uint16_t aHeaderLength, size_t aPatternStartOffset, |
| uint8_t aType) |
| { |
| static uint16_t lSequenceNumber = 0; |
| PacketBufferHandle lBuffer; |
| |
| // To ensure there is enough room for the user data and the ICMP |
| // header, include both the user data size and the ICMP header length. |
| |
| lBuffer = MakeDataBuffer(aDesiredUserLength + aHeaderLength, aPatternStartOffset); |
| |
| if (!lBuffer.IsNull()) |
| { |
| tType * lHeader = reinterpret_cast<tType *>(lBuffer->Start()); |
| |
| lHeader->mType = aType; |
| lHeader->mCode = 0; |
| lHeader->mChecksum = 0; |
| lHeader->mID = static_cast<uint16_t>(rand() & UINT16_MAX); |
| lHeader->mSequenceNumber = nlByteOrderSwap16HostToBig(lSequenceNumber++); |
| } |
| |
| return (lBuffer); |
| } |
| |
| PacketBufferHandle MakeICMPv4DataBuffer(size_t aDesiredUserLength) |
| { |
| constexpr uint16_t lICMPHeaderLength = sizeof(ICMPv4EchoHeader); |
| constexpr uint16_t lPatternStartOffset = lICMPHeaderLength; |
| const uint8_t lType = gICMPv4Types[kICMP_EchoRequestIndex]; |
| |
| return MakeICMPDataBuffer<ICMPv4EchoHeader>(aDesiredUserLength, lICMPHeaderLength, lPatternStartOffset, lType); |
| } |
| |
| PacketBufferHandle MakeICMPv6DataBuffer(size_t aDesiredUserLength) |
| { |
| constexpr uint16_t lICMPHeaderLength = sizeof(ICMPv6EchoHeader); |
| constexpr uint16_t lPatternStartOffset = lICMPHeaderLength; |
| const uint8_t lType = gICMPv6Types[kICMP_EchoRequestIndex]; |
| |
| return MakeICMPDataBuffer<ICMPv6EchoHeader>(aDesiredUserLength, lICMPHeaderLength, lPatternStartOffset, lType); |
| } |
| |
| PacketBufferHandle MakeDataBuffer(size_t aDesiredLength, uint8_t aFirstValue) |
| { |
| constexpr size_t lPatternStartOffset = 0; |
| return MakeDataBuffer(aDesiredLength, lPatternStartOffset, aFirstValue); |
| } |
| |
| PacketBufferHandle MakeDataBuffer(size_t aDesiredLength) |
| { |
| constexpr size_t lPatternStartOffset = 0; |
| return MakeDataBuffer(aDesiredLength, lPatternStartOffset); |
| } |
| |
| static bool HandleDataReceived(const PacketBufferHandle & aBuffer, TransferStats & aStats, bool aStatsByPacket, bool aCheckBuffer, |
| size_t aPatternStartOffset, size_t aFirstValue) |
| { |
| size_t lTotalDataLength = 0; |
| |
| // Walk through each buffer in the packet chain, checking the |
| // buffer for the expected pattern, if requested. |
| |
| for (PacketBufferHandle lBuffer = aBuffer.Retain(); !lBuffer.IsNull(); lBuffer.Advance()) |
| { |
| const size_t lDataLength = lBuffer->DataLength(); |
| const uint8_t * const p = lBuffer->Start(); |
| |
| if (aCheckBuffer && !CheckDataBufferPattern(p, lDataLength, aPatternStartOffset, aFirstValue)) |
| { |
| return false; |
| } |
| |
| lTotalDataLength = lTotalDataLength + lDataLength; |
| aFirstValue = aFirstValue + lDataLength; |
| } |
| |
| // If we are accumulating stats by packet rather than by size, |
| // then increment by one (1) rather than the total buffer length. |
| |
| VerifyOrReturnError(CanCastTo<uint32_t>(lTotalDataLength), false); |
| aStats.mReceive.mActual += ((aStatsByPacket) ? 1 : static_cast<uint32_t>(lTotalDataLength)); |
| |
| return true; |
| } |
| |
| static bool HandleICMPDataReceived(PacketBufferHandle aBuffer, uint16_t aHeaderLength, TransferStats & aStats, bool aStatsByPacket, |
| bool aCheckBuffer) |
| { |
| const uint16_t lPatternStartOffset = 0; |
| bool lStatus; |
| |
| aBuffer->ConsumeHead(aHeaderLength); |
| |
| lStatus = HandleDataReceived(aBuffer, aStats, aStatsByPacket, aCheckBuffer, lPatternStartOffset); |
| |
| return (lStatus); |
| } |
| |
| bool HandleICMPv4DataReceived(PacketBufferHandle && aBuffer, TransferStats & aStats, bool aStatsByPacket, bool aCheckBuffer) |
| { |
| const uint16_t lICMPHeaderLength = sizeof(ICMPv4EchoHeader); |
| bool lStatus; |
| |
| lStatus = HandleICMPDataReceived(std::move(aBuffer), lICMPHeaderLength, aStats, aStatsByPacket, aCheckBuffer); |
| |
| return (lStatus); |
| } |
| |
| bool HandleICMPv6DataReceived(PacketBufferHandle && aBuffer, TransferStats & aStats, bool aStatsByPacket, bool aCheckBuffer) |
| { |
| const uint16_t lICMPHeaderLength = sizeof(ICMPv6EchoHeader); |
| bool lStatus; |
| |
| lStatus = HandleICMPDataReceived(std::move(aBuffer), lICMPHeaderLength, aStats, aStatsByPacket, aCheckBuffer); |
| |
| return (lStatus); |
| } |
| |
| bool HandleDataReceived(const PacketBufferHandle & aBuffer, TransferStats & aStats, bool aStatsByPacket, bool aCheckBuffer, |
| uint8_t aFirstValue) |
| { |
| const uint16_t lPatternStartOffset = 0; |
| bool lStatus; |
| |
| lStatus = HandleDataReceived(aBuffer, aStats, aStatsByPacket, aCheckBuffer, lPatternStartOffset, aFirstValue); |
| |
| return (lStatus); |
| } |
| |
| bool HandleDataReceived(const PacketBufferHandle & aBuffer, TransferStats & aStats, bool aStatsByPacket, bool aCheckBuffer) |
| { |
| const uint8_t lFirstValue = 0; |
| const uint16_t lPatternStartOffset = 0; |
| bool lStatus; |
| |
| lStatus = HandleDataReceived(aBuffer, aStats, aStatsByPacket, aCheckBuffer, lPatternStartOffset, lFirstValue); |
| |
| return (lStatus); |
| } |
| |
| bool HandleUDPDataReceived(const PacketBufferHandle & aBuffer, TransferStats & aStats, bool aStatsByPacket, bool aCheckBuffer) |
| { |
| bool lStatus; |
| |
| lStatus = HandleDataReceived(aBuffer, aStats, aStatsByPacket, aCheckBuffer); |
| |
| return (lStatus); |
| } |
| |
| bool HandleTCPDataReceived(const PacketBufferHandle & aBuffer, TransferStats & aStats, bool aStatsByPacket, bool aCheckBuffer) |
| { |
| bool lStatus; |
| |
| lStatus = HandleDataReceived(aBuffer, aStats, aStatsByPacket, aCheckBuffer); |
| |
| return (lStatus); |
| } |
| |
| // Timer Callback Handler |
| |
| void HandleSendTimerComplete(System::Layer * aSystemLayer, void * aAppState) |
| { |
| gSendIntervalExpired = true; |
| |
| DriveSend(); |
| } |
| |
| // Raw Endpoint Callback Handlers |
| |
| void HandleRawMessageReceived(const UDPEndPoint * aEndPoint, const PacketBufferHandle & aBuffer, const IPPacketInfo * aPacketInfo) |
| { |
| char lSourceAddressBuffer[INET6_ADDRSTRLEN]; |
| char lDestinationAddressBuffer[INET6_ADDRSTRLEN]; |
| |
| aPacketInfo->SrcAddress.ToString(lSourceAddressBuffer); |
| aPacketInfo->DestAddress.ToString(lDestinationAddressBuffer); |
| |
| printf("Raw message received from %s to %s (%u bytes)\n", lSourceAddressBuffer, lDestinationAddressBuffer, |
| static_cast<unsigned int>(aBuffer->DataLength())); |
| } |
| |
| void HandleRawReceiveError(const UDPEndPoint * aEndPoint, const CHIP_ERROR & aError, const IPPacketInfo * aPacketInfo) |
| { |
| char lAddressBuffer[INET6_ADDRSTRLEN]; |
| |
| if (aPacketInfo != nullptr) |
| { |
| aPacketInfo->SrcAddress.ToString(lAddressBuffer); |
| } |
| else |
| { |
| strcpy(lAddressBuffer, "(unknown)"); |
| } |
| |
| printf("IP receive error from %s %s\n", lAddressBuffer, ErrorStr(aError)); |
| } |
| |
| // UDP Endpoint Callback Handlers |
| |
| void HandleUDPMessageReceived(const UDPEndPoint * aEndPoint, const PacketBufferHandle & aBuffer, const IPPacketInfo * aPacketInfo) |
| { |
| char lSourceAddressBuffer[INET6_ADDRSTRLEN]; |
| char lDestinationAddressBuffer[INET6_ADDRSTRLEN]; |
| |
| aPacketInfo->SrcAddress.ToString(lSourceAddressBuffer); |
| aPacketInfo->DestAddress.ToString(lDestinationAddressBuffer); |
| |
| printf("UDP packet received from %s:%u to %s:%u (%u bytes)\n", lSourceAddressBuffer, aPacketInfo->SrcPort, |
| lDestinationAddressBuffer, aPacketInfo->DestPort, static_cast<unsigned int>(aBuffer->DataLength())); |
| } |
| |
| void HandleUDPReceiveError(const UDPEndPoint * aEndPoint, const CHIP_ERROR & aError, const IPPacketInfo * aPacketInfo) |
| { |
| char lAddressBuffer[INET6_ADDRSTRLEN]; |
| uint16_t lSourcePort; |
| |
| if (aPacketInfo != nullptr) |
| { |
| aPacketInfo->SrcAddress.ToString(lAddressBuffer); |
| lSourcePort = aPacketInfo->SrcPort; |
| } |
| else |
| { |
| strcpy(lAddressBuffer, "(unknown)"); |
| lSourcePort = 0; |
| } |
| |
| printf("UDP receive error from %s:%u: %s\n", lAddressBuffer, lSourcePort, ErrorStr(aError)); |
| } |
| |
| } // namespace Common |