| /* |
| * |
| * Copyright (c) 2020 Project CHIP Authors |
| * Copyright (c) 2018-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 implements a process to effect a functional test for |
| * the InetLayer Internet Protocol stack abstraction interfaces |
| * for handling IP (v4 or v6) multicast on UDP endpoints. |
| * |
| */ |
| |
| #ifndef __STDC_LIMIT_MACROS |
| #define __STDC_LIMIT_MACROS |
| #endif |
| |
| #include <signal.h> |
| #include <stdint.h> |
| #include <string.h> |
| #include <unistd.h> |
| |
| #include <nlbyteorder.hpp> |
| |
| #include <CHIPVersion.h> |
| |
| #include <inet/IPAddress.h> |
| #include <inet/InetArgParser.h> |
| #include <lib/support/CHIPArgParser.hpp> |
| |
| #include "TestInetCommon.h" |
| #include "TestInetCommonOptions.h" |
| #include "TestInetLayerCommon.hpp" |
| #include "TestSetupFaultInjection.h" |
| #include "TestSetupSignalling.h" |
| |
| using namespace chip; |
| using namespace chip::ArgParser; |
| using namespace chip::Inet; |
| using namespace chip::System; |
| |
| /* Preprocessor Macros */ |
| |
| #define kToolName "TestInetLayerMulticast" |
| |
| #define kToolOptNoLoopback 'L' |
| #define kToolOptGroup 'g' |
| |
| #define kToolOptExpectedGroupRxPackets (kToolOptBase + 0) |
| #define kToolOptExpectedGroupTxPackets (kToolOptBase + 1) |
| |
| /* Type Definitions */ |
| |
| enum OptFlags |
| { |
| kOptFlagNoLoopback = 0x00010000 |
| }; |
| |
| struct GroupAddress |
| { |
| uint32_t mGroup; |
| TransferStats mStats; |
| IPAddress mMulticastAddress; |
| }; |
| |
| template <size_t tCapacity> |
| struct GroupAddresses |
| { |
| size_t mSize; |
| const size_t mCapacity = tCapacity; |
| GroupAddress mAddresses[tCapacity]; |
| }; |
| |
| template <size_t tCapacity> |
| struct TestState |
| { |
| GroupAddresses<tCapacity> & mGroupAddresses; |
| TestStatus mStatus; |
| }; |
| |
| /* Function Declarations */ |
| |
| static void HandleSignal(int aSignal); |
| static bool HandleOption(const char * aProgram, OptionSet * aOptions, int aIdentifier, const char * aName, const char * aValue); |
| static bool HandleNonOptionArgs(const char * aProgram, int argc, char * argv[]); |
| static bool ParseGroupOpt(const char * aProgram, const char * aValue, bool aIPv6, uint32_t & aOutLastGroupIndex); |
| static bool ParseAndUpdateExpectedGroupPackets(const char * aProgram, const char * aValue, uint32_t aGroup, |
| const char * aDescription, uint32_t & aOutExpected); |
| |
| static void StartTest(); |
| static void CleanupTest(); |
| |
| /* Global Variables */ |
| |
| // clang-format off |
| static const uint32_t kOptFlagsDefault = (kOptFlagUseIPv6 | kOptFlagUseUDPIP); |
| |
| static UDPEndPoint * sUDPIPEndPoint = nullptr; |
| |
| static GroupAddresses<4> sGroupAddresses; |
| |
| static TestState<4> sTestState = |
| { |
| sGroupAddresses, |
| { false, false } |
| }; |
| |
| static uint32_t sLastGroupIndex = 0; |
| |
| static OptionDef sToolOptionDefs[] = |
| { |
| { "interface", kArgumentRequired, kToolOptInterface }, |
| { "group", kArgumentRequired, kToolOptGroup }, |
| { "group-expected-rx-packets", kArgumentRequired, kToolOptExpectedGroupRxPackets }, |
| { "group-expected-tx-packets", kArgumentRequired, kToolOptExpectedGroupTxPackets }, |
| { "interval", kArgumentRequired, kToolOptInterval }, |
| #if INET_CONFIG_ENABLE_IPV4 |
| { "ipv4", kNoArgument, kToolOptIPv4Only }, |
| #endif // INET_CONFIG_ENABLE_IPV4 |
| { "ipv6", kNoArgument, kToolOptIPv6Only }, |
| { "listen", kNoArgument, kToolOptListen }, |
| { "no-loopback", kNoArgument, kToolOptNoLoopback }, |
| { "send-size", kArgumentRequired, kToolOptSendSize }, |
| { "udp", kNoArgument, kToolOptUDPIP }, |
| { } |
| }; |
| |
| static const char * sToolOptionHelp = |
| " -I, --interface <interface>\n" |
| " The network interface to bind to and from which to send and receive all packets.\n" |
| "\n" |
| " -L, --no-loopback\n" |
| " Suppress the loopback of multicast packets.\n" |
| "\n" |
| " -g, --group <group>\n" |
| " Multicast group number to join.\n" |
| "\n" |
| " --group-expected-rx-packets <packets>\n" |
| " Expect to receive this number of packets for the previously-specified multicast group.\n" |
| "\n" |
| " --group-expected-tx-packets <packets>\n" |
| " Expect to send this number of packets for the previously-specified multicast group.\n" |
| "\n" |
| " -i, --interval <interval>\n" |
| " Wait interval milliseconds between sending each packet (default: 1000 ms).\n" |
| "\n" |
| " -l, --listen\n" |
| " Act as a server (i.e., listen) for packets rather than send them.\n" |
| "\n" |
| #if INET_CONFIG_ENABLE_IPV4 |
| " -4, --ipv4\n" |
| " Use IPv4 only.\n" |
| "\n" |
| #endif // INET_CONFIG_ENABLE_IPV4 |
| " -6, --ipv6\n" |
| " Use IPv6 only (default).\n" |
| "\n" |
| " -s, --send-size <size>\n" |
| " Send size bytes of user data (default: 59 bytes)\n" |
| "\n" |
| " -u, --udp\n" |
| " Use UDP over IP.\n" |
| "\n"; |
| |
| static OptionSet sToolOptions = |
| { |
| HandleOption, |
| sToolOptionDefs, |
| "GENERAL OPTIONS", |
| sToolOptionHelp |
| }; |
| |
| static HelpOptions sHelpOptions( |
| kToolName, |
| "Usage: " kToolName " [ <options> ] [ -g <group> [ ... ] -I <interface> ]\n", |
| CHIP_VERSION_STRING "\n" CHIP_TOOL_COPYRIGHT |
| ); |
| |
| static OptionSet * sToolOptionSets[] = |
| { |
| &sToolOptions, |
| &gNetworkOptions, |
| &gFaultInjectionOptions, |
| &sHelpOptions, |
| nullptr |
| }; |
| // clang-format on |
| |
| static void CheckSucceededOrFailed(const GroupAddress & aAddress, bool & aOutSucceeded, bool & aOutFailed) |
| { |
| const TransferStats & lStats = aAddress.mStats; |
| |
| #if DEBUG |
| printf("Group %u: %u/%u sent, %u/%u received\n", aAddress.mGroup, lStats.mTransmit.mActual, lStats.mTransmit.mExpected, |
| lStats.mReceive.mActual, lStats.mReceive.mExpected); |
| #endif |
| |
| if (((lStats.mTransmit.mExpected > 0) && (lStats.mTransmit.mActual > lStats.mTransmit.mExpected)) || |
| ((lStats.mReceive.mExpected > 0) && (lStats.mReceive.mActual > lStats.mReceive.mExpected))) |
| { |
| aOutFailed = true; |
| } |
| else if (((lStats.mTransmit.mExpected > 0) && (lStats.mTransmit.mActual < lStats.mTransmit.mExpected)) || |
| ((lStats.mReceive.mExpected > 0) && (lStats.mReceive.mActual < lStats.mReceive.mExpected))) |
| { |
| aOutSucceeded = false; |
| } |
| } |
| |
| template <size_t tCapacity> |
| static void CheckSucceededOrFailed(TestState<tCapacity> & aTestState, bool & aOutSucceeded, bool & aOutFailed) |
| { |
| for (size_t i = 0; i < aTestState.mGroupAddresses.mSize; i++) |
| { |
| const GroupAddress & lGroup = aTestState.mGroupAddresses.mAddresses[i]; |
| |
| CheckSucceededOrFailed(lGroup, aOutSucceeded, aOutFailed); |
| } |
| |
| if (aOutSucceeded || aOutFailed) |
| { |
| if (aOutSucceeded) |
| aTestState.mStatus.mSucceeded = true; |
| |
| if (aOutFailed) |
| SetStatusFailed(aTestState.mStatus); |
| } |
| } |
| |
| static void HandleSignal(int aSignal) |
| { |
| switch (aSignal) |
| { |
| |
| case SIGUSR1: |
| SetStatusFailed(sTestState.mStatus); |
| break; |
| } |
| } |
| |
| namespace TestInetLayerMulticast { |
| int main(int argc, char * argv[]) |
| { |
| bool lSuccessful = true; |
| CHIP_ERROR lStatus; |
| |
| InitTestInetCommon(); |
| |
| SetupFaultInjectionContext(argc, argv); |
| |
| SetSignalHandler(HandleSignal); |
| |
| if (argc == 1) |
| { |
| sHelpOptions.PrintBriefUsage(stderr); |
| lSuccessful = false; |
| goto exit; |
| } |
| |
| if (!ParseArgsFromEnvVar(kToolName, TOOL_OPTIONS_ENV_VAR_NAME, sToolOptionSets, nullptr, true) || |
| !ParseArgs(kToolName, argc, argv, sToolOptionSets, HandleNonOptionArgs)) |
| { |
| lSuccessful = false; |
| goto exit; |
| } |
| |
| InitSystemLayer(); |
| |
| InitNetwork(); |
| |
| // At this point, we should have valid network interfaces, |
| // including LwIP TUN/TAP shim interfaces. Validate the |
| // -I/--interface argument, if present. |
| |
| if (gInterfaceName != nullptr) |
| { |
| lStatus = InterfaceId::InterfaceNameToId(gInterfaceName, gInterfaceId); |
| if (lStatus != CHIP_NO_ERROR) |
| { |
| PrintArgError("%s: unknown network interface %s\n", kToolName, gInterfaceName); |
| lSuccessful = false; |
| goto shutdown; |
| } |
| } |
| |
| // If any multicast groups have been specified, ensure that a |
| // network interface identifier has been specified and is valid. |
| |
| if ((sGroupAddresses.mSize > 0) && !gInterfaceId.IsPresent()) |
| { |
| PrintArgError("%s: a network interface is required when specifying one or more multicast groups\n", kToolName); |
| lSuccessful = false; |
| goto shutdown; |
| } |
| |
| StartTest(); |
| |
| while (Common::IsTesting(sTestState.mStatus)) |
| { |
| bool lSucceeded = true; |
| bool lFailed = false; |
| |
| constexpr uint32_t kSleepTimeMilliseconds = 10; |
| ServiceNetwork(kSleepTimeMilliseconds); |
| |
| CheckSucceededOrFailed(sTestState, lSucceeded, lFailed); |
| |
| #if DEBUG |
| printf("%s %s number of expected packets\n", ((lSucceeded) ? "successfully" : ((lFailed) ? "failed to" : "has not yet")), |
| ((lSucceeded) |
| ? (Common::IsReceiver() ? "received" : "sent") |
| : ((lFailed) ? (Common::IsReceiver() ? "receive" : "send") : Common::IsReceiver() ? "received" : "sent"))); |
| #endif |
| } |
| |
| CleanupTest(); |
| |
| shutdown: |
| ShutdownNetwork(); |
| ShutdownSystemLayer(); |
| |
| lSuccessful = Common::WasSuccessful(sTestState.mStatus); |
| |
| exit: |
| return (lSuccessful ? EXIT_SUCCESS : EXIT_FAILURE); |
| } |
| } // namespace TestInetLayerMulticast |
| |
| static bool HandleOption(const char * aProgram, OptionSet * aOptions, int aIdentifier, const char * aName, const char * aValue) |
| { |
| bool retval = true; |
| |
| switch (aIdentifier) |
| { |
| |
| case kToolOptInterval: |
| if (!ParseInt(aValue, gSendIntervalMs)) |
| { |
| PrintArgError("%s: invalid value specified for send interval: %s\n", aProgram, aValue); |
| retval = false; |
| } |
| break; |
| |
| case kToolOptListen: |
| gOptFlags |= kOptFlagListen; |
| break; |
| |
| case kToolOptNoLoopback: |
| gOptFlags |= kOptFlagNoLoopback; |
| break; |
| |
| case kToolOptGroup: |
| if (!ParseGroupOpt(aProgram, aValue, gOptFlags & kOptFlagUseIPv6, sLastGroupIndex)) |
| { |
| retval = false; |
| } |
| break; |
| |
| case kToolOptExpectedGroupRxPackets: { |
| GroupAddress & lGroupAddress = sGroupAddresses.mAddresses[sLastGroupIndex]; |
| |
| if (!ParseAndUpdateExpectedGroupPackets(aProgram, aValue, lGroupAddress.mGroup, "received", |
| lGroupAddress.mStats.mReceive.mExpected)) |
| { |
| retval = false; |
| } |
| } |
| break; |
| |
| case kToolOptExpectedGroupTxPackets: { |
| GroupAddress & lGroupAddress = sGroupAddresses.mAddresses[sLastGroupIndex]; |
| |
| if (!ParseAndUpdateExpectedGroupPackets(aProgram, aValue, lGroupAddress.mGroup, "sent", |
| lGroupAddress.mStats.mTransmit.mExpected)) |
| { |
| retval = false; |
| } |
| } |
| break; |
| |
| #if INET_CONFIG_ENABLE_IPV4 |
| case kToolOptIPv4Only: |
| if (gOptFlags & kOptFlagUseIPv6) |
| { |
| PrintArgError("%s: the use of --ipv4 is exclusive with --ipv6. Please select only one of the two options.\n", aProgram); |
| retval = false; |
| } |
| gOptFlags |= kOptFlagUseIPv4; |
| break; |
| #endif // INET_CONFIG_ENABLE_IPV4 |
| |
| case kToolOptIPv6Only: |
| if (gOptFlags & kOptFlagUseIPv4) |
| { |
| PrintArgError("%s: the use of --ipv6 is exclusive with --ipv4. Please select only one of the two options.\n", aProgram); |
| retval = false; |
| } |
| gOptFlags |= kOptFlagUseIPv6; |
| break; |
| |
| case kToolOptInterface: |
| |
| // NOTE: When using LwIP on a hosted OS, the interface will |
| // not actually be available until AFTER InitNetwork, |
| // consequently, we cannot do any meaningful validation |
| // here. Simply save the value off and we will validate it |
| // later. |
| |
| gInterfaceName = aValue; |
| break; |
| |
| case kToolOptSendSize: |
| if (!ParseInt(aValue, gSendSize)) |
| { |
| PrintArgError("%s: invalid value specified for send size: %s\n", aProgram, aValue); |
| return false; |
| } |
| break; |
| |
| case kToolOptUDPIP: |
| gOptFlags |= kOptFlagUseUDPIP; |
| break; |
| |
| default: |
| PrintArgError("%s: INTERNAL ERROR: Unhandled option: %s\n", aProgram, aName); |
| retval = false; |
| break; |
| } |
| |
| return (retval); |
| } |
| |
| bool HandleNonOptionArgs(const char * aProgram, int argc, char * argv[]) |
| { |
| if ((gOptFlags & (kOptFlagListen | kOptFlagNoLoopback)) == (kOptFlagListen | kOptFlagNoLoopback)) |
| { |
| PrintArgError("%s: the listen option is exclusive with the loopback suppression option. Please select one or the other.\n", |
| aProgram); |
| return false; |
| } |
| |
| // If there were any additional, non-parsed arguments, it's an error. |
| |
| if (argc > 0) |
| { |
| PrintArgError("%s: unexpected argument: %s\n", aProgram, argv[0]); |
| return false; |
| } |
| |
| // If no IP version or transport flags were specified, use the defaults. |
| |
| if (!(gOptFlags & (kOptFlagUseIPv4 | kOptFlagUseIPv6 | kOptFlagUseUDPIP))) |
| { |
| gOptFlags |= kOptFlagsDefault; |
| } |
| |
| return true; |
| } |
| |
| // Create an IPv4 administratively-scoped multicast address |
| |
| static IPAddress MakeIPv4Multicast(uint32_t aGroupIdentifier) |
| { |
| IPAddress lAddress; |
| |
| lAddress.Addr[0] = 0; |
| lAddress.Addr[1] = 0; |
| lAddress.Addr[2] = nlByteOrderSwap32HostToBig(0xFFFF); |
| lAddress.Addr[3] = nlByteOrderSwap32HostToBig((239 << 24) | (aGroupIdentifier & 0xFFFFFF)); |
| |
| return (lAddress); |
| } |
| |
| // Create an IPv6 site-scoped multicast address |
| |
| static IPAddress MakeIPv6Multicast(uint32_t aGroupIdentifier) |
| { |
| return (IPAddress::MakeIPv6Multicast(IPv6MulticastFlag::kTransient, kIPv6MulticastScope_Site, aGroupIdentifier)); |
| } |
| |
| static void SetGroup(GroupAddress & aGroupAddress, uint32_t aGroupIdentifier, uint32_t aExpectedRx, uint32_t aExpectedTx) |
| { |
| aGroupAddress.mGroup = aGroupIdentifier; |
| aGroupAddress.mStats.mReceive.mExpected = aExpectedRx; |
| aGroupAddress.mStats.mReceive.mActual = 0; |
| aGroupAddress.mStats.mTransmit.mExpected = aExpectedTx; |
| aGroupAddress.mStats.mTransmit.mActual = 0; |
| } |
| |
| static bool ParseGroupOpt(const char * aProgram, const char * aValue, bool aIPv6, uint32_t & aOutLastGroupIndex) |
| { |
| uint32_t lGroupIdentifier; |
| |
| if (sGroupAddresses.mSize == sGroupAddresses.mCapacity) |
| { |
| PrintArgError("%s: the maximum number of allowed groups (%zu) have been specified\n", aProgram, sGroupAddresses.mCapacity); |
| return false; |
| } |
| |
| if (!ParseInt(aValue, lGroupIdentifier)) |
| { |
| PrintArgError("%s: unrecognized group %s\n", aProgram, aValue); |
| return false; |
| } |
| |
| aOutLastGroupIndex = sGroupAddresses.mSize++; |
| |
| SetGroup(sGroupAddresses.mAddresses[aOutLastGroupIndex], lGroupIdentifier, lGroupIdentifier, lGroupIdentifier); |
| |
| return true; |
| } |
| |
| static bool ParseAndUpdateExpectedGroupPackets(const char * aProgram, const char * aValue, uint32_t aGroup, |
| const char * aDescription, uint32_t & aOutExpected) |
| { |
| uint32_t lExpectedGroupPackets; |
| |
| if (!ParseInt(aValue, lExpectedGroupPackets)) |
| { |
| PrintArgError("%s: invalid value specified for expected group %u %s packets: %s\n", aProgram, aGroup, aDescription, aValue); |
| return false; |
| } |
| |
| aOutExpected = lExpectedGroupPackets; |
| |
| return true; |
| } |
| |
| static GroupAddress * FindGroupAddress(const IPAddress & aSourceAddress) |
| { |
| GroupAddress * lResult = nullptr; |
| |
| for (size_t i = 0; i < sGroupAddresses.mSize; i++) |
| { |
| GroupAddress & lGroupAddress = sGroupAddresses.mAddresses[i]; |
| |
| if (lGroupAddress.mMulticastAddress == aSourceAddress) |
| { |
| lResult = &lGroupAddress; |
| break; |
| } |
| } |
| |
| return (lResult); |
| } |
| |
| static void PrintReceivedStats(const GroupAddress & aGroupAddress) |
| { |
| printf("%u/%u received for multicast group %u\n", aGroupAddress.mStats.mReceive.mActual, |
| aGroupAddress.mStats.mReceive.mExpected, aGroupAddress.mGroup); |
| } |
| |
| static bool HandleDataReceived(const PacketBufferHandle & aBuffer, GroupAddress & aGroupAddress, bool aCheckBuffer) |
| { |
| constexpr bool lStatsByPacket = true; |
| if (!Common::HandleDataReceived(aBuffer, aGroupAddress.mStats, lStatsByPacket, aCheckBuffer)) |
| { |
| return false; |
| } |
| |
| PrintReceivedStats(aGroupAddress); |
| |
| return true; |
| } |
| |
| static bool HandleDataReceived(const PacketBufferHandle & aBuffer, const IPPacketInfo & aPacketInfo, bool aCheckBuffer) |
| { |
| GroupAddress * const lGroupAddress = FindGroupAddress(aPacketInfo.DestAddress); |
| if (lGroupAddress != nullptr) |
| { |
| return HandleDataReceived(aBuffer, *lGroupAddress, aCheckBuffer); |
| } |
| return true; |
| } |
| |
| // UDP Endpoint Callbacks |
| |
| static void HandleUDPMessageReceived(IPEndPointBasis * aEndPoint, PacketBufferHandle && aBuffer, const IPPacketInfo * aPacketInfo) |
| { |
| const bool lCheckBuffer = true; |
| bool lStatus; |
| |
| VerifyOrExit(aEndPoint != nullptr, lStatus = false); |
| VerifyOrExit(!aBuffer.IsNull(), lStatus = false); |
| VerifyOrExit(aPacketInfo != nullptr, lStatus = false); |
| |
| Common::HandleUDPMessageReceived(aEndPoint, aBuffer, aPacketInfo); |
| |
| lStatus = HandleDataReceived(std::move(aBuffer), *aPacketInfo, lCheckBuffer); |
| |
| exit: |
| if (!lStatus) |
| { |
| SetStatusFailed(sTestState.mStatus); |
| } |
| } |
| |
| static void HandleUDPReceiveError(IPEndPointBasis * aEndPoint, CHIP_ERROR aError, const IPPacketInfo * aPacketInfo) |
| { |
| Common::HandleUDPReceiveError(aEndPoint, aError, aPacketInfo); |
| |
| SetStatusFailed(sTestState.mStatus); |
| } |
| |
| static bool IsTransportReadyForSend() |
| { |
| bool lStatus = false; |
| |
| if ((gOptFlags & kOptFlagUseUDPIP) == kOptFlagUseUDPIP) |
| { |
| lStatus = (sUDPIPEndPoint != nullptr); |
| } |
| |
| return (lStatus); |
| } |
| |
| static CHIP_ERROR PrepareTransportForSend() |
| { |
| CHIP_ERROR lStatus = CHIP_NO_ERROR; |
| |
| return (lStatus); |
| } |
| |
| static CHIP_ERROR DriveSendForDestination(const IPAddress & aAddress, uint16_t aSize) |
| { |
| PacketBufferHandle lBuffer; |
| |
| if ((gOptFlags & kOptFlagUseUDPIP) == kOptFlagUseUDPIP) |
| { |
| const uint8_t lFirstValue = 0; |
| |
| // For UDP, we'll send n aSize or smaller datagrams, each |
| // patterned from zero to aSize - 1. |
| |
| lBuffer = Common::MakeDataBuffer(aSize, lFirstValue); |
| VerifyOrReturnError(!lBuffer.IsNull(), CHIP_ERROR_NO_MEMORY); |
| |
| return sUDPIPEndPoint->SendTo(aAddress, kUDPPort, std::move(lBuffer)); |
| } |
| |
| return CHIP_NO_ERROR; |
| } |
| |
| static CHIP_ERROR DriveSendForGroup(GroupAddress & aGroupAddress) |
| { |
| if (aGroupAddress.mStats.mTransmit.mActual < aGroupAddress.mStats.mTransmit.mExpected) |
| { |
| ReturnErrorOnFailure(DriveSendForDestination(aGroupAddress.mMulticastAddress, gSendSize)); |
| |
| aGroupAddress.mStats.mTransmit.mActual++; |
| |
| printf("%u/%u transmitted for multicast group %u\n", aGroupAddress.mStats.mTransmit.mActual, |
| aGroupAddress.mStats.mTransmit.mExpected, aGroupAddress.mGroup); |
| } |
| |
| return CHIP_NO_ERROR; |
| } |
| |
| template <size_t tCapacity> |
| static CHIP_ERROR DriveSendForGroups(GroupAddresses<tCapacity> & aGroupAddresses) |
| { |
| // Iterate over each multicast group for which this node is a |
| // member and send a packet. |
| for (size_t i = 0; i < aGroupAddresses.mSize; i++) |
| { |
| ReturnErrorOnFailure(DriveSendForGroup(aGroupAddresses.mAddresses[i])); |
| } |
| |
| return CHIP_NO_ERROR; |
| } |
| |
| void DriveSend() |
| { |
| CHIP_ERROR lStatus = CHIP_NO_ERROR; |
| |
| if (!Common::IsSender()) |
| goto exit; |
| |
| if (!gSendIntervalExpired) |
| goto exit; |
| |
| if (!IsTransportReadyForSend()) |
| { |
| lStatus = PrepareTransportForSend(); |
| SuccessOrExit(lStatus); |
| } |
| else |
| { |
| gSendIntervalExpired = false; |
| gSystemLayer.StartTimer(System::Clock::Milliseconds32(gSendIntervalMs), Common::HandleSendTimerComplete, nullptr); |
| |
| lStatus = DriveSendForGroups(sGroupAddresses); |
| SuccessOrExit(lStatus); |
| } |
| |
| exit: |
| if (lStatus != CHIP_NO_ERROR) |
| { |
| SetStatusFailed(sTestState.mStatus); |
| } |
| } |
| |
| static void StartTest() |
| { |
| IPAddressType lIPAddressType = IPAddressType::kIPv6; |
| IPVersion lIPVersion = kIPVersion_6; |
| IPAddress lAddress = IPAddress::Any; |
| IPEndPointBasis * lEndPoint = nullptr; |
| const bool lUseLoopback = ((gOptFlags & kOptFlagNoLoopback) == 0); |
| CHIP_ERROR lStatus; |
| |
| #if INET_CONFIG_ENABLE_IPV4 |
| if (gOptFlags & kOptFlagUseIPv4) |
| { |
| lIPAddressType = IPAddressType::kIPv4; |
| lIPVersion = kIPVersion_4; |
| } |
| #endif // INET_CONFIG_ENABLE_IPV4 |
| |
| // clang-format off |
| printf("Using %sIP%s, device interface: %s (w/%c LwIP)\n", |
| "UDP/", |
| ((gOptFlags & kOptFlagUseIPv4) ? "v4" : "v6"), |
| ((gInterfaceName) ? gInterfaceName : "<none>"), |
| (CHIP_SYSTEM_CONFIG_USE_LWIP ? '\0' : 'o')); |
| // clang-format ob |
| |
| // Allocate the endpoints for sending or receiving. |
| |
| if (gOptFlags & kOptFlagUseUDPIP) |
| { |
| lStatus = gInet.NewUDPEndPoint(&sUDPIPEndPoint); |
| INET_FAIL_ERROR(lStatus, "InetLayer::NewUDPEndPoint failed"); |
| |
| lStatus = sUDPIPEndPoint->Bind(lIPAddressType, lAddress, kUDPPort); |
| INET_FAIL_ERROR(lStatus, "UDPEndPoint::Bind failed"); |
| |
| if (gInterfaceId.IsPresent()) |
| { |
| lStatus = sUDPIPEndPoint->BindInterface(lIPAddressType, gInterfaceId); |
| INET_FAIL_ERROR(lStatus, "UDPEndPoint::BindInterface failed"); |
| } |
| |
| lStatus = sUDPIPEndPoint->Listen(HandleUDPMessageReceived, HandleUDPReceiveError); |
| INET_FAIL_ERROR(lStatus, "UDPEndPoint::Listen failed"); |
| |
| lEndPoint = sUDPIPEndPoint; |
| } |
| |
| // If loopback suppression has been requested, attempt to disable |
| // it; otherwise, attempt to enable it. |
| |
| lStatus = lEndPoint->SetMulticastLoopback(lIPVersion, lUseLoopback); |
| INET_FAIL_ERROR(lStatus, "SetMulticastLoopback failed"); |
| |
| // Configure and join the multicast groups |
| |
| for (size_t i = 0; i < sGroupAddresses.mSize; i++) |
| { |
| char lAddressBuffer[INET6_ADDRSTRLEN]; |
| GroupAddress & lGroupAddress = sGroupAddresses.mAddresses[i]; |
| IPAddress & lMulticastAddress = lGroupAddress.mMulticastAddress; |
| |
| if ((lEndPoint != nullptr) && gInterfaceId.IsPresent()) |
| { |
| if (gOptFlags & kOptFlagUseIPv4) |
| { |
| lMulticastAddress = MakeIPv4Multicast(lGroupAddress.mGroup); |
| } |
| else |
| { |
| lMulticastAddress = MakeIPv6Multicast(lGroupAddress.mGroup); |
| } |
| |
| lMulticastAddress.ToString(lAddressBuffer, sizeof(lAddressBuffer)); |
| |
| printf("Will join multicast group %s\n", lAddressBuffer); |
| |
| lStatus = lEndPoint->JoinMulticastGroup(gInterfaceId, lMulticastAddress); |
| INET_FAIL_ERROR(lStatus, "Could not join multicast group"); |
| } |
| } |
| |
| if (Common::IsReceiver()) |
| printf("Listening...\n"); |
| else |
| DriveSend(); |
| } |
| |
| static void CleanupTest() |
| { |
| IPEndPointBasis * lEndPoint = nullptr; |
| CHIP_ERROR lStatus; |
| |
| gSendIntervalExpired = false; |
| gSystemLayer.CancelTimer(Common::HandleSendTimerComplete, nullptr); |
| |
| // Leave the multicast groups |
| |
| if (gOptFlags & kOptFlagUseUDPIP) |
| { |
| lEndPoint = sUDPIPEndPoint; |
| } |
| |
| for (size_t i = 0; i < sGroupAddresses.mSize; i++) |
| { |
| char lAddressBuffer[INET6_ADDRSTRLEN]; |
| GroupAddress & lGroupAddress = sGroupAddresses.mAddresses[i]; |
| IPAddress & lMulticastAddress = lGroupAddress.mMulticastAddress; |
| |
| if ((lEndPoint != nullptr) && gInterfaceId.IsPresent()) |
| { |
| lMulticastAddress.ToString(lAddressBuffer, sizeof(lAddressBuffer)); |
| |
| printf("Will leave multicast group %s\n", lAddressBuffer); |
| |
| lStatus = lEndPoint->LeaveMulticastGroup(gInterfaceId, lMulticastAddress); |
| INET_FAIL_ERROR(lStatus, "Could not leave multicast group"); |
| } |
| } |
| |
| // Release the resources associated with the allocated end points. |
| |
| if (sUDPIPEndPoint != nullptr) |
| { |
| sUDPIPEndPoint->Free(); |
| } |
| } |