Skip to content

Commit 66584ea

Browse files
ROX-28557: Improved networking delta when the config is updated (#2068)
1 parent 697c797 commit 66584ea

File tree

6 files changed

+196
-7
lines changed

6 files changed

+196
-7
lines changed

collector/lib/ConnTracker.cpp

Lines changed: 56 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -71,7 +71,7 @@ void ConnectionTracker::Update(
7171
}
7272
}
7373

74-
IPNet ConnectionTracker::NormalizeAddressNoLock(const Address& address) const {
74+
IPNet ConnectionTracker::NormalizeAddressNoLock(const Address& address, bool enable_external_ips) const {
7575
if (address.IsNull()) {
7676
return {};
7777
}
@@ -96,7 +96,7 @@ IPNet ConnectionTracker::NormalizeAddressNoLock(const Address& address) const {
9696
return network;
9797
}
9898

99-
if (enable_external_ips_) {
99+
if (enable_external_ips) {
100100
return IPNet(address, address.length() * 8);
101101
}
102102

@@ -111,6 +111,58 @@ IPNet ConnectionTracker::NormalizeAddressNoLock(const Address& address) const {
111111
}
112112
}
113113

114+
bool ConnectionTracker::ShouldNormalizeConnection(const Connection* conn) const {
115+
Endpoint local, remote = conn->remote();
116+
IPNet ipnet = NormalizeAddressNoLock(remote.address(), false);
117+
118+
return Address::IsCanonicalExternalIp(ipnet.address());
119+
}
120+
121+
/**
122+
* Closes connections that match a predicate and moves them to a delta
123+
*/
124+
void ConnectionTracker::CloseConnections(ConnMap* old_conn_state, ConnMap* delta_conn, std::function<bool(const Connection*)> predicate) {
125+
for (auto it = old_conn_state->begin(); it != old_conn_state->end();) {
126+
auto& old_conn = *it;
127+
if (predicate(&old_conn.first)) {
128+
if (old_conn.second.IsActive()) {
129+
old_conn.second.SetActive(false);
130+
}
131+
delta_conn->insert(old_conn);
132+
it = old_conn_state->erase(it);
133+
} else {
134+
it++;
135+
}
136+
}
137+
}
138+
139+
/**
140+
* Closes connections that have the 255.255.255.255 external IP address
141+
*/
142+
void ConnectionTracker::CloseNormalizedConnections(ConnMap* old_conn_state, ConnMap* delta_conn) {
143+
CloseConnections(old_conn_state, delta_conn, [](const Connection* conn) {
144+
return Address::IsCanonicalExternalIp(conn->remote().address());
145+
});
146+
}
147+
148+
/**
149+
* Closes unnormalized connections that would be normalized to the canonical external
150+
* IP address if external IPs was enabled
151+
*/
152+
void ConnectionTracker::CloseExternalUnnormalizedConnections(ConnMap* old_conn_state, ConnMap* delta_conn) {
153+
CloseConnections(old_conn_state, delta_conn, [this](const Connection* conn) {
154+
return ShouldNormalizeConnection(conn) && !Address::IsCanonicalExternalIp(conn->remote().address());
155+
});
156+
}
157+
158+
void ConnectionTracker::CloseConnectionsOnRuntimeConfigChange(ConnMap* old_conn_state, ConnMap* delta_conn, bool enableExternalIPs) {
159+
if (enableExternalIPs) {
160+
CloseNormalizedConnections(old_conn_state, delta_conn);
161+
} else {
162+
CloseExternalUnnormalizedConnections(old_conn_state, delta_conn);
163+
}
164+
}
165+
114166
Connection ConnectionTracker::NormalizeConnectionNoLock(const Connection& conn) const {
115167
bool is_server = conn.is_server();
116168
if (conn.l4proto() == L4Proto::UDP) {
@@ -123,11 +175,11 @@ Connection ConnectionTracker::NormalizeConnectionNoLock(const Connection& conn)
123175
if (is_server) {
124176
// If this is the server, only the local port is relevant, while the remote port does not matter.
125177
local = Endpoint(IPNet(Address()), conn.local().port());
126-
remote = Endpoint(NormalizeAddressNoLock(conn.remote().address()), 0);
178+
remote = Endpoint(NormalizeAddressNoLock(conn.remote().address(), enable_external_ips_), 0);
127179
} else {
128180
// If this is the client, the local port and address are not relevant.
129181
local = Endpoint();
130-
remote = Endpoint(NormalizeAddressNoLock(remote.address()), remote.port());
182+
remote = Endpoint(NormalizeAddressNoLock(remote.address(), enable_external_ips_), remote.port());
131183
}
132184

133185
return Connection(conn.container(), local, remote, conn.l4proto(), is_server);

collector/lib/ConnTracker.h

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -100,6 +100,11 @@ class ConnectionTracker {
100100
template <typename T>
101101
static void UpdateOldState(UnorderedMap<T, ConnStatus>* old_state, const UnorderedMap<T, ConnStatus>& new_state, int64_t time_micros, int64_t afterglow_period_micros);
102102

103+
void CloseConnections(ConnMap* old_conn_state, ConnMap* delta_conn, std::function<bool(const Connection*)> predicate);
104+
void CloseNormalizedConnections(ConnMap* old_conn_state, ConnMap* delta_conn);
105+
void CloseExternalUnnormalizedConnections(ConnMap* old_conn_state, ConnMap* delta_conn);
106+
void CloseConnectionsOnRuntimeConfigChange(ConnMap* old_conn_state, ConnMap* delta_conn, bool enableExternalIPs);
107+
103108
// ComputeDelta computes a diff between new_state and old_state
104109
template <typename T>
105110
static void ComputeDeltaAfterglow(const UnorderedMap<T, ConnStatus>& new_state, const UnorderedMap<T, ConnStatus>& old_state, UnorderedMap<T, ConnStatus>& delta, int64_t time_micros, int64_t time_at_last_scrape, int64_t afterglow_period_micros);
@@ -154,11 +159,12 @@ class ConnectionTracker {
154159
// Those counters are updated as new connections are reported by the system.
155160
Stats GetConnectionStats_NewConnectionCounters();
156161

162+
bool ShouldNormalizeConnection(const Connection* conn) const;
163+
157164
private:
158165
// NormalizeConnection transforms a connection into a normalized form.
159166
Connection NormalizeConnectionNoLock(const Connection& conn) const;
160-
161-
IPNet NormalizeAddressNoLock(const Address& address) const;
167+
IPNet NormalizeAddressNoLock(const Address& address, bool enable_external_ips) const;
162168

163169
// Returns true if any connection filters are found.
164170
inline bool HasConnectionFilters() const {

collector/lib/NetworkConnection.h

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -136,6 +136,17 @@ class Address {
136136
}
137137
}
138138

139+
static bool IsCanonicalExternalIp(const Address& addr) {
140+
switch (addr.family_) {
141+
case Family::IPV4:
142+
return addr.data_[0] == 0xffffffffULL;
143+
case Family::IPV6:
144+
return addr.data_[0] == 0xffffffffffffffffULL && addr.data_[1] == 0xffffffffffffffffULL;
145+
default:
146+
return false;
147+
}
148+
}
149+
139150
private:
140151
friend std::ostream& operator<<(std::ostream& os, const Address& addr) {
141152
int af = (addr.family_ == Family::IPV4) ? AF_INET : AF_INET6;

collector/lib/NetworkStatusNotifier.cpp

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -225,6 +225,8 @@ void NetworkStatusNotifier::RunSingle(IDuplexClientWriter<sensor::NetworkConnect
225225
auto next_scrape = std::chrono::system_clock::now();
226226
int64_t time_at_last_scrape = NowMicros();
227227

228+
bool prevEnableExternalIPs = config_.EnableExternalIPs();
229+
228230
while (writer->Sleep(next_scrape)) {
229231
CLOG(DEBUG) << "Starting network status notification";
230232
next_scrape = std::chrono::system_clock::now() + std::chrono::seconds(config_.ScrapeInterval());
@@ -240,12 +242,18 @@ void NetworkStatusNotifier::RunSingle(IDuplexClientWriter<sensor::NetworkConnect
240242
const sensor::NetworkConnectionInfoMessage* msg;
241243
ConnMap new_conn_state, delta_conn;
242244
AdvertisedEndpointMap new_cep_state;
245+
bool enableExternalIPs = config_.EnableExternalIPs();
246+
243247
WITH_TIMER(CollectorStats::net_fetch_state) {
244-
conn_tracker_->EnableExternalIPs(config_.EnableExternalIPs());
248+
conn_tracker_->EnableExternalIPs(enableExternalIPs);
245249

246250
new_conn_state = conn_tracker_->FetchConnState(true, true);
247251
if (config_.EnableAfterglow()) {
248252
ConnectionTracker::ComputeDeltaAfterglow(new_conn_state, old_conn_state, delta_conn, time_micros, time_at_last_scrape, config_.AfterglowPeriod());
253+
if (prevEnableExternalIPs != enableExternalIPs) {
254+
conn_tracker_->CloseConnectionsOnRuntimeConfigChange(&old_conn_state, &delta_conn, enableExternalIPs);
255+
prevEnableExternalIPs = enableExternalIPs;
256+
}
249257
} else {
250258
ConnectionTracker::ComputeDelta(new_conn_state, &old_conn_state);
251259
}

collector/test/ConnTrackerTest.cpp

Lines changed: 98 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1651,6 +1651,104 @@ TEST(ConnTrackerTest, TestConnectionStats) {
16511651
EXPECT_EQ(stats.outbound.public_, 4);
16521652
}
16531653

1654+
TEST(ConnTrackerTest, TestCloseNormalizedConnections) {
1655+
ConnectionTracker tracker;
1656+
1657+
Endpoint a(Address(192, 168, 0, 1), 80);
1658+
Endpoint b(Address(255, 255, 255, 255), 9999);
1659+
1660+
Connection conn("xyz", a, b, L4Proto::TCP, true);
1661+
int64_t connection_time = 990;
1662+
1663+
ConnMap old_state = {{conn, ConnStatus(connection_time, true)}};
1664+
ConnMap delta;
1665+
ConnMap expected_delta = {{conn, ConnStatus(connection_time, false)}};
1666+
1667+
tracker.CloseNormalizedConnections(&old_state, &delta);
1668+
1669+
EXPECT_THAT(old_state, IsEmpty());
1670+
EXPECT_THAT(delta, expected_delta);
1671+
}
1672+
1673+
TEST(CloseNormalizedConnectionsTest, UnnormalizedConnectionsAreKept) {
1674+
ConnectionTracker tracker;
1675+
1676+
Endpoint a(Address(192, 168, 0, 1), 80);
1677+
Endpoint b(Address(192, 168, 1, 10), 9999);
1678+
1679+
Connection conn("xyz", a, b, L4Proto::TCP, true);
1680+
int64_t connection_time = 990;
1681+
1682+
ConnMap old_state = {{conn, ConnStatus(connection_time, true)}};
1683+
ConnMap delta;
1684+
ConnMap expected_old_state = {{conn, ConnStatus(connection_time, true)}};
1685+
1686+
tracker.CloseNormalizedConnections(&old_state, &delta);
1687+
1688+
EXPECT_THAT(old_state, expected_old_state);
1689+
EXPECT_THAT(delta, IsEmpty());
1690+
}
1691+
1692+
TEST(ConnTrackerTest, TestCloseExternalUnnormalizedConnections) {
1693+
ConnectionTracker tracker;
1694+
1695+
Endpoint a(Address(192, 168, 0, 1), 80);
1696+
Endpoint b(Address(11, 168, 1, 10), 9999);
1697+
1698+
Connection conn("xyz", a, b, L4Proto::TCP, true);
1699+
int64_t connection_time = 990;
1700+
1701+
ConnMap old_state = {{conn, ConnStatus(connection_time, true)}};
1702+
ConnMap delta;
1703+
ConnMap expected_delta = {{conn, ConnStatus(connection_time, false)}};
1704+
1705+
tracker.CloseExternalUnnormalizedConnections(&old_state, &delta);
1706+
1707+
EXPECT_THAT(old_state, IsEmpty());
1708+
EXPECT_THAT(delta, expected_delta);
1709+
}
1710+
1711+
TEST(CloseExternalUnnormalizedConnectionsTest, InternalConnectionsAreKept) {
1712+
ConnectionTracker tracker;
1713+
1714+
Endpoint a(Address(192, 168, 0, 1), 80);
1715+
Endpoint b(Address(192, 168, 1, 10), 9999);
1716+
1717+
Connection conn("xyz", a, b, L4Proto::TCP, true);
1718+
int64_t connection_time = 990;
1719+
1720+
ConnMap old_state = {{conn, ConnStatus(connection_time, true)}};
1721+
ConnMap delta;
1722+
ConnMap expected_old_state = {{conn, ConnStatus(connection_time, true)}};
1723+
1724+
tracker.CloseExternalUnnormalizedConnections(&old_state, &delta);
1725+
1726+
EXPECT_THAT(old_state, expected_old_state);
1727+
EXPECT_THAT(delta, IsEmpty());
1728+
}
1729+
1730+
TEST(ConnTrackerTest, TestShouldNormalizeConnection) {
1731+
ConnectionTracker tracker;
1732+
1733+
Endpoint a(Address(192, 168, 0, 1), 80);
1734+
Endpoint b(Address(11, 168, 1, 10), 9999);
1735+
1736+
const Connection conn("xyz", a, b, L4Proto::TCP, true);
1737+
1738+
EXPECT_TRUE(tracker.ShouldNormalizeConnection(&conn));
1739+
}
1740+
1741+
TEST(ConnTrackerTest, TestShouldNormalizeConnectionFalse) {
1742+
ConnectionTracker tracker;
1743+
1744+
Endpoint a(Address(192, 168, 0, 1), 80);
1745+
Endpoint b(Address(192, 168, 1, 10), 9999);
1746+
1747+
const Connection conn("xyz", a, b, L4Proto::TCP, true);
1748+
1749+
EXPECT_FALSE(tracker.ShouldNormalizeConnection(&conn));
1750+
}
1751+
16541752
} // namespace
16551753

16561754
} // namespace collector

collector/test/NetworkConnectionTest.cpp

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -162,6 +162,20 @@ TEST(TestIPNet, Parse) {
162162
EXPECT_FALSE(ip_net);
163163
}
164164

165+
TEST(TestIPNet, TestIsCanonicalExternalIp) {
166+
std::vector<std::pair<Address, bool>> tests = {
167+
{{192, 168, 0, 1}, false},
168+
{{192, 168, 1, 10}, false},
169+
{{255, 255, 255, 255}, true},
170+
{{0xdeadbeefULL, 0xffffffffffffffffULL}, false},
171+
{{0xffffffffffffffffULL, 0xffffffffffffffffULL}, true},
172+
};
173+
174+
for (const auto& [address, expected] : tests) {
175+
EXPECT_EQ(Address::IsCanonicalExternalIp(address), expected);
176+
}
177+
}
178+
165179
} // namespace
166180

167181
} // namespace collector

0 commit comments

Comments
 (0)