Add support for dropped fields in NetworkTracing
Dropping fields removes them from the bucketing and from the trace
output (the important part being the bucketing). The most useful is
dropping the local port which is often meaningless and can occasionally
have very high cardinality (leading to less bundling, less aggregation
and more space used by interning).
Bug: 246985031
Test: atest libnetworkstats_test
Change-Id: I2ae3289583fa7f29e60d92c58378f189a564bd81
diff --git a/service-t/native/libs/libnetworkstats/NetworkTraceHandler.cpp b/service-t/native/libs/libnetworkstats/NetworkTraceHandler.cpp
index a9550b5..cd6480c 100644
--- a/service-t/native/libs/libnetworkstats/NetworkTraceHandler.cpp
+++ b/service-t/native/libs/libnetworkstats/NetworkTraceHandler.cpp
@@ -165,7 +165,16 @@
}
std::unordered_map<BundleKey, BundleDetails, BundleHash, BundleEq> bundles;
for (const PacketTrace& pkt : packets) {
- BundleDetails& bundle = bundles[pkt];
+ BundleKey key = pkt;
+
+ // Dropping fields should remove them from the output and remove them from
+ // the aggregation key. In order to do the latter without changing the hash
+ // function, set the dropped fields to zero.
+ if (mDropTcpFlags) key.tcpFlags = 0;
+ if (mDropLocalPort) (key.egress ? key.sport : key.dport) = 0;
+ if (mDropRemotePort) (key.egress ? key.dport : key.sport) = 0;
+
+ BundleDetails& bundle = bundles[key];
bundle.time_and_len.emplace_back(pkt.timestampNs, pkt.length);
bundle.minTs = std::min(bundle.minTs, pkt.timestampNs);
bundle.maxTs = std::max(bundle.maxTs, pkt.timestampNs);
@@ -201,7 +210,6 @@
}
}
-// static class method
void NetworkTraceHandler::Fill(const PacketTrace& src,
NetworkPacketEvent* event) {
event->set_direction(src.egress ? TrafficDirection::DIR_EGRESS
@@ -209,11 +217,17 @@
event->set_uid(src.uid);
event->set_tag(src.tag);
- event->set_local_port(src.egress ? ntohs(src.sport) : ntohs(src.dport));
- event->set_remote_port(src.egress ? ntohs(src.dport) : ntohs(src.sport));
+ if (!mDropLocalPort) {
+ event->set_local_port(ntohs(src.egress ? src.sport : src.dport));
+ }
+ if (!mDropRemotePort) {
+ event->set_remote_port(ntohs(src.egress ? src.dport : src.sport));
+ }
+ if (!mDropTcpFlags) {
+ event->set_tcp_flags(src.tcpFlags);
+ }
event->set_ip_proto(src.ipProto);
- event->set_tcp_flags(src.tcpFlags);
char ifname[IF_NAMESIZE] = {};
if (if_indextoname(src.ifindex, ifname) == ifname) {
diff --git a/service-t/native/libs/libnetworkstats/NetworkTraceHandlerTest.cpp b/service-t/native/libs/libnetworkstats/NetworkTraceHandlerTest.cpp
index 6756fb0..d2c1d38 100644
--- a/service-t/native/libs/libnetworkstats/NetworkTraceHandlerTest.cpp
+++ b/service-t/native/libs/libnetworkstats/NetworkTraceHandlerTest.cpp
@@ -237,5 +237,106 @@
testing::ElementsAre(0, 2));
}
+TEST_F(NetworkTraceHandlerTest, DropLocalPort) {
+ NetworkPacketTraceConfig config;
+ config.set_drop_local_port(true);
+ config.set_aggregation_threshold(10);
+
+ __be16 a = htons(10000);
+ __be16 b = htons(10001);
+ std::vector<PacketTrace> input = {
+ // Recall that local is `src` for egress and `dst` for ingress.
+ PacketTrace{.timestampNs = 1, .length = 2, .egress = true, .sport = a},
+ PacketTrace{.timestampNs = 2, .length = 4, .egress = false, .dport = a},
+ PacketTrace{.timestampNs = 3, .length = 6, .egress = true, .sport = b},
+ PacketTrace{.timestampNs = 4, .length = 8, .egress = false, .dport = b},
+ };
+
+ std::vector<TracePacket> events;
+ ASSERT_TRUE(TraceAndSortPackets(input, &events, config));
+ ASSERT_EQ(events.size(), 2);
+
+ // Despite having different local ports, drop and bundle by remaining fields.
+ EXPECT_EQ(events[0].network_packet_bundle().ctx().direction(),
+ TrafficDirection::DIR_EGRESS);
+ EXPECT_THAT(events[0].network_packet_bundle().packet_lengths(),
+ testing::ElementsAre(2, 6));
+
+ EXPECT_EQ(events[1].network_packet_bundle().ctx().direction(),
+ TrafficDirection::DIR_INGRESS);
+ EXPECT_THAT(events[1].network_packet_bundle().packet_lengths(),
+ testing::ElementsAre(4, 8));
+
+ // Local port shouldn't be in output.
+ EXPECT_FALSE(events[0].network_packet_bundle().ctx().has_local_port());
+ EXPECT_FALSE(events[1].network_packet_bundle().ctx().has_local_port());
+}
+
+TEST_F(NetworkTraceHandlerTest, DropRemotePort) {
+ NetworkPacketTraceConfig config;
+ config.set_drop_remote_port(true);
+ config.set_aggregation_threshold(10);
+
+ __be16 a = htons(443);
+ __be16 b = htons(80);
+ std::vector<PacketTrace> input = {
+ // Recall that remote is `dst` for egress and `src` for ingress.
+ PacketTrace{.timestampNs = 1, .length = 2, .egress = true, .dport = a},
+ PacketTrace{.timestampNs = 2, .length = 4, .egress = false, .sport = a},
+ PacketTrace{.timestampNs = 3, .length = 6, .egress = true, .dport = b},
+ PacketTrace{.timestampNs = 4, .length = 8, .egress = false, .sport = b},
+ };
+
+ std::vector<TracePacket> events;
+ ASSERT_TRUE(TraceAndSortPackets(input, &events, config));
+ ASSERT_EQ(events.size(), 2);
+
+ // Despite having different remote ports, drop and bundle by remaining fields.
+ EXPECT_EQ(events[0].network_packet_bundle().ctx().direction(),
+ TrafficDirection::DIR_EGRESS);
+ EXPECT_THAT(events[0].network_packet_bundle().packet_lengths(),
+ testing::ElementsAre(2, 6));
+
+ EXPECT_EQ(events[1].network_packet_bundle().ctx().direction(),
+ TrafficDirection::DIR_INGRESS);
+ EXPECT_THAT(events[1].network_packet_bundle().packet_lengths(),
+ testing::ElementsAre(4, 8));
+
+ // Remote port shouldn't be in output.
+ EXPECT_FALSE(events[0].network_packet_bundle().ctx().has_remote_port());
+ EXPECT_FALSE(events[1].network_packet_bundle().ctx().has_remote_port());
+}
+
+TEST_F(NetworkTraceHandlerTest, DropTcpFlags) {
+ NetworkPacketTraceConfig config;
+ config.set_drop_tcp_flags(true);
+ config.set_aggregation_threshold(10);
+
+ std::vector<PacketTrace> input = {
+ PacketTrace{.timestampNs = 1, .uid = 123, .length = 1, .tcpFlags = 1},
+ PacketTrace{.timestampNs = 2, .uid = 123, .length = 2, .tcpFlags = 2},
+ PacketTrace{.timestampNs = 3, .uid = 456, .length = 3, .tcpFlags = 1},
+ PacketTrace{.timestampNs = 4, .uid = 456, .length = 4, .tcpFlags = 2},
+ };
+
+ std::vector<TracePacket> events;
+ ASSERT_TRUE(TraceAndSortPackets(input, &events, config));
+
+ ASSERT_EQ(events.size(), 2);
+
+ // Despite having different tcp flags, drop and bundle by remaining fields.
+ EXPECT_EQ(events[0].network_packet_bundle().ctx().uid(), 123);
+ EXPECT_THAT(events[0].network_packet_bundle().packet_lengths(),
+ testing::ElementsAre(1, 2));
+
+ EXPECT_EQ(events[1].network_packet_bundle().ctx().uid(), 456);
+ EXPECT_THAT(events[1].network_packet_bundle().packet_lengths(),
+ testing::ElementsAre(3, 4));
+
+ // Tcp flags shouldn't be in output.
+ EXPECT_FALSE(events[0].network_packet_bundle().ctx().has_tcp_flags());
+ EXPECT_FALSE(events[1].network_packet_bundle().ctx().has_tcp_flags());
+}
+
} // namespace bpf
} // namespace android
diff --git a/service-t/native/libs/libnetworkstats/include/netdbpf/NetworkTraceHandler.h b/service-t/native/libs/libnetworkstats/include/netdbpf/NetworkTraceHandler.h
index 03f8651..e9b194b 100644
--- a/service-t/native/libs/libnetworkstats/include/netdbpf/NetworkTraceHandler.h
+++ b/service-t/native/libs/libnetworkstats/include/netdbpf/NetworkTraceHandler.h
@@ -53,8 +53,8 @@
private:
// Convert a PacketTrace into a Perfetto trace packet.
- static void Fill(const PacketTrace& src,
- ::perfetto::protos::pbzero::NetworkPacketEvent* event);
+ void Fill(const PacketTrace& src,
+ ::perfetto::protos::pbzero::NetworkPacketEvent* event);
static internal::NetworkTracePoller sPoller;
bool mStarted;