Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
28 changes: 28 additions & 0 deletions collector/lib/HostHeuristics.cpp
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
#include "HostHeuristics.h"

#include "Logging.h"
#include "NetworkConnection.h" // for enumerating local addresses

namespace collector {

Expand Down Expand Up @@ -93,11 +94,38 @@ class CPUHeuristic : public Heuristic {
}
};

class NetworkInterfaceHeuristic : public Heuristic {
public:
// Log local network interface addresses and warn if any are public.
void Process(HostInfo& host, const CollectorConfig& config, HostConfig* hconfig) const {
auto interfaces = GetLocalInterfaceAddresses();

for (const auto& iface : interfaces) {
const auto& addr = iface.address();

// Skip loopback addresses
if (addr.IsLocal()) {
continue;
}

CLOG(INFO) << "Local interface address: " << iface;

// Warn if the address is not private (i.e., it is public)
if (addr.IsPublic()) {
CLOG(WARNING)
<< "Interface address " << addr << " is public (not in private IP ranges). "
<< "Network flows will not work unless you set 'ROX_NON_AGGREGATED_NETWORKS'";
}
}
}
};

const std::unique_ptr<Heuristic> g_host_heuristics[] = {
std::unique_ptr<Heuristic>(new CollectionHeuristic),
std::unique_ptr<Heuristic>(new DockerDesktopHeuristic),
std::unique_ptr<Heuristic>(new PowerHeuristic),
std::unique_ptr<Heuristic>(new CPUHeuristic),
std::unique_ptr<Heuristic>(new NetworkInterfaceHeuristic),
};

} // namespace
Expand Down
69 changes: 69 additions & 0 deletions collector/lib/NetworkConnection.cpp
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
#include "NetworkConnection.h"

#include <ifaddrs.h>
#include <netdb.h>

#include <netinet/in.h>
Expand Down Expand Up @@ -135,4 +136,72 @@ std::optional<IPNet> IPNet::parse(const std::string& ipnet_string) {
return IPNet(address.value(), prefix_length, prefix_length != 0);
}

size_t netmask_to_prefix_length(const struct sockaddr* netmask) {
if (!netmask) {
return 0;
}

size_t prefix_length = 0;

if (netmask->sa_family == AF_INET) {
const auto* sin = reinterpret_cast<const struct sockaddr_in*>(netmask);
uint32_t mask = ntohl(sin->sin_addr.s_addr);
for (int i = 31; i >= 0; --i) {
if (mask & (1U << i)) {
prefix_length++;
} else {
break;
}
}
} else if (netmask->sa_family == AF_INET6) {
const auto* sin6 = reinterpret_cast<const struct sockaddr_in6*>(netmask);
const uint8_t* addr_bytes = sin6->sin6_addr.s6_addr;
for (size_t i = 0; i < 16; ++i) {
uint8_t byte = addr_bytes[i];
for (int j = 7; j >= 0; --j) {
if (byte & (1U << j)) {
prefix_length++;
} else {
return prefix_length;
}
}
}
}

return prefix_length;
}

std::vector<IPNet> GetLocalInterfaceAddresses() {
std::vector<IPNet> result;
struct ifaddrs* ifaddr = nullptr;

if (getifaddrs(&ifaddr) == -1) {
return result;
}

for (struct ifaddrs* ifa = ifaddr; ifa != nullptr; ifa = ifa->ifa_next) {
if (!ifa->ifa_addr) {
continue;
}

Address::Family family = get_family(*reinterpret_cast<struct sockaddr_storage*>(ifa->ifa_addr));
if (family == Address::Family::UNKNOWN) {
continue;
}

const uint8_t* raw_address = get_raw_address(*reinterpret_cast<struct sockaddr_storage*>(ifa->ifa_addr));
if (!raw_address) {
continue;
}

Address addr(family, reinterpret_cast<const std::array<uint8_t, Address::kMaxLen>&>(*raw_address));
size_t prefix_length = netmask_to_prefix_length(ifa->ifa_netmask);

result.emplace_back(addr, prefix_length, true);
}

freeifaddrs(ifaddr);
return result;
}

} // namespace collector
8 changes: 8 additions & 0 deletions collector/lib/NetworkConnection.h
Original file line number Diff line number Diff line change
Expand Up @@ -488,4 +488,12 @@ static inline const std::vector<IPNet>& PrivateNetworks() {
return *ret;
}

// GetLocalInterfaceAddresses returns a list of IPNet objects representing
// the local system's network interface addresses with their netmasks.
std::vector<IPNet> GetLocalInterfaceAddresses();

// netmask_to_prefix_length converts a sockaddr netmask to a prefix length (e.g., /24).
// Returns 0 if the netmask is null or has an unsupported address family.
size_t netmask_to_prefix_length(const struct sockaddr* netmask);

} // namespace collector
73 changes: 73 additions & 0 deletions collector/test/NetworkConnectionTest.cpp
Original file line number Diff line number Diff line change
@@ -1,5 +1,9 @@
#include <utility>

#include <arpa/inet.h>
#include <netinet/in.h>
#include <sys/socket.h>

