core/unittest/ebpf/ConnectionManagerUnittest.cpp (196 lines of code) (raw):

// Copyright 2025 iLogtail Authors // // 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. #include <gtest/gtest.h> #include <chrono> #include <memory> #include <thread> #include "ebpf/plugin/network_observer/Connection.h" #include "ebpf/plugin/network_observer/ConnectionManager.h" #include "unittest/Unittest.h" namespace logtail { namespace ebpf { class ConnectionManagerUnittest : public ::testing::Test { public: void TestBasicOperations(); void TestEventHandling(); void TestTimeoutMechanism(); void TestConcurrentAccess(); void TestMetadataHandling(); void TestProtocolDetection(); void TestResourceManagement(); void TestErrorHandling(); protected: void SetUp() override {} void TearDown() override {} private: std::shared_ptr<ConnectionManager> CreateManager() { return ConnectionManager::Create(200); } ConnId CreateTestConnId(int fd = 1, uint32_t tgid = 1000, uint64_t start = 123456) { return ConnId(fd, tgid, start); } void ValidateTracker(const std::shared_ptr<Connection>& tracker, bool shouldExist, support_role_e expectedRole = support_role_e::IsUnknown) { if (shouldExist) { EXPECT_NE(tracker, nullptr); if (tracker) { EXPECT_EQ(tracker->GetRole(), expectedRole); } } else { EXPECT_EQ(tracker, nullptr); } } }; void ConnectionManagerUnittest::TestBasicOperations() { auto manager = CreateManager(); auto connId = CreateTestConnId(); auto tracker = manager->getOrCreateConnection(connId); ValidateTracker(tracker, true); auto existingTracker = manager->getConnection(connId); EXPECT_EQ(existingTracker, tracker); manager->deleteConnection(connId); auto nullTracker = manager->getConnection(connId); ValidateTracker(nullTracker, false); } void ConnectionManagerUnittest::TestEventHandling() { auto manager = CreateManager(); auto connId = CreateTestConnId(); struct conn_ctrl_event_t connectEvent = {}; connectEvent.conn_id.fd = connId.fd; connectEvent.conn_id.tgid = connId.tgid; connectEvent.conn_id.start = connId.start; connectEvent.type = EventConnect; connectEvent.ts = 1; manager->AcceptNetCtrlEvent(&connectEvent); auto tracker = manager->getConnection(connId); ValidateTracker(tracker, true); struct conn_data_event_t dataEvent = {}; dataEvent.conn_id = connectEvent.conn_id; dataEvent.protocol = support_proto_e::ProtoHTTP; dataEvent.role = support_role_e::IsClient; manager->AcceptNetDataEvent(&dataEvent); ValidateTracker(tracker, true, support_role_e::IsClient); struct conn_stats_event_t statsEvent = {}; statsEvent.conn_id = connectEvent.conn_id; statsEvent.si.family = AF_INET; statsEvent.si.netns = 12345; statsEvent.si.ap.saddr = 0x0100007F; // 127.0.0.1 statsEvent.si.ap.daddr = 0x0101A8C0; // 192.168.1.1 statsEvent.si.ap.sport = htons(8080); statsEvent.si.ap.dport = htons(80); statsEvent.protocol = support_proto_e::ProtoHTTP; statsEvent.role = support_role_e::IsUnknown; statsEvent.ts = 2; manager->AcceptNetStatsEvent(&statsEvent); tracker = manager->getConnection(connId); ValidateTracker(tracker, true, support_role_e::IsClient); struct conn_ctrl_event_t closeEvent = connectEvent; closeEvent.type = EventClose; manager->AcceptNetCtrlEvent(&closeEvent); tracker = manager->getConnection(connId); EXPECT_TRUE(tracker->IsClose()); } void ConnectionManagerUnittest::TestTimeoutMechanism() { auto manager = CreateManager(); auto connId = CreateTestConnId(); auto tracker = manager->getOrCreateConnection(connId); ValidateTracker(tracker, true); struct conn_ctrl_event_t closeEvent = {}; closeEvent.conn_id.fd = connId.fd; closeEvent.conn_id.tgid = connId.tgid; closeEvent.conn_id.start = connId.start; closeEvent.type = EventClose; manager->AcceptNetCtrlEvent(&closeEvent); for (size_t i = 0; i < 12; i++) { manager->Iterations(); } auto nullTracker = manager->getConnection(connId); ValidateTracker(nullTracker, false); } void ConnectionManagerUnittest::TestMetadataHandling() { auto manager = CreateManager(); auto connId = CreateTestConnId(); auto tracker = manager->getOrCreateConnection(connId); struct conn_stats_event_t statsEvent = {}; statsEvent.conn_id.fd = connId.fd; statsEvent.conn_id.tgid = connId.tgid; statsEvent.conn_id.start = connId.start; statsEvent.si.family = AF_INET; statsEvent.si.netns = 12345; statsEvent.si.ap.saddr = 0x0100007F; // 127.0.0.1 statsEvent.si.ap.daddr = 0x0101A8C0; // 192.168.1.1 statsEvent.si.ap.sport = htons(8080); statsEvent.si.ap.dport = htons(80); statsEvent.ts = 1; manager->AcceptNetStatsEvent(&statsEvent); auto updatedTracker = manager->getConnection(connId); EXPECT_EQ(updatedTracker->GetSourceIp(), "127.0.0.1"); EXPECT_EQ(updatedTracker->GetRemoteIp(), "192.168.1.1"); } void ConnectionManagerUnittest::TestProtocolDetection() { auto manager = CreateManager(); auto connId = CreateTestConnId(); auto tracker = manager->getOrCreateConnection(connId); struct conn_stats_event_t statsEvent = {}; statsEvent.conn_id.fd = connId.fd; statsEvent.conn_id.tgid = connId.tgid; statsEvent.conn_id.start = connId.start; statsEvent.protocol = support_proto_e::ProtoHTTP; statsEvent.si.family = AF_INET; statsEvent.si.netns = 12345; statsEvent.si.ap.saddr = 0x0100007F; // 127.0.0.1 statsEvent.si.ap.daddr = 0x0101A8C0; // 192.168.1.1 statsEvent.si.ap.sport = htons(8080); statsEvent.si.ap.dport = htons(80); statsEvent.ts = 1; manager->AcceptNetStatsEvent(&statsEvent); auto& attrs = tracker->GetConnTrackerAttrs(); APSARA_TEST_EQUAL(attrs[kConnTrackerTable.ColIndex(kIp.Name())], "127.0.0.1"); } void ConnectionManagerUnittest::TestResourceManagement() { auto manager = CreateManager(); std::vector<ConnId> connIds; const int connectionCount = 100; for (int i = 0; i < connectionCount; ++i) { auto connId = CreateTestConnId(i); connIds.push_back(connId); auto tracker = manager->getOrCreateConnection(connId); ValidateTracker(tracker, true); } for (int i = 0; i < connectionCount / 2; ++i) { struct conn_ctrl_event_t closeEvent = {}; closeEvent.conn_id.fd = connIds[i].fd; closeEvent.conn_id.tgid = connIds[i].tgid; closeEvent.conn_id.start = connIds[i].start; closeEvent.type = EventClose; manager->AcceptNetCtrlEvent(&closeEvent); } std::this_thread::sleep_for(std::chrono::milliseconds(500)); for (size_t i = 0; i < 12; i++) { manager->Iterations(); } for (int i = 0; i < connectionCount; ++i) { auto tracker = manager->getConnection(connIds[i]); if (i < connectionCount / 2) { ValidateTracker(tracker, false); } else { ValidateTracker(tracker, true); } } } void ConnectionManagerUnittest::TestErrorHandling() { auto manager = CreateManager(); ConnId invalidConnId(-1, 0, 0); auto nullTracker = manager->getConnection(invalidConnId); ValidateTracker(nullTracker, false); auto connId = CreateTestConnId(); manager->deleteConnection(connId); // re-delete manager->deleteConnection(connId); } UNIT_TEST_CASE(ConnectionManagerUnittest, TestBasicOperations); UNIT_TEST_CASE(ConnectionManagerUnittest, TestEventHandling); UNIT_TEST_CASE(ConnectionManagerUnittest, TestTimeoutMechanism); UNIT_TEST_CASE(ConnectionManagerUnittest, TestMetadataHandling); UNIT_TEST_CASE(ConnectionManagerUnittest, TestProtocolDetection); UNIT_TEST_CASE(ConnectionManagerUnittest, TestResourceManagement); UNIT_TEST_CASE(ConnectionManagerUnittest, TestErrorHandling); } // namespace ebpf } // namespace logtail UNIT_TEST_MAIN