Skip to content

Commit

Permalink
Add external ip connections counters for each service
Browse files Browse the repository at this point in the history
  • Loading branch information
lnarolski committed Nov 12, 2024
1 parent 17487fd commit d8fa245
Show file tree
Hide file tree
Showing 15 changed files with 622 additions and 68 deletions.
19 changes: 10 additions & 9 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -54,15 +54,16 @@ Please note that in order to run eBPF Discovery on a system it needs to have eBP

Optional command line arguments can be set in place of the OPTIONS tag:

|Option |Description |Default value |
|---------------------|---------------------------------------------------------------------------------------------------------------|------------------------------------|
|`--help, -h` |Display available options. |false |
|`--interval=VALUE` |Set the time inteval (in seconds) in which the discovered services are reported to the programs standard output|60 (seconds). |
|`--log-dir=DIRECTORY`|Set log files directory. |eBPF Discovery binary root directory|
|`--log-level=LEVEL` |Set logging level, where LEVEL={trace, debug, info, warning, error, critical, off}. |error |
|`--log-no-stdout` |Disable logging to stdout. |false |
|`--test-launch` |Exit program after launch for testing purposes. |false |
|`--version` |Display program version. |false |
| Option | Description | Default value |
|-----------------------------|-----------------------------------------------------------------------------------------------------------------|--------------------------------------|
| `--help, -h` | Display available options. | false |
| `--interval=VALUE` | Set the time inteval (in seconds) in which the discovered services are reported to the programs standard output | 60 (seconds). |
| `--log-dir=DIRECTORY` | Set log files directory. | eBPF Discovery binary root directory |
| `--log-level=LEVEL` | Set logging level, where LEVEL={trace, debug, info, warning, error, critical, off}. | error |
| `--log-no-stdout` | Disable logging to stdout. | false |
| `--test-launch` | Exit program after launch for testing purposes. | false |
| `--version` | Display program version. | false |
| `--enable-network-counters` | Enable network counters. | false |


## Help & Support
Expand Down
16 changes: 15 additions & 1 deletion ebpfdiscoverysrv/src/main.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,7 @@ static po::options_description getProgramOptions() {
("log-no-stdout", po::bool_switch()->default_value(false), "Disable logging to stdout")
("version", "Display program version")
("interval", po::value<int>()->default_value(60), "Services reporting time interval (in seconds)")
("enable-network-counters", po::bool_switch()->default_value(false), "Enable network counters")
;
// clang-format on

Expand Down Expand Up @@ -164,8 +165,10 @@ int main(int argc, char** argv) {
return EXIT_FAILURE;
}

const bool enableNetworkCounters{vm["enable-network-counters"].as<bool>()};

const auto bpfFds{discoveryBpf.getFds()};
ebpfdiscovery::Discovery instance(bpfFds);
ebpfdiscovery::Discovery instance(bpfFds, enableNetworkCounters);
try {
instance.init();
} catch (const std::runtime_error& e) {
Expand Down Expand Up @@ -196,6 +199,14 @@ int main(int argc, char** argv) {
}
});

std::future<void> networkCountersCleaningFuture{};
if (enableNetworkCounters) {
auto networkCountersCleaningInterval{std::chrono::minutes(1)};
networkCountersCleaningFuture = std::async(std::launch::async, periodicTask, networkCountersCleaningInterval, [&instance]() {
instance.networkCountersCleaning();
});
}

auto outputServicesToStdoutInterval{std::chrono::seconds(vm["interval"].as<int>())};
auto outputServicesToStdoutFuture = std::async(std::launch::async, periodicTask, outputServicesToStdoutInterval, [&](){ instance.outputServicesToStdout(); });