#include "NetworkConnection.h"
#include "Utility.h"
#include "gmock/gmock.h"
Expand Down Expand Up @@ -176,6 +180,75 @@ TEST(TestIPNet, TestIsCanonicalExternalIp) {
}
}

TEST(TestNetmaskToPrefixLength, IPv4Netmasks) {
// Test /0 - 0.0.0.0
struct sockaddr_in netmask_0 = {.sin_family = AF_INET, .sin_addr = {.s_addr = 0x00000000}};
EXPECT_EQ(netmask_to_prefix_length(reinterpret_cast<struct sockaddr*>(&netmask_0)), 0);

// Test /8 - 255.0.0.0
struct sockaddr_in netmask_8 = {.sin_family = AF_INET, .sin_addr = {.s_addr = htonl(0xff000000)}};
EXPECT_EQ(netmask_to_prefix_length(reinterpret_cast<struct sockaddr*>(&netmask_8)), 8);

// Test /12 - 255.240.0.0
struct sockaddr_in netmask_12 = {.sin_family = AF_INET, .sin_addr = {.s_addr = htonl(0xfff00000)}};
EXPECT_EQ(netmask_to_prefix_length(reinterpret_cast<struct sockaddr*>(&netmask_12)), 12);

// Test /16 - 255.255.0.0
struct sockaddr_in netmask_16 = {.sin_family = AF_INET, .sin_addr = {.s_addr = htonl(0xffff0000)}};
EXPECT_EQ(netmask_to_prefix_length(reinterpret_cast<struct sockaddr*>(&netmask_16)), 16);

// Test /24 - 255.255.255.0
struct sockaddr_in netmask_24 = {.sin_family = AF_INET, .sin_addr = {.s_addr = htonl(0xffffff00)}};
EXPECT_EQ(netmask_to_prefix_length(reinterpret_cast<struct sockaddr*>(&netmask_24)), 24);

// Test /28 - 255.255.255.240
struct sockaddr_in netmask_28 = {.sin_family = AF_INET, .sin_addr = {.s_addr = htonl(0xfffffff0)}};
EXPECT_EQ(netmask_to_prefix_length(reinterpret_cast<struct sockaddr*>(&netmask_28)), 28);

// Test /32 - 255.255.255.255
struct sockaddr_in netmask_32 = {.sin_family = AF_INET, .sin_addr = {.s_addr = htonl(0xffffffff)}};
EXPECT_EQ(netmask_to_prefix_length(reinterpret_cast<struct sockaddr*>(&netmask_32)), 32);
}

TEST(TestNetmaskToPrefixLength, IPv6Netmasks) {
// Test /0 - all zeros
struct sockaddr_in6 netmask_0 = {.sin6_family = AF_INET6, .sin6_addr = {{0}}};
EXPECT_EQ(netmask_to_prefix_length(reinterpret_cast<struct sockaddr*>(&netmask_0)), 0);

// Test /8 - ff00::
struct sockaddr_in6 netmask_8 = {.sin6_family = AF_INET6, .sin6_addr = {{0xff, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}}};
EXPECT_EQ(netmask_to_prefix_length(reinterpret_cast<struct sockaddr*>(&netmask_8)), 8);

// Test /48 - ffff:ffff:ffff::
struct sockaddr_in6 netmask_48 = {.sin6_family = AF_INET6, .sin6_addr = {{0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}}};
EXPECT_EQ(netmask_to_prefix_length(reinterpret_cast<struct sockaddr*>(&netmask_48)), 48);

// Test /52 - ffff:ffff:ffff:f000::
struct sockaddr_in6 netmask_52 = {.sin6_family = AF_INET6, .sin6_addr = {{0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xf0, 0, 0, 0, 0, 0, 0, 0, 0, 0}}};
EXPECT_EQ(netmask_to_prefix_length(reinterpret_cast<struct sockaddr*>(&netmask_52)), 52);

// Test /64 - ffff:ffff:ffff:ffff::
struct sockaddr_in6 netmask_64 = {.sin6_family = AF_INET6, .sin6_addr = {{0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0, 0, 0, 0, 0, 0, 0, 0}}};
EXPECT_EQ(netmask_to_prefix_length(reinterpret_cast<struct sockaddr*>(&netmask_64)), 64);

// Test /96 - ffff:ffff:ffff:ffff:ffff:ffff::
struct sockaddr_in6 netmask_96 = {.sin6_family = AF_INET6, .sin6_addr = {{0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0, 0, 0, 0}}};
EXPECT_EQ(netmask_to_prefix_length(reinterpret_cast<struct sockaddr*>(&netmask_96)), 96);

// Test /128 - ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff
struct sockaddr_in6 netmask_128 = {.sin6_family = AF_INET6, .sin6_addr = {{0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff}}};
EXPECT_EQ(netmask_to_prefix_length(reinterpret_cast<struct sockaddr*>(&netmask_128)), 128);
}

TEST(TestNetmaskToPrefixLength, EdgeCases) {
// Null pointer
EXPECT_EQ(netmask_to_prefix_length(nullptr), 0);

// Unsupported address family
struct sockaddr unsupported = {.sa_family = AF_UNIX};
EXPECT_EQ(netmask_to_prefix_length(&unsupported), 0);
}

} // namespace

} // namespace collector
Loading