Fix for x86_64 build fail
[platform/upstream/connectedhomeip.git] / src / inet / tests / TestInetLayerMulticast.cpp
1 /*
2  *
3  *    Copyright (c) 2020 Project CHIP Authors
4  *    Copyright (c) 2018-2019 Google LLC
5  *    All rights reserved.
6  *
7  *    Licensed under the Apache License, Version 2.0 (the "License");
8  *    you may not use this file except in compliance with the License.
9  *    You may obtain a copy of the License at
10  *
11  *        http://www.apache.org/licenses/LICENSE-2.0
12  *
13  *    Unless required by applicable law or agreed to in writing, software
14  *    distributed under the License is distributed on an "AS IS" BASIS,
15  *    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
16  *    See the License for the specific language governing permissions and
17  *    limitations under the License.
18  */
19
20 /**
21  *    @file
22  *      This file implements a process to effect a functional test for
23  *      the InetLayer Internet Protocol stack abstraction interfaces
24  *      for handling IP (v4 or v6) multicast on either bare IP (i.e.,
25  *      "raw") or UDP endpoints.
26  *
27  */
28
29 #ifndef __STDC_LIMIT_MACROS
30 #define __STDC_LIMIT_MACROS
31 #endif
32
33 #include <signal.h>
34 #include <stdint.h>
35 #include <string.h>
36 #include <unistd.h>
37
38 #include <nlbyteorder.hpp>
39
40 #include <CHIPVersion.h>
41
42 #include <inet/IPAddress.h>
43 #include <inet/InetArgParser.h>
44 #include <support/CHIPArgParser.hpp>
45 #include <system/SystemTimer.h>
46
47 #include "TestInetCommon.h"
48 #include "TestInetCommonOptions.h"
49 #include "TestInetLayerCommon.hpp"
50 #include "TestSetupFaultInjection.h"
51 #include "TestSetupSignalling.h"
52
53 using namespace chip;
54 using namespace chip::ArgParser;
55 using namespace chip::Inet;
56 using namespace chip::System;
57
58 /* Preprocessor Macros */
59
60 #define kToolName "TestInetLayerMulticast"
61
62 #define kToolOptNoLoopback 'L'
63 #define kToolOptGroup 'g'
64
65 #define kToolOptExpectedGroupRxPackets (kToolOptBase + 0)
66 #define kToolOptExpectedGroupTxPackets (kToolOptBase + 1)
67
68 /* Type Definitions */
69
70 enum OptFlags
71 {
72     kOptFlagNoLoopback = 0x00010000
73 };
74
75 struct GroupAddress
76 {
77     uint32_t mGroup;
78     TransferStats mStats;
79     IPAddress mMulticastAddress;
80 };
81
82 template <size_t tCapacity>
83 struct GroupAddresses
84 {
85     size_t mSize;
86     const size_t mCapacity = tCapacity;
87     GroupAddress mAddresses[tCapacity];
88 };
89
90 template <size_t tCapacity>
91 struct TestState
92 {
93     GroupAddresses<tCapacity> & mGroupAddresses;
94     TestStatus mStatus;
95 };
96
97 /* Function Declarations */
98
99 static void HandleSignal(int aSignal);
100 static bool HandleOption(const char * aProgram, OptionSet * aOptions, int aIdentifier, const char * aName, const char * aValue);
101 static bool HandleNonOptionArgs(const char * aProgram, int argc, char * argv[]);
102 static bool ParseGroupOpt(const char * aProgram, const char * aValue, bool aIPv6, uint32_t & aOutLastGroupIndex);
103 static bool ParseAndUpdateExpectedGroupPackets(const char * aProgram, const char * aValue, uint32_t aGroup,
104                                                const char * aDescription, uint32_t & aOutExpected);
105
106 static void StartTest();
107 static void CleanupTest();
108
109 /* Global Variables */
110
111 // clang-format off
112 static const uint32_t    kOptFlagsDefault       = (kOptFlagUseIPv6 | kOptFlagUseRawIP);
113
114 static RawEndPoint *     sRawIPEndPoint         = nullptr;
115 static UDPEndPoint *     sUDPIPEndPoint         = nullptr;
116
117 static GroupAddresses<4> sGroupAddresses;
118
119 static TestState<4>      sTestState             =
120 {
121     sGroupAddresses,
122     { false, false }
123 };
124
125 static uint32_t          sLastGroupIndex        = 0;
126
127 static OptionDef         sToolOptionDefs[] =
128 {
129     { "interface",                 kArgumentRequired,  kToolOptInterface              },
130     { "group",                     kArgumentRequired,  kToolOptGroup                  },
131     { "group-expected-rx-packets", kArgumentRequired,  kToolOptExpectedGroupRxPackets },
132     { "group-expected-tx-packets", kArgumentRequired,  kToolOptExpectedGroupTxPackets },
133     { "interval",                  kArgumentRequired,  kToolOptInterval               },
134 #if INET_CONFIG_ENABLE_IPV4
135     { "ipv4",                      kNoArgument,        kToolOptIPv4Only               },
136 #endif // INET_CONFIG_ENABLE_IPV4
137     { "ipv6",                      kNoArgument,        kToolOptIPv6Only               },
138     { "listen",                    kNoArgument,        kToolOptListen                 },
139     { "no-loopback",               kNoArgument,        kToolOptNoLoopback             },
140     { "raw",                       kNoArgument,        kToolOptRawIP                  },
141     { "send-size",                 kArgumentRequired,  kToolOptSendSize               },
142     { "udp",                       kNoArgument,        kToolOptUDPIP                  },
143     { }
144 };
145
146 static const char *      sToolOptionHelp =
147     "  -I, --interface <interface>\n"
148     "       The network interface to bind to and from which to send and receive all packets.\n"
149     "\n"
150     "  -L, --no-loopback\n"
151     "       Suppress the loopback of multicast packets.\n"
152     "\n"
153     "  -g, --group <group>\n"
154     "       Multicast group number to join.\n"
155     "\n"
156     "  --group-expected-rx-packets <packets>\n"
157     "       Expect to receive this number of packets for the previously-specified multicast group.\n"
158     "\n"
159     "  --group-expected-tx-packets <packets>\n"
160     "       Expect to send this number of packets for the previously-specified multicast group.\n"
161     "\n"
162     "  -i, --interval <interval>\n"
163     "       Wait interval milliseconds between sending each packet (default: 1000 ms).\n"
164     "\n"
165     "  -l, --listen\n"
166     "       Act as a server (i.e., listen) for packets rather than send them.\n"
167     "\n"
168 #if INET_CONFIG_ENABLE_IPV4
169     "  -4, --ipv4\n"
170     "       Use IPv4 only.\n"
171     "\n"
172 #endif // INET_CONFIG_ENABLE_IPV4
173     "  -6, --ipv6\n"
174     "       Use IPv6 only (default).\n"
175     "\n"
176     "  -s, --send-size <size>\n"
177     "       Send size bytes of user data (default: 59 bytes)\n"
178     "\n"
179     "  -r, --raw\n"
180     "       Use raw IP (default).\n"
181     "\n"
182     "  -u, --udp\n"
183     "       Use UDP over IP.\n"
184     "\n";
185
186 static OptionSet         sToolOptions =
187 {
188     HandleOption,
189     sToolOptionDefs,
190     "GENERAL OPTIONS",
191     sToolOptionHelp
192 };
193
194 static HelpOptions       sHelpOptions(
195     kToolName,
196     "Usage: " kToolName " [ <options> ] [ -g <group> [ ... ] -I <interface> ]\n",
197     CHIP_VERSION_STRING "\n" CHIP_TOOL_COPYRIGHT
198 );
199
200 static OptionSet *       sToolOptionSets[] =
201 {
202     &sToolOptions,
203     &gNetworkOptions,
204     &gFaultInjectionOptions,
205     &sHelpOptions,
206     nullptr
207 };
208 // clang-format on
209
210 static void CheckSucceededOrFailed(const GroupAddress & aAddress, bool & aOutSucceeded, bool & aOutFailed)
211 {
212     const TransferStats & lStats = aAddress.mStats;
213
214 #if DEBUG
215     printf("Group %u: %u/%u sent, %u/%u received\n", aAddress.mGroup, lStats.mTransmit.mActual, lStats.mTransmit.mExpected,
216            lStats.mReceive.mActual, lStats.mReceive.mExpected);
217 #endif
218
219     if (((lStats.mTransmit.mExpected > 0) && (lStats.mTransmit.mActual > lStats.mTransmit.mExpected)) ||
220         ((lStats.mReceive.mExpected > 0) && (lStats.mReceive.mActual > lStats.mReceive.mExpected)))
221     {
222         aOutFailed = true;
223     }
224     else if (((lStats.mTransmit.mExpected > 0) && (lStats.mTransmit.mActual < lStats.mTransmit.mExpected)) ||
225              ((lStats.mReceive.mExpected > 0) && (lStats.mReceive.mActual < lStats.mReceive.mExpected)))
226     {
227         aOutSucceeded = false;
228     }
229 }
230
231 template <size_t tCapacity>
232 static void CheckSucceededOrFailed(TestState<tCapacity> & aTestState, bool & aOutSucceeded, bool & aOutFailed)
233 {
234     for (size_t i = 0; i < aTestState.mGroupAddresses.mSize; i++)
235     {
236         const GroupAddress & lGroup = aTestState.mGroupAddresses.mAddresses[i];
237
238         CheckSucceededOrFailed(lGroup, aOutSucceeded, aOutFailed);
239     }
240
241     if (aOutSucceeded || aOutFailed)
242     {
243         if (aOutSucceeded)
244             aTestState.mStatus.mSucceeded = true;
245
246         if (aOutFailed)
247             SetStatusFailed(aTestState.mStatus);
248     }
249 }
250
251 static void HandleSignal(int aSignal)
252 {
253     switch (aSignal)
254     {
255
256     case SIGUSR1:
257         SetStatusFailed(sTestState.mStatus);
258         break;
259     }
260 }
261
262 namespace TestInetLayerMulticast {
263 int main(int argc, char * argv[])
264 {
265     bool lSuccessful = true;
266     INET_ERROR lStatus;
267
268     InitTestInetCommon();
269
270     SetupFaultInjectionContext(argc, argv);
271
272     SetSignalHandler(HandleSignal);
273
274     if (argc == 1)
275     {
276         sHelpOptions.PrintBriefUsage(stderr);
277         lSuccessful = false;
278         goto exit;
279     }
280
281     if (!ParseArgsFromEnvVar(kToolName, TOOL_OPTIONS_ENV_VAR_NAME, sToolOptionSets, nullptr, true) ||
282         !ParseArgs(kToolName, argc, argv, sToolOptionSets, HandleNonOptionArgs))
283     {
284         lSuccessful = false;
285         goto exit;
286     }
287
288     InitSystemLayer();
289
290     InitNetwork();
291
292     // At this point, we should have valid network interfaces,
293     // including LwIP TUN/TAP shim interfaces. Validate the
294     // -I/--interface argument, if present.
295
296     if (gInterfaceName != nullptr)
297     {
298         lStatus = InterfaceNameToId(gInterfaceName, gInterfaceId);
299         if (lStatus != INET_NO_ERROR)
300         {
301             PrintArgError("%s: unknown network interface %s\n", kToolName, gInterfaceName);
302             lSuccessful = false;
303             goto shutdown;
304         }
305     }
306
307     // If any multicast groups have been specified, ensure that a
308     // network interface identifier has been specified and is valid.
309
310     if ((sGroupAddresses.mSize > 0) && !IsInterfaceIdPresent(gInterfaceId))
311     {
312         PrintArgError("%s: a network interface is required when specifying one or more multicast groups\n", kToolName);
313         lSuccessful = false;
314         goto shutdown;
315     }
316
317     StartTest();
318
319     while (Common::IsTesting(sTestState.mStatus))
320     {
321         struct timeval sleepTime;
322         bool lSucceeded = true;
323         bool lFailed    = false;
324
325         sleepTime.tv_sec  = 0;
326         sleepTime.tv_usec = 10000;
327
328         ServiceNetwork(sleepTime);
329
330         CheckSucceededOrFailed(sTestState, lSucceeded, lFailed);
331
332 #if DEBUG
333         printf("%s %s number of expected packets\n", ((lSucceeded) ? "successfully" : ((lFailed) ? "failed to" : "has not yet")),
334                ((lSucceeded)
335                     ? (Common::IsReceiver() ? "received" : "sent")
336                     : ((lFailed) ? (Common::IsReceiver() ? "receive" : "send") : Common::IsReceiver() ? "received" : "sent")));
337 #endif
338     }
339
340     CleanupTest();
341
342 shutdown:
343     ShutdownNetwork();
344     ShutdownSystemLayer();
345
346     lSuccessful = Common::WasSuccessful(sTestState.mStatus);
347
348 exit:
349     return (lSuccessful ? EXIT_SUCCESS : EXIT_FAILURE);
350 }
351 } // namespace TestInetLayerMulticast
352
353 static bool HandleOption(const char * aProgram, OptionSet * aOptions, int aIdentifier, const char * aName, const char * aValue)
354 {
355     bool retval = true;
356
357     switch (aIdentifier)
358     {
359
360     case kToolOptInterval:
361         if (!ParseInt(aValue, gSendIntervalMs))
362         {
363             PrintArgError("%s: invalid value specified for send interval: %s\n", aProgram, aValue);
364             retval = false;
365         }
366         break;
367
368     case kToolOptListen:
369         gOptFlags |= kOptFlagListen;
370         break;
371
372     case kToolOptNoLoopback:
373         gOptFlags |= kOptFlagNoLoopback;
374         break;
375
376     case kToolOptGroup:
377         if (!ParseGroupOpt(aProgram, aValue, gOptFlags & kOptFlagUseIPv6, sLastGroupIndex))
378         {
379             retval = false;
380         }
381         break;
382
383     case kToolOptExpectedGroupRxPackets: {
384         GroupAddress & lGroupAddress = sGroupAddresses.mAddresses[sLastGroupIndex];
385
386         if (!ParseAndUpdateExpectedGroupPackets(aProgram, aValue, lGroupAddress.mGroup, "received",
387                                                 lGroupAddress.mStats.mReceive.mExpected))
388         {
389             retval = false;
390         }
391     }
392     break;
393
394     case kToolOptExpectedGroupTxPackets: {
395         GroupAddress & lGroupAddress = sGroupAddresses.mAddresses[sLastGroupIndex];
396
397         if (!ParseAndUpdateExpectedGroupPackets(aProgram, aValue, lGroupAddress.mGroup, "sent",
398                                                 lGroupAddress.mStats.mTransmit.mExpected))
399         {
400             retval = false;
401         }
402     }
403     break;
404
405 #if INET_CONFIG_ENABLE_IPV4
406     case kToolOptIPv4Only:
407         if (gOptFlags & kOptFlagUseIPv6)
408         {
409             PrintArgError("%s: the use of --ipv4 is exclusive with --ipv6. Please select only one of the two options.\n", aProgram);
410             retval = false;
411         }
412         gOptFlags |= kOptFlagUseIPv4;
413         break;
414 #endif // INET_CONFIG_ENABLE_IPV4
415
416     case kToolOptIPv6Only:
417         if (gOptFlags & kOptFlagUseIPv4)
418         {
419             PrintArgError("%s: the use of --ipv6 is exclusive with --ipv4. Please select only one of the two options.\n", aProgram);
420             retval = false;
421         }
422         gOptFlags |= kOptFlagUseIPv6;
423         break;
424
425     case kToolOptInterface:
426
427         // NOTE: When using LwIP on a hosted OS, the interface will
428         // not actually be available until AFTER InitNetwork,
429         // consequently, we cannot do any meaningful validation
430         // here. Simply save the value off and we will validate it
431         // later.
432
433         gInterfaceName = aValue;
434         break;
435
436     case kToolOptRawIP:
437         if (gOptFlags & kOptFlagUseUDPIP)
438         {
439             PrintArgError("%s: the use of --raw is exclusive with --udp. Please select only one of the two options.\n", aProgram);
440             retval = false;
441         }
442         gOptFlags |= kOptFlagUseRawIP;
443         break;
444
445     case kToolOptSendSize:
446         if (!ParseInt(aValue, gSendSize))
447         {
448             PrintArgError("%s: invalid value specified for send size: %s\n", aProgram, aValue);
449             return false;
450         }
451         break;
452
453     case kToolOptUDPIP:
454         if (gOptFlags & kOptFlagUseRawIP)
455         {
456             PrintArgError("%s: the use of --udp is exclusive with --raw. Please select only one of the two options.\n", aProgram);
457             retval = false;
458         }
459         gOptFlags |= kOptFlagUseUDPIP;
460         break;
461
462     default:
463         PrintArgError("%s: INTERNAL ERROR: Unhandled option: %s\n", aProgram, aName);
464         retval = false;
465         break;
466     }
467
468     return (retval);
469 }
470
471 bool HandleNonOptionArgs(const char * aProgram, int argc, char * argv[])
472 {
473     bool retval = true;
474
475     if ((gOptFlags & (kOptFlagListen | kOptFlagNoLoopback)) == (kOptFlagListen | kOptFlagNoLoopback))
476     {
477         PrintArgError("%s: the listen option is exclusive with the loopback suppression option. Please select one or the other.\n",
478                       aProgram);
479         retval = false;
480         goto exit;
481     }
482
483     // If there were any additional, non-parsed arguments, it's an error.
484
485     if (argc > 0)
486     {
487         PrintArgError("%s: unexpected argument: %s\n", aProgram, argv[0]);
488         retval = false;
489         goto exit;
490     }
491
492     // If no IP version or transport flags were specified, use the defaults.
493
494     if (!(gOptFlags & (kOptFlagUseIPv4 | kOptFlagUseIPv6 | kOptFlagUseRawIP | kOptFlagUseUDPIP)))
495     {
496         gOptFlags |= kOptFlagsDefault;
497     }
498
499 exit:
500     return (retval);
501 }
502
503 // Create an IPv4 administratively-scoped multicast address
504
505 static IPAddress MakeIPv4Multicast(uint32_t aGroupIdentifier)
506 {
507     IPAddress lAddress;
508
509     lAddress.Addr[0] = 0;
510     lAddress.Addr[1] = 0;
511     lAddress.Addr[2] = nlByteOrderSwap32HostToBig(0xFFFF);
512     lAddress.Addr[3] = nlByteOrderSwap32HostToBig((239 << 24) | (aGroupIdentifier & 0xFFFFFF));
513
514     return (lAddress);
515 }
516
517 // Create an IPv6 site-scoped multicast address
518
519 static IPAddress MakeIPv6Multicast(uint32_t aGroupIdentifier)
520 {
521     const uint8_t lFlags = kIPv6MulticastFlag_Transient;
522
523     return (IPAddress::MakeIPv6Multicast(lFlags, kIPv6MulticastScope_Site, aGroupIdentifier));
524 }
525
526 static void SetGroup(GroupAddress & aGroupAddress, uint32_t aGroupIdentifier, uint32_t aExpectedRx, uint32_t aExpectedTx)
527 {
528     aGroupAddress.mGroup                     = aGroupIdentifier;
529     aGroupAddress.mStats.mReceive.mExpected  = aExpectedRx;
530     aGroupAddress.mStats.mReceive.mActual    = 0;
531     aGroupAddress.mStats.mTransmit.mExpected = aExpectedTx;
532     aGroupAddress.mStats.mTransmit.mActual   = 0;
533 }
534
535 static bool ParseGroupOpt(const char * aProgram, const char * aValue, bool aIPv6, uint32_t & aOutLastGroupIndex)
536 {
537     uint32_t lGroupIdentifier;
538     bool lRetval = true;
539
540     if (sGroupAddresses.mSize == sGroupAddresses.mCapacity)
541     {
542         PrintArgError("%s: the maximum number of allowed groups (%zu) have been specified\n", aProgram, sGroupAddresses.mCapacity);
543         lRetval = false;
544         goto exit;
545     }
546
547     if (!ParseInt(aValue, lGroupIdentifier))
548     {
549         PrintArgError("%s: unrecognized group %s\n", aProgram, aValue);
550         lRetval = false;
551         goto exit;
552     }
553
554     aOutLastGroupIndex = sGroupAddresses.mSize++;
555
556     SetGroup(sGroupAddresses.mAddresses[aOutLastGroupIndex], lGroupIdentifier, lGroupIdentifier, lGroupIdentifier);
557
558 exit:
559     return (lRetval);
560 }
561
562 static bool ParseAndUpdateExpectedGroupPackets(const char * aProgram, const char * aValue, uint32_t aGroup,
563                                                const char * aDescription, uint32_t & aOutExpected)
564 {
565     uint32_t lExpectedGroupPackets;
566     bool lRetval = true;
567
568     if (!ParseInt(aValue, lExpectedGroupPackets))
569     {
570         PrintArgError("%s: invalid value specified for expected group %u %s packets: %s\n", aProgram, aGroup, aDescription, aValue);
571         lRetval = false;
572         goto exit;
573     }
574
575     aOutExpected = lExpectedGroupPackets;
576
577 exit:
578     return (lRetval);
579 }
580
581 static GroupAddress * FindGroupAddress(const IPAddress & aSourceAddress)
582 {
583     GroupAddress * lResult = nullptr;
584
585     for (size_t i = 0; i < sGroupAddresses.mSize; i++)
586     {
587         GroupAddress & lGroupAddress = sGroupAddresses.mAddresses[i];
588
589         if (lGroupAddress.mMulticastAddress == aSourceAddress)
590         {
591             lResult = &lGroupAddress;
592             break;
593         }
594     }
595
596     return (lResult);
597 }
598
599 static void PrintReceivedStats(const GroupAddress & aGroupAddress)
600 {
601     printf("%u/%u received for multicast group %u\n", aGroupAddress.mStats.mReceive.mActual,
602            aGroupAddress.mStats.mReceive.mExpected, aGroupAddress.mGroup);
603 }
604
605 static bool HandleDataReceived(const PacketBufferHandle & aBuffer, GroupAddress & aGroupAddress, bool aCheckBuffer)
606 {
607     const bool lStatsByPacket = true;
608     bool lStatus              = true;
609
610     lStatus = Common::HandleDataReceived(aBuffer, aGroupAddress.mStats, lStatsByPacket, aCheckBuffer);
611     VerifyOrExit(lStatus == true, );
612
613     PrintReceivedStats(aGroupAddress);
614
615 exit:
616     return (lStatus);
617 }
618
619 static bool HandleDataReceived(const PacketBufferHandle & aBuffer, const IPPacketInfo & aPacketInfo, bool aCheckBuffer)
620 {
621     bool lStatus = true;
622     GroupAddress * lGroupAddress;
623
624     lGroupAddress = FindGroupAddress(aPacketInfo.DestAddress);
625
626     if (lGroupAddress != nullptr)
627     {
628         lStatus = HandleDataReceived(aBuffer, *lGroupAddress, aCheckBuffer);
629         VerifyOrExit(lStatus == true, );
630     }
631
632 exit:
633     return (lStatus);
634 }
635
636 // Raw Endpoint Callbacks
637
638 static void HandleRawMessageReceived(IPEndPointBasis * aEndPoint, PacketBufferHandle aBuffer, const IPPacketInfo * aPacketInfo)
639 {
640     const bool lCheckBuffer   = true;
641     const bool lStatsByPacket = true;
642     IPAddressType lAddressType;
643     bool lStatus = true;
644     GroupAddress * lGroupAddress;
645
646     VerifyOrExit(aEndPoint != nullptr, lStatus = false);
647     VerifyOrExit(!aBuffer.IsNull(), lStatus = false);
648     VerifyOrExit(aPacketInfo != nullptr, lStatus = false);
649
650     Common::HandleRawMessageReceived(aEndPoint, aBuffer, aPacketInfo);
651
652     lGroupAddress = FindGroupAddress(aPacketInfo->DestAddress);
653
654     if (lGroupAddress != nullptr)
655     {
656         lAddressType = aPacketInfo->DestAddress.Type();
657
658         if (lAddressType == kIPAddressType_IPv4)
659         {
660             const uint16_t kIPv4HeaderSize = 20;
661
662             aBuffer->ConsumeHead(kIPv4HeaderSize);
663
664             lStatus = Common::HandleICMPv4DataReceived(std::move(aBuffer), lGroupAddress->mStats, lStatsByPacket, lCheckBuffer);
665         }
666         else if (lAddressType == kIPAddressType_IPv6)
667         {
668             lStatus = Common::HandleICMPv6DataReceived(std::move(aBuffer), lGroupAddress->mStats, lStatsByPacket, lCheckBuffer);
669         }
670         else
671         {
672             lStatus = false;
673         }
674
675         if (lStatus)
676         {
677             PrintReceivedStats(*lGroupAddress);
678         }
679     }
680
681 exit:
682     if (!lStatus)
683     {
684         SetStatusFailed(sTestState.mStatus);
685     }
686 }
687
688 static void HandleRawReceiveError(IPEndPointBasis * aEndPoint, INET_ERROR aError, const IPPacketInfo * aPacketInfo)
689 {
690     Common::HandleRawReceiveError(aEndPoint, aError, aPacketInfo);
691
692     SetStatusFailed(sTestState.mStatus);
693 }
694
695 // UDP Endpoint Callbacks
696
697 static void HandleUDPMessageReceived(IPEndPointBasis * aEndPoint, PacketBufferHandle aBuffer, const IPPacketInfo * aPacketInfo)
698 {
699     const bool lCheckBuffer = true;
700     bool lStatus;
701
702     VerifyOrExit(aEndPoint != nullptr, lStatus = false);
703     VerifyOrExit(!aBuffer.IsNull(), lStatus = false);
704     VerifyOrExit(aPacketInfo != nullptr, lStatus = false);
705
706     Common::HandleUDPMessageReceived(aEndPoint, aBuffer, aPacketInfo);
707
708     lStatus = HandleDataReceived(std::move(aBuffer), *aPacketInfo, lCheckBuffer);
709
710 exit:
711     if (!lStatus)
712     {
713         SetStatusFailed(sTestState.mStatus);
714     }
715 }
716
717 static void HandleUDPReceiveError(IPEndPointBasis * aEndPoint, INET_ERROR aError, const IPPacketInfo * aPacketInfo)
718 {
719     Common::HandleUDPReceiveError(aEndPoint, aError, aPacketInfo);
720
721     SetStatusFailed(sTestState.mStatus);
722 }
723
724 static bool IsTransportReadyForSend()
725 {
726     bool lStatus = false;
727
728     if ((gOptFlags & (kOptFlagUseRawIP)) == (kOptFlagUseRawIP))
729     {
730         lStatus = (sRawIPEndPoint != nullptr);
731     }
732     else if ((gOptFlags & kOptFlagUseUDPIP) == kOptFlagUseUDPIP)
733     {
734         lStatus = (sUDPIPEndPoint != nullptr);
735     }
736
737     return (lStatus);
738 }
739
740 static INET_ERROR PrepareTransportForSend()
741 {
742     INET_ERROR lStatus = INET_NO_ERROR;
743
744     return (lStatus);
745 }
746
747 static INET_ERROR DriveSendForDestination(const IPAddress & aAddress, uint16_t aSize)
748 {
749     PacketBufferHandle lBuffer;
750     INET_ERROR lStatus = INET_NO_ERROR;
751
752     if ((gOptFlags & (kOptFlagUseRawIP)) == (kOptFlagUseRawIP))
753     {
754         // For ICMP (v4 or v6), we'll send n aSize or smaller
755         // datagrams (with overhead for the ICMP header), each
756         // patterned from zero to aSize - 1, following the ICMP
757         // header.
758
759         if ((gOptFlags & kOptFlagUseIPv6) == (kOptFlagUseIPv6))
760         {
761             lBuffer = Common::MakeICMPv6DataBuffer(aSize);
762             VerifyOrExit(!lBuffer.IsNull(), lStatus = INET_ERROR_NO_MEMORY);
763         }
764 #if INET_CONFIG_ENABLE_IPV4
765         else if ((gOptFlags & kOptFlagUseIPv4) == (kOptFlagUseIPv4))
766         {
767             lBuffer = Common::MakeICMPv4DataBuffer(aSize);
768             VerifyOrExit(!lBuffer.IsNull(), lStatus = INET_ERROR_NO_MEMORY);
769         }
770 #endif // INET_CONFIG_ENABLE_IPV4
771
772         lStatus = sRawIPEndPoint->SendTo(aAddress, std::move(lBuffer));
773         SuccessOrExit(lStatus);
774     }
775     else
776     {
777         if ((gOptFlags & kOptFlagUseUDPIP) == kOptFlagUseUDPIP)
778         {
779             const uint8_t lFirstValue = 0;
780
781             // For UDP, we'll send n aSize or smaller datagrams, each
782             // patterned from zero to aSize - 1.
783
784             lBuffer = Common::MakeDataBuffer(aSize, lFirstValue);
785             VerifyOrExit(!lBuffer.IsNull(), lStatus = INET_ERROR_NO_MEMORY);
786
787             lStatus = sUDPIPEndPoint->SendTo(aAddress, kUDPPort, std::move(lBuffer));
788             SuccessOrExit(lStatus);
789         }
790     }
791
792 exit:
793     return (lStatus);
794 }
795
796 static INET_ERROR DriveSendForGroup(GroupAddress & aGroupAddress)
797 {
798     INET_ERROR lStatus = INET_NO_ERROR;
799
800     if (aGroupAddress.mStats.mTransmit.mActual < aGroupAddress.mStats.mTransmit.mExpected)
801     {
802         lStatus = DriveSendForDestination(aGroupAddress.mMulticastAddress, gSendSize);
803         SuccessOrExit(lStatus);
804
805         aGroupAddress.mStats.mTransmit.mActual++;
806
807         printf("%u/%u transmitted for multicast group %u\n", aGroupAddress.mStats.mTransmit.mActual,
808                aGroupAddress.mStats.mTransmit.mExpected, aGroupAddress.mGroup);
809     }
810
811 exit:
812     return (lStatus);
813 }
814
815 template <size_t tCapacity>
816 static INET_ERROR DriveSendForGroups(GroupAddresses<tCapacity> & aGroupAddresses)
817 {
818     INET_ERROR lStatus = INET_NO_ERROR;
819
820     // Iterate over each multicast group for which this node is a
821     // member and send a packet.
822
823     for (size_t i = 0; i < aGroupAddresses.mSize; i++)
824     {
825         GroupAddress & lGroupAddress = aGroupAddresses.mAddresses[i];
826
827         lStatus = DriveSendForGroup(lGroupAddress);
828         SuccessOrExit(lStatus);
829     }
830
831 exit:
832     return (lStatus);
833 }
834
835 void DriveSend()
836 {
837     INET_ERROR lStatus = INET_NO_ERROR;
838
839     if (!Common::IsSender())
840         goto exit;
841
842     if (!gSendIntervalExpired)
843         goto exit;
844
845     if (!IsTransportReadyForSend())
846     {
847         lStatus = PrepareTransportForSend();
848         SuccessOrExit(lStatus);
849     }
850     else
851     {
852         gSendIntervalExpired = false;
853         gSystemLayer.StartTimer(gSendIntervalMs, Common::HandleSendTimerComplete, nullptr);
854
855         lStatus = DriveSendForGroups(sGroupAddresses);
856         SuccessOrExit(lStatus);
857     }
858
859 exit:
860     if (lStatus != INET_NO_ERROR)
861     {
862         SetStatusFailed(sTestState.mStatus);
863     }
864 }
865
866 static void StartTest()
867 {
868     IPAddressType lIPAddressType = kIPAddressType_IPv6;
869     IPProtocol lIPProtocol       = kIPProtocol_ICMPv6;
870     IPVersion lIPVersion         = kIPVersion_6;
871     IPAddress lAddress           = IPAddress::Any;
872     IPEndPointBasis * lEndPoint  = nullptr;
873     const bool lUseLoopback      = ((gOptFlags & kOptFlagNoLoopback) == 0);
874     INET_ERROR lStatus;
875
876 #if INET_CONFIG_ENABLE_IPV4
877     if (gOptFlags & kOptFlagUseIPv4)
878     {
879         lIPAddressType = kIPAddressType_IPv4;
880         lIPProtocol    = kIPProtocol_ICMPv4;
881         lIPVersion     = kIPVersion_4;
882     }
883 #endif // INET_CONFIG_ENABLE_IPV4
884
885     // clang-format off
886     printf("Using %sIP%s, device interface: %s (w/%c LwIP)\n",
887            ((gOptFlags & kOptFlagUseRawIP) ? "" : "UDP/"),
888            ((gOptFlags & kOptFlagUseIPv4) ? "v4" : "v6"),
889            ((gInterfaceName) ? gInterfaceName : "<none>"),
890            (CHIP_SYSTEM_CONFIG_USE_LWIP ? '\0' : 'o'));
891         // clang-format ob
892
893     // Allocate the endpoints for sending or receiving.
894
895     if (gOptFlags & kOptFlagUseRawIP)
896     {
897         lStatus = gInet.NewRawEndPoint(lIPVersion, lIPProtocol, &sRawIPEndPoint);
898         INET_FAIL_ERROR(lStatus, "InetLayer::NewRawEndPoint failed");
899
900         sRawIPEndPoint->OnMessageReceived = HandleRawMessageReceived;
901         sRawIPEndPoint->OnReceiveError    = HandleRawReceiveError;
902
903         lStatus = sRawIPEndPoint->Bind(lIPAddressType, lAddress);
904         INET_FAIL_ERROR(lStatus, "RawEndPoint::Bind failed");
905
906         if (gOptFlags & kOptFlagUseIPv6)
907         {
908             lStatus = sRawIPEndPoint->SetICMPFilter(kICMPv6_FilterTypes, gICMPv6Types);
909             INET_FAIL_ERROR(lStatus, "RawEndPoint::SetICMPFilter (IPv6) failed");
910         }
911
912         if (IsInterfaceIdPresent(gInterfaceId))
913         {
914             lStatus = sRawIPEndPoint->BindInterface(lIPAddressType, gInterfaceId);
915             INET_FAIL_ERROR(lStatus, "RawEndPoint::BindInterface failed");
916         }
917
918         lStatus = sRawIPEndPoint->Listen();
919         INET_FAIL_ERROR(lStatus, "RawEndPoint::Listen failed");
920
921         lEndPoint = sRawIPEndPoint;
922     }
923     else if (gOptFlags & kOptFlagUseUDPIP)
924     {
925         lStatus = gInet.NewUDPEndPoint(&sUDPIPEndPoint);
926         INET_FAIL_ERROR(lStatus, "InetLayer::NewUDPEndPoint failed");
927
928         sUDPIPEndPoint->OnMessageReceived = HandleUDPMessageReceived;
929         sUDPIPEndPoint->OnReceiveError    = HandleUDPReceiveError;
930
931         lStatus = sUDPIPEndPoint->Bind(lIPAddressType, lAddress, kUDPPort);
932         INET_FAIL_ERROR(lStatus, "UDPEndPoint::Bind failed");
933
934         if (IsInterfaceIdPresent(gInterfaceId))
935         {
936             lStatus = sUDPIPEndPoint->BindInterface(lIPAddressType, gInterfaceId);
937             INET_FAIL_ERROR(lStatus, "UDPEndPoint::BindInterface failed");
938         }
939
940         lStatus = sUDPIPEndPoint->Listen();
941         INET_FAIL_ERROR(lStatus, "UDPEndPoint::Listen failed");
942
943         lEndPoint = sUDPIPEndPoint;
944     }
945
946     // If loopback suppression has been requested, attempt to disable
947     // it; otherwise, attempt to enable it.
948
949     lStatus = lEndPoint->SetMulticastLoopback(lIPVersion, lUseLoopback);
950     INET_FAIL_ERROR(lStatus, "SetMulticastLoopback failed");
951
952     // Configure and join the multicast groups
953
954     for (size_t i = 0; i < sGroupAddresses.mSize; i++)
955     {
956         char lAddressBuffer[INET6_ADDRSTRLEN];
957         GroupAddress & lGroupAddress  = sGroupAddresses.mAddresses[i];
958         IPAddress & lMulticastAddress = lGroupAddress.mMulticastAddress;
959
960         if ((lEndPoint != nullptr) && IsInterfaceIdPresent(gInterfaceId))
961         {
962             if (gOptFlags & kOptFlagUseIPv4)
963             {
964                 lMulticastAddress = MakeIPv4Multicast(lGroupAddress.mGroup);
965             }
966             else
967             {
968                 lMulticastAddress = MakeIPv6Multicast(lGroupAddress.mGroup);
969             }
970
971             lMulticastAddress.ToString(lAddressBuffer, sizeof(lAddressBuffer));
972
973             printf("Will join multicast group %s\n", lAddressBuffer);
974
975             lStatus = lEndPoint->JoinMulticastGroup(gInterfaceId, lMulticastAddress);
976             INET_FAIL_ERROR(lStatus, "Could not join multicast group");
977         }
978     }
979
980     if (Common::IsReceiver())
981         printf("Listening...\n");
982     else
983         DriveSend();
984 }
985
986 static void CleanupTest()
987 {
988     IPEndPointBasis * lEndPoint = nullptr;
989     INET_ERROR lStatus;
990
991     gSendIntervalExpired = false;
992     gSystemLayer.CancelTimer(Common::HandleSendTimerComplete, nullptr);
993
994     //  Leave the multicast groups
995
996     if (gOptFlags & kOptFlagUseRawIP)
997     {
998         lEndPoint = sRawIPEndPoint;
999     }
1000     else if (gOptFlags & kOptFlagUseUDPIP)
1001     {
1002         lEndPoint = sUDPIPEndPoint;
1003     }
1004
1005     for (size_t i = 0; i < sGroupAddresses.mSize; i++)
1006     {
1007         char lAddressBuffer[INET6_ADDRSTRLEN];
1008         GroupAddress & lGroupAddress  = sGroupAddresses.mAddresses[i];
1009         IPAddress & lMulticastAddress = lGroupAddress.mMulticastAddress;
1010
1011         if ((lEndPoint != nullptr) && IsInterfaceIdPresent(gInterfaceId))
1012         {
1013             lMulticastAddress.ToString(lAddressBuffer, sizeof(lAddressBuffer));
1014
1015             printf("Will leave multicast group %s\n", lAddressBuffer);
1016
1017             lStatus = lEndPoint->LeaveMulticastGroup(gInterfaceId, lMulticastAddress);
1018             INET_FAIL_ERROR(lStatus, "Could not leave multicast group");
1019         }
1020     }
1021
1022     // Release the resources associated with the allocated end points.
1023
1024     if (sRawIPEndPoint != nullptr)
1025     {
1026         sRawIPEndPoint->Free();
1027     }
1028
1029     if (sUDPIPEndPoint != nullptr)
1030     {
1031         sUDPIPEndPoint->Free();
1032     }
1033 }