Expand All @@ -211,6 +222,9 @@ int main(int argc, char** argv) {
if(featchAndHandleEventsFuture.valid()) {
featchAndHandleEventsFuture.wait();
}
if(!enableNetworkCounters && networkCountersCleaningFuture.valid()) {
networkCountersCleaningFuture.wait();
}

programRunningFlag = false;

Expand Down
6 changes: 4 additions & 2 deletions libebpfdiscovery/headers/ebpfdiscovery/Discovery.h
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,7 @@ using httpparser::HttpRequestParser;

class Discovery {
public:
Discovery(const DiscoveryBpfFds& bpfFds);
Discovery(const DiscoveryBpfFds& bpfFds, const bool _enableNetworkCounters);
Discovery(const Discovery&) = delete;
Discovery& operator=(const Discovery&) = delete;

Expand All @@ -48,6 +48,8 @@ class Discovery {
int fetchAndHandleEvents();
void outputServicesToStdout();

void networkCountersCleaning();

private:
using SavedSessionsCacheType = LRUCache<DiscoverySavedSessionKey, Session, DiscoverySavedSessionKeyHash>;

Expand All @@ -73,7 +75,7 @@ class Discovery {
SavedSessionsCacheType savedSessions;
service::NetlinkCalls netlinkCalls;
service::IpAddressNetlinkChecker ipChecker{netlinkCalls};
service::Aggregator serviceAggregator{ipChecker};
service::Aggregator serviceAggregator;
};

} // namespace ebpfdiscovery
19 changes: 15 additions & 4 deletions libebpfdiscovery/src/Discovery.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,7 @@

namespace ebpfdiscovery {

Discovery::Discovery(const DiscoveryBpfFds& bpfFds) : bpfFds{bpfFds}, savedSessions{DISCOVERY_MAX_SESSIONS} {
Discovery::Discovery(const DiscoveryBpfFds& bpfFds, const bool _enableNetworkCounters) : bpfFds{bpfFds}, savedSessions{DISCOVERY_MAX_SESSIONS}, serviceAggregator{ipChecker, _enableNetworkCounters} {
}

void Discovery::init() {
Expand All @@ -62,9 +62,17 @@ void Discovery::outputServicesToStdout() {
return;
}

const auto servicesProto{proto::internalToProto(services)};
const auto servicesJson{proto::protoToJson(servicesProto)};
std::cout << servicesJson << std::endl;
ServicesList servicesProto{};
bool isListEmpty = false;
{
std::lock_guard<std::mutex> lock(serviceAggregator.getServicesMutex());
std::tie(servicesProto, isListEmpty) = proto::internalToProto(services, serviceAggregator.getEnableNetworkCounters());
}
if (!isListEmpty) {
const auto servicesJson{proto::protoToJson(servicesProto)};
std::cout << servicesJson << std::endl;
}

serviceAggregator.clear();
}

Expand Down Expand Up @@ -216,5 +224,8 @@ void Discovery::saveSession(const DiscoverySavedSessionKey& sessionKey, const Se
int Discovery::bpfDiscoveryDeleteSession(const DiscoveryTrackedSessionKey& trackedSessionKey) {
return bpf_map_delete_elem(bpfFds.trackedSessionsMap, &trackedSessionKey);
}
void Discovery::networkCountersCleaning() {
serviceAggregator.networkCountersCleaning();
}

} // namespace ebpfdiscovery
3 changes: 3 additions & 0 deletions libebpfdiscoveryproto/ebpfdiscoveryproto/service.proto
Original file line number Diff line number Diff line change
Expand Up @@ -27,4 +27,7 @@ message Service {
string scheme = 4;
uint32 internalClientsNumber = 5;
uint32 externalClientsNumber = 6;
uint32 externalIPv4_16ClientNets = 7;
uint32 externalIPv4_24ClientNets = 8;
uint32 externalIPv6ClientsNets = 9;
}
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@

namespace proto {

ServicesList internalToProto(const std::vector<std::reference_wrapper<service::Service>>& services);
std::pair<ServicesList, bool> internalToProto(const std::vector<std::reference_wrapper<service::Service>>& services, const bool enableNetworkCounters);

std::string protoToJson(const ServicesList& protoServices);

Expand Down
19 changes: 16 additions & 3 deletions libebpfdiscoveryproto/src/Translator.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -20,20 +20,33 @@

namespace proto {

ServicesList internalToProto(const std::vector<std::reference_wrapper<service::Service>>& services) {
std::pair<ServicesList, bool> internalToProto(const std::vector<std::reference_wrapper<service::Service>>& services, const bool enableNetworkCounters) {
ServicesList protoServicesList;
bool isListEmpty = true;
for (const auto& serviceRef : services) {
const auto protoService{protoServicesList.add_service()};
const auto& service{serviceRef.get()};

if (service.internalClientsNumber == 0 && service.externalClientsNumber == 0) {
continue;
}

const auto protoService{protoServicesList.add_service()};

protoService->set_pid(service.pid);
protoService->set_endpoint(service.endpoint);
protoService->set_domain(service.domain);
protoService->set_scheme(service.scheme);
protoService->set_internalclientsnumber(service.internalClientsNumber);
protoService->set_externalclientsnumber(service.externalClientsNumber);
if (enableNetworkCounters) {
protoService->set_externalipv4_16clientnets(service.detectedExternalIPv416Networks.size());
protoService->set_externalipv4_24clientnets(service.detectedExternalIPv424Networks.size());
protoService->set_externalipv6clientsnets(service.detectedExternalIPv6Networks.size());
}

isListEmpty = false;
}
return protoServicesList;
return {protoServicesList, isListEmpty};
}

std::string protoToJson(const ServicesList& protoServices) {
Expand Down
46 changes: 44 additions & 2 deletions libebpfdiscoveryproto/test/TranslatorTest.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -38,8 +38,9 @@ TEST_F(ProtobufTranslatorTest, successfulTranslationToJson) {
internalServices.push_back(service4);
internalServices.push_back(service5);

const auto proto{internalToProto(internalServices)};
const auto result{protoToJson(proto)};
const auto proto{internalToProto(internalServices, false)};
ASSERT_FALSE(proto.second);
const auto result{protoToJson(proto.first)};
const std::string expected{"{\"service\":[{\"pid\":1,\"endpoint\":\"/endpoint/"
"1\",\"internalClientsNumber\":1,\"externalClientsNumber\":2},{\"pid\":2,\"endpoint\":\"/endpoint/"
"1\",\"internalClientsNumber\":1,\"externalClientsNumber\":2},{\"pid\":3,\"endpoint\":\"/endpoint/"
Expand All @@ -48,3 +49,44 @@ TEST_F(ProtobufTranslatorTest, successfulTranslationToJson) {
"endpoint/4\",\"domain\":\"dynatrace.com\",\"scheme\":\"https\",\"internalClientsNumber\":1,\"externalClientsNumber\":2}]}"};
EXPECT_EQ(result, expected);
}

TEST_F(ProtobufTranslatorTest, successfulTranslationToJsonNetworkCounters) {
std::unordered_map<std::array<uint8_t, 2>, std::chrono::time_point<std::chrono::steady_clock>, service::ArrayHasher> detectedExternalIPv416Networks = {
{{0xAC, 0x8F}, std::chrono::steady_clock::now()},
{{0xAC, 0xC7}, std::chrono::steady_clock::now()}
};
std::unordered_map<std::array<uint8_t, 3>, std::chrono::time_point<std::chrono::steady_clock>, service::ArrayHasher> detectedExternalIPv424Networks = {
{{0xAC, 0x8F, 0x04}, std::chrono::steady_clock::now()},
{{0xAC, 0x8F, 0x06}, std::chrono::steady_clock::now()},
{{0xAC, 0xC7, 0x2D}, std::chrono::steady_clock::now()}
};
std::unordered_map<std::array<uint8_t, 10>, std::chrono::time_point<std::chrono::steady_clock>, service::ArrayHasher> detectedExternalIPv6Networks = {
{{0x20, 0x01, 0x48, 0x60, 0x48, 0x60, 0x00, 0x00, 0x00, 0x00}, std::chrono::steady_clock::now()},
{{0x12, 0x34, 0x23, 0x45, 0x34, 0x56, 0x45, 0x67, 0x56, 0x78}, std::chrono::steady_clock::now()}
};

std::vector<std::reference_wrapper<service::Service>> internalServices;
service::Service service1{.pid = 1, .endpoint = "/endpoint/1", .internalClientsNumber = 1, .externalClientsNumber = 3, .detectedExternalIPv416Networks = detectedExternalIPv416Networks, .detectedExternalIPv424Networks = detectedExternalIPv424Networks};
service::Service service2{.pid = 2, .endpoint = "/endpoint/1", .internalClientsNumber = 1, .externalClientsNumber = 3, .detectedExternalIPv416Networks = detectedExternalIPv416Networks, .detectedExternalIPv424Networks = detectedExternalIPv424Networks};
service::Service service3{.pid = 3, .endpoint = "/endpoint/2", .internalClientsNumber = 1, .externalClientsNumber = 3, .detectedExternalIPv416Networks = detectedExternalIPv416Networks, .detectedExternalIPv424Networks = detectedExternalIPv424Networks};

service::Service service4{.pid = 4, .endpoint = "google.com/endpoint/3", .domain = "google.com", .scheme = "http", .internalClientsNumber = 1, .externalClientsNumber = 2, .detectedExternalIPv6Networks = detectedExternalIPv6Networks};
service::Service service5{.pid = 5, .endpoint = "dynatrace.com/endpoint/4", .domain = "dynatrace.com", .scheme = "https", .internalClientsNumber = 1, .externalClientsNumber = 2, .detectedExternalIPv6Networks = detectedExternalIPv6Networks};

internalServices.push_back(service1);
internalServices.push_back(service2);
internalServices.push_back(service3);
internalServices.push_back(service4);
internalServices.push_back(service5);

const auto proto{internalToProto(internalServices, true)};
ASSERT_FALSE(proto.second);
const auto result{protoToJson(proto.first)};
const std::string expected{"{\"service\":[{\"pid\":1,\"endpoint\":\"/endpoint/"
"1\",\"internalClientsNumber\":1,\"externalClientsNumber\":3,\"externalIPv4_16ClientNets\":2,\"externalIPv4_24ClientNets\":3},{\"pid\":2,\"endpoint\":\"/endpoint/"
"1\",\"internalClientsNumber\":1,\"externalClientsNumber\":3,\"externalIPv4_16ClientNets\":2,\"externalIPv4_24ClientNets\":3},{\"pid\":3,\"endpoint\":\"/endpoint/"
"2\",\"internalClientsNumber\":1,\"externalClientsNumber\":3,\"externalIPv4_16ClientNets\":2,\"externalIPv4_24ClientNets\":3},{\"pid\":4,\"endpoint\":\"google.com/"
"endpoint/3\",\"domain\":\"google.com\",\"scheme\":\"http\",\"internalClientsNumber\":1,\"externalClientsNumber\":2,\"externalIPv6ClientsNets\":2},{\"pid\":5,\"endpoint\":\"dynatrace.com/"
"endpoint/4\",\"domain\":\"dynatrace.com\",\"scheme\":\"https\",\"internalClientsNumber\":1,\"externalClientsNumber\":2,\"externalIPv6ClientsNets\":2}]}"};
EXPECT_EQ(result, expected);
}
10 changes: 9 additions & 1 deletion libservice/headers/service/Aggregator.h
Original file line number Diff line number Diff line change
Expand Up @@ -51,14 +51,22 @@ class Aggregator {
using ServiceStorage = std::unordered_map<ServiceKey, Service>;

public:
Aggregator(const service::IpAddressChecker& ipChecker);
Aggregator(const service::IpAddressChecker& ipChecker, bool _enableNetworkCounters);

void clear();
void newRequest(const httpparser::HttpRequest& request, const DiscoverySessionMeta& meta);
std::vector<std::reference_wrapper<Service>> collectServices();
void networkCountersCleaning();
std::mutex& getServicesMutex();
bool getEnableNetworkCounters() const;

protected:
virtual std::chrono::time_point<std::chrono::steady_clock> getCurrentTime() const;

private:
const IpAddressChecker& ipChecker;

std::mutex servicesMutex{};
ServiceStorage services;
};

Expand Down
22 changes: 21 additions & 1 deletion libservice/headers/service/Service.h
Original file line number Diff line number Diff line change
Expand Up @@ -16,11 +16,26 @@

#pragma once

#include <chrono>
#include <cstdint>
#include <iostream>
#include <string>
#include <unordered_map>
#include <vector>

namespace service {

struct ArrayHasher {
template<size_t N>
std::size_t operator()(const std::array<uint8_t, N>& array) const {
std::size_t hash = 0;
for (auto element : array) {
hash ^= std::hash<int>{}(element) + 0x9e3779b9 + (hash << 6) + (hash >> 2);
}
return hash;
}
};

struct Service {
uint32_t pid;
std::string endpoint;
Expand All @@ -29,9 +44,14 @@ struct Service {
uint32_t internalClientsNumber{0u};
uint32_t externalClientsNumber{0u};

std::unordered_map<std::array<uint8_t, 2>, std::chrono::time_point<std::chrono::steady_clock>, ArrayHasher> detectedExternalIPv416Networks;
std::unordered_map<std::array<uint8_t, 3>, std::chrono::time_point<std::chrono::steady_clock>, ArrayHasher> detectedExternalIPv424Networks;
std::unordered_map<std::array<uint8_t, 10>, std::chrono::time_point<std::chrono::steady_clock>, ArrayHasher> detectedExternalIPv6Networks;

bool operator==(const Service& other) const {
return pid == other.pid && endpoint == other.endpoint && domain == other.domain && scheme == other.scheme && internalClientsNumber == other.internalClientsNumber &&
externalClientsNumber == other.externalClientsNumber;
externalClientsNumber == other.externalClientsNumber && detectedExternalIPv416Networks == other.detectedExternalIPv416Networks &&
detectedExternalIPv424Networks == other.detectedExternalIPv424Networks && detectedExternalIPv6Networks == other.detectedExternalIPv6Networks;
}
};

Expand Down
Loading

0 comments on commit d8fa245

Please sign in to comment.