3 * Copyright (c) 2020-2021 Project CHIP Authors
6 * Licensed under the Apache License, Version 2.0 (the "License");
7 * you may not use this file except in compliance with the License.
8 * You may obtain a copy of the License at
10 * http://www.apache.org/licenses/LICENSE-2.0
12 * Unless required by applicable law or agreed to in writing, software
13 * distributed under the License is distributed on an "AS IS" BASIS,
14 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15 * See the License for the specific language governing permissions and
16 * limitations under the License.
21 * This file implements unit tests for the TcpTransport implementation.
24 #include "NetworkTestHelpers.h"
26 #include <core/CHIPCore.h>
27 #include <core/CHIPEncoding.h>
28 #include <support/CHIPMem.h>
29 #include <support/CodeUtils.h>
30 #include <support/UnitTestRegistration.h>
31 #include <system/SystemLayer.h>
32 #include <system/SystemObject.h>
33 #include <transport/TransportMgr.h>
34 #include <transport/raw/TCP.h>
36 #include <nlbyteorder.h>
37 #include <nlunit-test.h>
45 using namespace chip::Inet;
47 static int Initialize(void * aContext);
48 static int Finalize(void * aContext);
55 static void CheckProcessReceivedBuffer(nlTestSuite * inSuite, void * inContext);
57 } // namespace Transport
62 constexpr size_t kMaxTcpActiveConnectionCount = 4;
63 constexpr size_t kMaxTcpPendingPackets = 4;
64 constexpr uint16_t kPacketSizeBytes = static_cast<uint16_t>(sizeof(uint16_t));
66 using TCPImpl = Transport::TCP<kMaxTcpActiveConnectionCount, kMaxTcpPendingPackets>;
68 constexpr NodeId kSourceNodeId = 123654;
69 constexpr NodeId kDestinationNodeId = 111222333;
70 constexpr uint32_t kMessageId = 18;
72 using TestContext = chip::Test::IOContext;
75 const char PAYLOAD[] = "Hello!";
77 class MockTransportMgrDelegate : public chip::TransportMgrDelegate
80 typedef int (*MessageReceivedCallback)(const uint8_t * message, size_t length, int count, void * data);
82 MockTransportMgrDelegate(nlTestSuite * inSuite, TestContext & inContext) :
83 mSuite(inSuite), mContext(inContext), mCallback(nullptr), mCallbackData(nullptr)
85 ~MockTransportMgrDelegate() override {}
87 void SetCallback(MessageReceivedCallback callback = nullptr, void * callback_data = nullptr)
90 mCallbackData = callback_data;
92 void OnMessageReceived(const PacketHeader & header, const Transport::PeerAddress & source,
93 System::PacketBufferHandle msgBuf) override
95 NL_TEST_ASSERT(mSuite, header.GetSourceNodeId() == Optional<NodeId>::Value(kSourceNodeId));
96 NL_TEST_ASSERT(mSuite, header.GetDestinationNodeId() == Optional<NodeId>::Value(kDestinationNodeId));
97 NL_TEST_ASSERT(mSuite, header.GetMessageId() == kMessageId);
101 int err = mCallback(msgBuf->Start(), msgBuf->DataLength(), mReceiveHandlerCallCount, mCallbackData);
102 NL_TEST_ASSERT(mSuite, err == 0);
105 mReceiveHandlerCallCount++;
108 void InitializeMessageTest(TCPImpl & tcp, const IPAddress & addr)
110 CHIP_ERROR err = tcp.Init(Transport::TcpListenParameters(&mContext.GetInetLayer()).SetAddressType(addr.Type()));
111 NL_TEST_ASSERT(mSuite, err == CHIP_NO_ERROR);
113 mTransportMgrBase.SetSecureSessionMgr(this);
114 mTransportMgrBase.SetRendezvousSession(this);
115 mTransportMgrBase.Init(&tcp);
117 mReceiveHandlerCallCount = 0;
120 void SingleMessageTest(TCPImpl & tcp, const IPAddress & addr)
122 chip::System::PacketBufferHandle buffer = chip::System::PacketBufferHandle::NewWithData(PAYLOAD, sizeof(PAYLOAD));
123 NL_TEST_ASSERT(mSuite, !buffer.IsNull());
126 header.SetSourceNodeId(kSourceNodeId).SetDestinationNodeId(kDestinationNodeId).SetMessageId(kMessageId);
128 SetCallback([](const uint8_t * message, size_t length, int count, void * data) { return memcmp(message, data, length); },
129 const_cast<void *>(static_cast<const void *>(PAYLOAD)));
131 // Should be able to send a message to itself by just calling send.
132 CHIP_ERROR err = tcp.SendMessage(header, Transport::PeerAddress::TCP(addr), std::move(buffer));
133 if (err == System::MapErrorPOSIX(EADDRNOTAVAIL))
135 // TODO(#2698): the underlying system does not support IPV6. This early return
136 // should be removed and error should be made fatal.
137 printf("%s:%u: System does NOT support IPV6.\n", __FILE__, __LINE__);
141 NL_TEST_ASSERT(mSuite, err == CHIP_NO_ERROR);
143 mContext.DriveIOUntil(5000 /* ms */, [this]() { return mReceiveHandlerCallCount != 0; });
144 NL_TEST_ASSERT(mSuite, mReceiveHandlerCallCount == 1);
146 SetCallback(nullptr);
149 void FinalizeMessageTest(TCPImpl & tcp, const IPAddress & addr)
151 // Disconnect and wait for seeing peer close
152 tcp.Disconnect(Transport::PeerAddress::TCP(addr));
153 mContext.DriveIOUntil(5000 /* ms */, [&tcp]() { return !tcp.HasActiveConnections(); });
156 int mReceiveHandlerCallCount = 0;
159 nlTestSuite * mSuite;
160 TestContext & mContext;
161 MessageReceivedCallback mCallback;
162 void * mCallbackData;
163 TransportMgrBase mTransportMgrBase;
166 /////////////////////////// Init test
168 void CheckSimpleInitTest(nlTestSuite * inSuite, void * inContext, Inet::IPAddressType type)
170 TestContext & ctx = *reinterpret_cast<TestContext *>(inContext);
174 CHIP_ERROR err = tcp.Init(Transport::TcpListenParameters(&ctx.GetInetLayer()).SetAddressType(type));
176 NL_TEST_ASSERT(inSuite, err == CHIP_NO_ERROR);
179 #if INET_CONFIG_ENABLE_IPV4
180 void CheckSimpleInitTest4(nlTestSuite * inSuite, void * inContext)
182 CheckSimpleInitTest(inSuite, inContext, kIPAddressType_IPv4);
186 void CheckSimpleInitTest6(nlTestSuite * inSuite, void * inContext)
188 CheckSimpleInitTest(inSuite, inContext, kIPAddressType_IPv6);
191 /////////////////////////// Messaging test
193 void CheckMessageTest(nlTestSuite * inSuite, void * inContext, const IPAddress & addr)
195 TestContext & ctx = *reinterpret_cast<TestContext *>(inContext);
198 MockTransportMgrDelegate gMockTransportMgrDelegate(inSuite, ctx);
199 gMockTransportMgrDelegate.InitializeMessageTest(tcp, addr);
200 gMockTransportMgrDelegate.SingleMessageTest(tcp, addr);
201 gMockTransportMgrDelegate.FinalizeMessageTest(tcp, addr);
204 void CheckMessageTest4(nlTestSuite * inSuite, void * inContext)
207 IPAddress::FromString("127.0.0.1", addr);
208 CheckMessageTest(inSuite, inContext, addr);
211 void CheckMessageTest6(nlTestSuite * inSuite, void * inContext)
214 IPAddress::FromString("::1", addr);
215 CheckMessageTest(inSuite, inContext, addr);
218 // Generates a packet buffer or a chain of packet buffers for a single message.
221 // `sizes[]` is a zero-terminated sequence of packet buffer sizes.
222 // If total length supplied is not large enough for at least the PacketHeader and length field,
223 // the last buffer will be made larger.
224 TestData() : mHandle(), mPayload(nullptr), mTotalLength(0), mMessageLength(0), mMessageOffset(0) {}
225 ~TestData() { Free(); }
226 bool Init(const uint16_t sizes[]);
228 bool IsValid() { return !mHandle.IsNull() && (mPayload != nullptr); }
230 chip::System::PacketBufferHandle mHandle;
233 size_t mMessageLength;
234 size_t mMessageOffset;
237 bool TestData::Init(const uint16_t sizes[])
242 header.SetSourceNodeId(kSourceNodeId).SetDestinationNodeId(kDestinationNodeId).SetMessageId(kMessageId);
243 const size_t headerLength = header.EncodeSizeBytes();
245 // Determine the total length.
248 for (; sizes[bufferCount] != 0; ++bufferCount)
250 mTotalLength += sizes[bufferCount];
253 uint16_t additionalLength = 0;
254 if (headerLength + kPacketSizeBytes > mTotalLength)
256 additionalLength = static_cast<uint16_t>((headerLength + kPacketSizeBytes) - mTotalLength);
257 mTotalLength += additionalLength;
259 if (mTotalLength > UINT16_MAX)
263 uint16_t messageLength = static_cast<uint16_t>(mTotalLength - kPacketSizeBytes);
265 // Build the test payload.
266 uint8_t * payload = static_cast<uint8_t *>(chip::Platform::MemoryCalloc(1, mTotalLength));
267 if (payload == nullptr)
271 chip::Encoding::LittleEndian::Put16(payload, messageLength);
273 CHIP_ERROR err = header.Encode(payload + kPacketSizeBytes, messageLength, &headerSize);
274 if (err != CHIP_NO_ERROR)
278 mMessageLength = messageLength - headerSize;
279 mMessageOffset = kPacketSizeBytes + headerSize;
280 // Fill the rest of the payload with a recognizable pattern.
281 for (size_t i = mMessageOffset; i < mTotalLength; ++i)
283 payload[i] = static_cast<uint8_t>(i);
285 // When we get the message back, the header will have been removed.
287 // Allocate the buffer chain.
288 System::PacketBufferHandle head = chip::System::PacketBufferHandle::New(sizes[0], 0 /* reserve */);
289 for (int i = 1; i <= bufferCount; ++i)
291 uint16_t size = sizes[i];
292 if (i == bufferCount)
294 size = static_cast<uint16_t>(size + additionalLength);
296 chip::System::PacketBufferHandle buffer = chip::System::PacketBufferHandle::New(size, 0 /* reserve */);
301 head.AddToEnd(std::move(buffer));
304 // Write the test payload to the buffer chain.
305 System::PacketBufferHandle iterator = head.Retain();
306 uint8_t * writePayload = payload;
307 size_t writeLength = mTotalLength;
308 while (writeLength > 0)
310 if (iterator.IsNull())
314 size_t lAvailableLengthInCurrentBuf = iterator->AvailableDataLength();
315 size_t lToWriteToCurrentBuf = lAvailableLengthInCurrentBuf;
316 if (writeLength < lToWriteToCurrentBuf)
318 lToWriteToCurrentBuf = writeLength;
320 if (lToWriteToCurrentBuf != 0)
322 memcpy(iterator->Start(), writePayload, lToWriteToCurrentBuf);
323 iterator->SetDataLength(static_cast<uint16_t>(iterator->DataLength() + lToWriteToCurrentBuf), head);
324 writePayload += lToWriteToCurrentBuf;
325 writeLength -= lToWriteToCurrentBuf;
330 mHandle = std::move(head);
335 void TestData::Free()
337 chip::Platform::MemoryFree(mPayload);
345 int TestDataCallbackCheck(const uint8_t * message, size_t length, int count, void * data)
351 TestData * currentData = static_cast<TestData *>(data) + count;
352 if (currentData->mPayload == nullptr)
356 if (currentData->mMessageLength != length)
360 if (memcmp(currentData->mPayload + currentData->mMessageOffset, message, length) != 0)
369 void chip::Transport::TCPTest::CheckProcessReceivedBuffer(nlTestSuite * inSuite, void * inContext)
371 TestContext & ctx = *reinterpret_cast<TestContext *>(inContext);
375 IPAddress::FromString("::1", addr);
377 MockTransportMgrDelegate gMockTransportMgrDelegate(inSuite, ctx);
378 gMockTransportMgrDelegate.InitializeMessageTest(tcp, addr);
380 // Send a packet to get TCP going, so that we can find a TCPEndPoint to pass to ProcessReceivedBuffer.
381 // (The current TCPEndPoint implementation is not effectively mockable.)
382 gMockTransportMgrDelegate.SingleMessageTest(tcp, addr);
384 Transport::PeerAddress lPeerAddress = Transport::PeerAddress::TCP(addr);
385 TCPBase::ActiveConnectionState * state = tcp.FindActiveConnection(lPeerAddress);
386 NL_TEST_ASSERT(inSuite, state != nullptr);
387 Inet::TCPEndPoint * lEndPoint = state->mEndPoint;
388 NL_TEST_ASSERT(inSuite, lEndPoint != nullptr);
390 CHIP_ERROR err = CHIP_NO_ERROR;
391 TestData testData[2];
392 gMockTransportMgrDelegate.SetCallback(TestDataCallbackCheck, testData);
394 // Test a single packet buffer.
395 gMockTransportMgrDelegate.mReceiveHandlerCallCount = 0;
396 NL_TEST_ASSERT(inSuite, testData[0].Init((const uint16_t[]){ 111, 0 }));
397 err = tcp.ProcessReceivedBuffer(lEndPoint, lPeerAddress, std::move(testData[0].mHandle));
398 NL_TEST_ASSERT(inSuite, err == CHIP_NO_ERROR);
399 NL_TEST_ASSERT(inSuite, gMockTransportMgrDelegate.mReceiveHandlerCallCount == 1);
401 // Test a message in a chain of three packet buffers. The message length is split accross buffers.
402 gMockTransportMgrDelegate.mReceiveHandlerCallCount = 0;
403 NL_TEST_ASSERT(inSuite, testData[0].Init((const uint16_t[]){ 1, 122, 123, 0 }));
404 err = tcp.ProcessReceivedBuffer(lEndPoint, lPeerAddress, std::move(testData[0].mHandle));
405 NL_TEST_ASSERT(inSuite, err == CHIP_NO_ERROR);
406 NL_TEST_ASSERT(inSuite, gMockTransportMgrDelegate.mReceiveHandlerCallCount == 1);
408 // Test two messages in a chain.
409 gMockTransportMgrDelegate.mReceiveHandlerCallCount = 0;
410 NL_TEST_ASSERT(inSuite, testData[0].Init((const uint16_t[]){ 131, 0 }));
411 NL_TEST_ASSERT(inSuite, testData[1].Init((const uint16_t[]){ 132, 0 }));
412 testData[0].mHandle->AddToEnd(std::move(testData[1].mHandle));
413 err = tcp.ProcessReceivedBuffer(lEndPoint, lPeerAddress, std::move(testData[0].mHandle));
414 NL_TEST_ASSERT(inSuite, err == CHIP_NO_ERROR);
415 NL_TEST_ASSERT(inSuite, gMockTransportMgrDelegate.mReceiveHandlerCallCount == 2);
417 // Test a chain of two messages, each a chain.
418 gMockTransportMgrDelegate.mReceiveHandlerCallCount = 0;
419 NL_TEST_ASSERT(inSuite, testData[0].Init((const uint16_t[]){ 141, 142, 0 }));
420 NL_TEST_ASSERT(inSuite, testData[1].Init((const uint16_t[]){ 143, 144, 0 }));
421 testData[0].mHandle->AddToEnd(std::move(testData[1].mHandle));
422 err = tcp.ProcessReceivedBuffer(lEndPoint, lPeerAddress, std::move(testData[0].mHandle));
423 NL_TEST_ASSERT(inSuite, err == CHIP_NO_ERROR);
424 NL_TEST_ASSERT(inSuite, gMockTransportMgrDelegate.mReceiveHandlerCallCount == 2);
426 // Test a message that is too large to coalesce into a single packet buffer.
427 gMockTransportMgrDelegate.mReceiveHandlerCallCount = 0;
428 gMockTransportMgrDelegate.SetCallback(TestDataCallbackCheck, &testData[1]);
429 NL_TEST_ASSERT(inSuite, testData[0].Init((const uint16_t[]){ 51, System::PacketBuffer::kMaxSizeWithoutReserve, 0 }));
430 // Sending only the first buffer of the long chain. This should be enough to trigger the error.
431 System::PacketBufferHandle head = testData[0].mHandle.PopHead();
432 err = tcp.ProcessReceivedBuffer(lEndPoint, lPeerAddress, std::move(head));
433 NL_TEST_ASSERT(inSuite, err == CHIP_ERROR_MESSAGE_TOO_LONG);
434 NL_TEST_ASSERT(inSuite, gMockTransportMgrDelegate.mReceiveHandlerCallCount == 0);
436 gMockTransportMgrDelegate.FinalizeMessageTest(tcp, addr);
441 * Test Suite that lists all the test functions.
444 static const nlTest sTests[] =
446 #if INET_CONFIG_ENABLE_IPV4
447 NL_TEST_DEF("Simple Init Test IPV4", CheckSimpleInitTest4),
448 NL_TEST_DEF("Message Self Test IPV4", CheckMessageTest4),
451 NL_TEST_DEF("Simple Init Test IPV6", CheckSimpleInitTest6),
452 NL_TEST_DEF("Message Self Test IPV6", CheckMessageTest6),
453 NL_TEST_DEF("ProcessReceivedBuffer Test", chip::Transport::TCPTest::CheckProcessReceivedBuffer),
460 static nlTestSuite sSuite =
470 * Initialize the test suite.
472 static int Initialize(void * aContext)
474 CHIP_ERROR err = reinterpret_cast<TestContext *>(aContext)->Init(&sSuite);
475 return (err == CHIP_NO_ERROR) ? SUCCESS : FAILURE;
479 * Finalize the test suite.
481 static int Finalize(void * aContext)
483 CHIP_ERROR err = reinterpret_cast<TestContext *>(aContext)->Shutdown();
484 return (err == CHIP_NO_ERROR) ? SUCCESS : FAILURE;
489 // Run test suit against one context
490 nlTestRunner(&sSuite, &sContext);
492 return (nlTestRunnerStats(&sSuite));
495 CHIP_REGISTER_TEST_SUITE(TestTCP);