Merge changes Ic69fc75e,I6373d251 into main
* changes: Add ICMP type/code and udplite/sctp in Nettrace. Make BundleKey a distinct struct from PacketTrace.
This commit is contained in:
@@ -18,6 +18,7 @@
|
|||||||
|
|
||||||
#include "netdbpf/NetworkTraceHandler.h"
|
#include "netdbpf/NetworkTraceHandler.h"
|
||||||
|
|
||||||
|
#include <android-base/macros.h>
|
||||||
#include <arpa/inet.h>
|
#include <arpa/inet.h>
|
||||||
#include <bpf/BpfUtils.h>
|
#include <bpf/BpfUtils.h>
|
||||||
#include <log/log.h>
|
#include <log/log.h>
|
||||||
@@ -75,9 +76,35 @@ struct BundleDetails {
|
|||||||
uint32_t bytes = 0;
|
uint32_t bytes = 0;
|
||||||
};
|
};
|
||||||
|
|
||||||
#define AGG_FIELDS(x) \
|
BundleKey::BundleKey(const PacketTrace& pkt)
|
||||||
(x).ifindex, (x).uid, (x).tag, (x).sport, (x).dport, (x).egress, \
|
: ifindex(pkt.ifindex),
|
||||||
(x).ipProto, (x).tcpFlags
|
uid(pkt.uid),
|
||||||
|
tag(pkt.tag),
|
||||||
|
egress(pkt.egress),
|
||||||
|
ipProto(pkt.ipProto),
|
||||||
|
ipVersion(pkt.ipVersion) {
|
||||||
|
switch (ipProto) {
|
||||||
|
case IPPROTO_TCP:
|
||||||
|
tcpFlags = pkt.tcpFlags;
|
||||||
|
FALLTHROUGH_INTENDED;
|
||||||
|
case IPPROTO_DCCP:
|
||||||
|
case IPPROTO_UDP:
|
||||||
|
case IPPROTO_UDPLITE:
|
||||||
|
case IPPROTO_SCTP:
|
||||||
|
localPort = ntohs(pkt.egress ? pkt.sport : pkt.dport);
|
||||||
|
remotePort = ntohs(pkt.egress ? pkt.dport : pkt.sport);
|
||||||
|
break;
|
||||||
|
case IPPROTO_ICMP:
|
||||||
|
case IPPROTO_ICMPV6:
|
||||||
|
icmpType = ntohs(pkt.sport);
|
||||||
|
icmpCode = ntohs(pkt.dport);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#define AGG_FIELDS(x) \
|
||||||
|
(x).ifindex, (x).uid, (x).tag, (x).egress, (x).ipProto, (x).ipVersion, \
|
||||||
|
(x).tcpFlags, (x).localPort, (x).remotePort, (x).icmpType, (x).icmpCode
|
||||||
|
|
||||||
std::size_t BundleHash::operator()(const BundleKey& a) const {
|
std::size_t BundleHash::operator()(const BundleKey& a) const {
|
||||||
std::size_t seed = 0;
|
std::size_t seed = 0;
|
||||||
@@ -179,7 +206,7 @@ void NetworkTraceHandler::Write(const std::vector<PacketTrace>& packets,
|
|||||||
dst->set_timestamp(pkt.timestampNs);
|
dst->set_timestamp(pkt.timestampNs);
|
||||||
auto* event = dst->set_network_packet();
|
auto* event = dst->set_network_packet();
|
||||||
event->set_length(pkt.length);
|
event->set_length(pkt.length);
|
||||||
Fill(pkt, event);
|
Fill(BundleKey(pkt), event);
|
||||||
}
|
}
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@@ -187,14 +214,13 @@ void NetworkTraceHandler::Write(const std::vector<PacketTrace>& packets,
|
|||||||
uint64_t minTs = std::numeric_limits<uint64_t>::max();
|
uint64_t minTs = std::numeric_limits<uint64_t>::max();
|
||||||
std::unordered_map<BundleKey, BundleDetails, BundleHash, BundleEq> bundles;
|
std::unordered_map<BundleKey, BundleDetails, BundleHash, BundleEq> bundles;
|
||||||
for (const PacketTrace& pkt : packets) {
|
for (const PacketTrace& pkt : packets) {
|
||||||
BundleKey key = pkt;
|
BundleKey key(pkt);
|
||||||
|
|
||||||
// Dropping fields should remove them from the output and remove them from
|
// 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
|
// the aggregation key. Reset the optionals to indicate omission.
|
||||||
// function, set the dropped fields to zero.
|
if (mDropTcpFlags) key.tcpFlags.reset();
|
||||||
if (mDropTcpFlags) key.tcpFlags = 0;
|
if (mDropLocalPort) key.localPort.reset();
|
||||||
if (mDropLocalPort) (key.egress ? key.sport : key.dport) = 0;
|
if (mDropRemotePort) key.remotePort.reset();
|
||||||
if (mDropRemotePort) (key.egress ? key.dport : key.sport) = 0;
|
|
||||||
|
|
||||||
minTs = std::min(minTs, pkt.timestampNs);
|
minTs = std::min(minTs, pkt.timestampNs);
|
||||||
|
|
||||||
@@ -245,22 +271,18 @@ void NetworkTraceHandler::Write(const std::vector<PacketTrace>& packets,
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void NetworkTraceHandler::Fill(const PacketTrace& src,
|
void NetworkTraceHandler::Fill(const BundleKey& src,
|
||||||
NetworkPacketEvent* event) {
|
NetworkPacketEvent* event) {
|
||||||
event->set_direction(src.egress ? TrafficDirection::DIR_EGRESS
|
event->set_direction(src.egress ? TrafficDirection::DIR_EGRESS
|
||||||
: TrafficDirection::DIR_INGRESS);
|
: TrafficDirection::DIR_INGRESS);
|
||||||
event->set_uid(src.uid);
|
event->set_uid(src.uid);
|
||||||
event->set_tag(src.tag);
|
event->set_tag(src.tag);
|
||||||
|
|
||||||
if (!mDropLocalPort) {
|
if (src.tcpFlags.has_value()) event->set_tcp_flags(*src.tcpFlags);
|
||||||
event->set_local_port(ntohs(src.egress ? src.sport : src.dport));
|
if (src.localPort.has_value()) event->set_local_port(*src.localPort);
|
||||||
}
|
if (src.remotePort.has_value()) event->set_remote_port(*src.remotePort);
|
||||||
if (!mDropRemotePort) {
|
if (src.icmpType.has_value()) event->set_icmp_type(*src.icmpType);
|
||||||
event->set_remote_port(ntohs(src.egress ? src.dport : src.sport));
|
if (src.icmpCode.has_value()) event->set_icmp_code(*src.icmpCode);
|
||||||
}
|
|
||||||
if (!mDropTcpFlags) {
|
|
||||||
event->set_tcp_flags(src.tcpFlags);
|
|
||||||
}
|
|
||||||
|
|
||||||
event->set_ip_proto(src.ipProto);
|
event->set_ip_proto(src.ipProto);
|
||||||
|
|
||||||
|
|||||||
@@ -113,7 +113,7 @@ TEST_F(NetworkTraceHandlerTest, WriteBasicFields) {
|
|||||||
.length = 100,
|
.length = 100,
|
||||||
.uid = 10,
|
.uid = 10,
|
||||||
.tag = 123,
|
.tag = 123,
|
||||||
.ipProto = 6,
|
.ipProto = IPPROTO_TCP,
|
||||||
.tcpFlags = 1,
|
.tcpFlags = 1,
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
@@ -138,12 +138,14 @@ TEST_F(NetworkTraceHandlerTest, WriteDirectionAndPorts) {
|
|||||||
.sport = htons(8080),
|
.sport = htons(8080),
|
||||||
.dport = htons(443),
|
.dport = htons(443),
|
||||||
.egress = true,
|
.egress = true,
|
||||||
|
.ipProto = IPPROTO_TCP,
|
||||||
},
|
},
|
||||||
PacketTrace{
|
PacketTrace{
|
||||||
.timestampNs = 2,
|
.timestampNs = 2,
|
||||||
.sport = htons(443),
|
.sport = htons(443),
|
||||||
.dport = htons(8080),
|
.dport = htons(8080),
|
||||||
.egress = false,
|
.egress = false,
|
||||||
|
.ipProto = IPPROTO_TCP,
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -161,6 +163,42 @@ TEST_F(NetworkTraceHandlerTest, WriteDirectionAndPorts) {
|
|||||||
TrafficDirection::DIR_INGRESS);
|
TrafficDirection::DIR_INGRESS);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
TEST_F(NetworkTraceHandlerTest, WriteIcmpTypeAndCode) {
|
||||||
|
std::vector<PacketTrace> input = {
|
||||||
|
PacketTrace{
|
||||||
|
.timestampNs = 1,
|
||||||
|
.sport = htons(11), // type
|
||||||
|
.dport = htons(22), // code
|
||||||
|
.egress = true,
|
||||||
|
.ipProto = IPPROTO_ICMP,
|
||||||
|
},
|
||||||
|
PacketTrace{
|
||||||
|
.timestampNs = 2,
|
||||||
|
.sport = htons(33), // type
|
||||||
|
.dport = htons(44), // code
|
||||||
|
.egress = false,
|
||||||
|
.ipProto = IPPROTO_ICMPV6,
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
std::vector<TracePacket> events;
|
||||||
|
ASSERT_TRUE(TraceAndSortPackets(input, &events));
|
||||||
|
|
||||||
|
ASSERT_EQ(events.size(), 2);
|
||||||
|
EXPECT_FALSE(events[0].network_packet().has_local_port());
|
||||||
|
EXPECT_FALSE(events[0].network_packet().has_remote_port());
|
||||||
|
EXPECT_THAT(events[0].network_packet().icmp_type(), 11);
|
||||||
|
EXPECT_THAT(events[0].network_packet().icmp_code(), 22);
|
||||||
|
EXPECT_THAT(events[0].network_packet().direction(),
|
||||||
|
TrafficDirection::DIR_EGRESS);
|
||||||
|
EXPECT_FALSE(events[1].network_packet().local_port());
|
||||||
|
EXPECT_FALSE(events[1].network_packet().remote_port());
|
||||||
|
EXPECT_THAT(events[1].network_packet().icmp_type(), 33);
|
||||||
|
EXPECT_THAT(events[1].network_packet().icmp_code(), 44);
|
||||||
|
EXPECT_THAT(events[1].network_packet().direction(),
|
||||||
|
TrafficDirection::DIR_INGRESS);
|
||||||
|
}
|
||||||
|
|
||||||
TEST_F(NetworkTraceHandlerTest, BasicBundling) {
|
TEST_F(NetworkTraceHandlerTest, BasicBundling) {
|
||||||
// TODO: remove this once bundling becomes default. Until then, set arbitrary
|
// TODO: remove this once bundling becomes default. Until then, set arbitrary
|
||||||
// aggregation threshold to enable bundling.
|
// aggregation threshold to enable bundling.
|
||||||
@@ -168,12 +206,12 @@ TEST_F(NetworkTraceHandlerTest, BasicBundling) {
|
|||||||
config.set_aggregation_threshold(10);
|
config.set_aggregation_threshold(10);
|
||||||
|
|
||||||
std::vector<PacketTrace> input = {
|
std::vector<PacketTrace> input = {
|
||||||
PacketTrace{.uid = 123, .timestampNs = 2, .length = 200},
|
PacketTrace{.timestampNs = 2, .length = 200, .uid = 123},
|
||||||
PacketTrace{.uid = 123, .timestampNs = 1, .length = 100},
|
PacketTrace{.timestampNs = 1, .length = 100, .uid = 123},
|
||||||
PacketTrace{.uid = 123, .timestampNs = 4, .length = 300},
|
PacketTrace{.timestampNs = 4, .length = 300, .uid = 123},
|
||||||
|
|
||||||
PacketTrace{.uid = 456, .timestampNs = 2, .length = 400},
|
PacketTrace{.timestampNs = 2, .length = 400, .uid = 456},
|
||||||
PacketTrace{.uid = 456, .timestampNs = 4, .length = 100},
|
PacketTrace{.timestampNs = 4, .length = 100, .uid = 456},
|
||||||
};
|
};
|
||||||
|
|
||||||
std::vector<TracePacket> events;
|
std::vector<TracePacket> events;
|
||||||
@@ -203,12 +241,12 @@ TEST_F(NetworkTraceHandlerTest, AggregationThreshold) {
|
|||||||
config.set_aggregation_threshold(3);
|
config.set_aggregation_threshold(3);
|
||||||
|
|
||||||
std::vector<PacketTrace> input = {
|
std::vector<PacketTrace> input = {
|
||||||
PacketTrace{.uid = 123, .timestampNs = 2, .length = 200},
|
PacketTrace{.timestampNs = 2, .length = 200, .uid = 123},
|
||||||
PacketTrace{.uid = 123, .timestampNs = 1, .length = 100},
|
PacketTrace{.timestampNs = 1, .length = 100, .uid = 123},
|
||||||
PacketTrace{.uid = 123, .timestampNs = 4, .length = 300},
|
PacketTrace{.timestampNs = 4, .length = 300, .uid = 123},
|
||||||
|
|
||||||
PacketTrace{.uid = 456, .timestampNs = 2, .length = 400},
|
PacketTrace{.timestampNs = 2, .length = 400, .uid = 456},
|
||||||
PacketTrace{.uid = 456, .timestampNs = 4, .length = 100},
|
PacketTrace{.timestampNs = 4, .length = 100, .uid = 456},
|
||||||
};
|
};
|
||||||
|
|
||||||
std::vector<TracePacket> events;
|
std::vector<TracePacket> events;
|
||||||
@@ -239,12 +277,17 @@ TEST_F(NetworkTraceHandlerTest, DropLocalPort) {
|
|||||||
__be16 b = htons(10001);
|
__be16 b = htons(10001);
|
||||||
std::vector<PacketTrace> input = {
|
std::vector<PacketTrace> input = {
|
||||||
// Recall that local is `src` for egress and `dst` for ingress.
|
// Recall that local is `src` for egress and `dst` for ingress.
|
||||||
PacketTrace{.timestampNs = 1, .length = 2, .egress = true, .sport = a},
|
PacketTrace{.timestampNs = 1, .length = 2, .sport = a, .egress = true},
|
||||||
PacketTrace{.timestampNs = 2, .length = 4, .egress = false, .dport = a},
|
PacketTrace{.timestampNs = 2, .length = 4, .dport = a, .egress = false},
|
||||||
PacketTrace{.timestampNs = 3, .length = 6, .egress = true, .sport = b},
|
PacketTrace{.timestampNs = 3, .length = 6, .sport = b, .egress = true},
|
||||||
PacketTrace{.timestampNs = 4, .length = 8, .egress = false, .dport = b},
|
PacketTrace{.timestampNs = 4, .length = 8, .dport = b, .egress = false},
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// Set common fields.
|
||||||
|
for (PacketTrace& pkt : input) {
|
||||||
|
pkt.ipProto = IPPROTO_TCP;
|
||||||
|
}
|
||||||
|
|
||||||
std::vector<TracePacket> events;
|
std::vector<TracePacket> events;
|
||||||
ASSERT_TRUE(TraceAndSortPackets(input, &events, config));
|
ASSERT_TRUE(TraceAndSortPackets(input, &events, config));
|
||||||
ASSERT_EQ(events.size(), 2);
|
ASSERT_EQ(events.size(), 2);
|
||||||
@@ -274,12 +317,17 @@ TEST_F(NetworkTraceHandlerTest, DropRemotePort) {
|
|||||||
__be16 b = htons(80);
|
__be16 b = htons(80);
|
||||||
std::vector<PacketTrace> input = {
|
std::vector<PacketTrace> input = {
|
||||||
// Recall that remote is `dst` for egress and `src` for ingress.
|
// Recall that remote is `dst` for egress and `src` for ingress.
|
||||||
PacketTrace{.timestampNs = 1, .length = 2, .egress = true, .dport = a},
|
PacketTrace{.timestampNs = 1, .length = 2, .dport = a, .egress = true},
|
||||||
PacketTrace{.timestampNs = 2, .length = 4, .egress = false, .sport = a},
|
PacketTrace{.timestampNs = 2, .length = 4, .sport = a, .egress = false},
|
||||||
PacketTrace{.timestampNs = 3, .length = 6, .egress = true, .dport = b},
|
PacketTrace{.timestampNs = 3, .length = 6, .dport = b, .egress = true},
|
||||||
PacketTrace{.timestampNs = 4, .length = 8, .egress = false, .sport = b},
|
PacketTrace{.timestampNs = 4, .length = 8, .sport = b, .egress = false},
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// Set common fields.
|
||||||
|
for (PacketTrace& pkt : input) {
|
||||||
|
pkt.ipProto = IPPROTO_TCP;
|
||||||
|
}
|
||||||
|
|
||||||
std::vector<TracePacket> events;
|
std::vector<TracePacket> events;
|
||||||
ASSERT_TRUE(TraceAndSortPackets(input, &events, config));
|
ASSERT_TRUE(TraceAndSortPackets(input, &events, config));
|
||||||
ASSERT_EQ(events.size(), 2);
|
ASSERT_EQ(events.size(), 2);
|
||||||
@@ -306,12 +354,17 @@ TEST_F(NetworkTraceHandlerTest, DropTcpFlags) {
|
|||||||
config.set_aggregation_threshold(10);
|
config.set_aggregation_threshold(10);
|
||||||
|
|
||||||
std::vector<PacketTrace> input = {
|
std::vector<PacketTrace> input = {
|
||||||
PacketTrace{.timestampNs = 1, .uid = 123, .length = 1, .tcpFlags = 1},
|
PacketTrace{.timestampNs = 1, .length = 1, .uid = 123, .tcpFlags = 1},
|
||||||
PacketTrace{.timestampNs = 2, .uid = 123, .length = 2, .tcpFlags = 2},
|
PacketTrace{.timestampNs = 2, .length = 2, .uid = 123, .tcpFlags = 2},
|
||||||
PacketTrace{.timestampNs = 3, .uid = 456, .length = 3, .tcpFlags = 1},
|
PacketTrace{.timestampNs = 3, .length = 3, .uid = 456, .tcpFlags = 1},
|
||||||
PacketTrace{.timestampNs = 4, .uid = 456, .length = 4, .tcpFlags = 2},
|
PacketTrace{.timestampNs = 4, .length = 4, .uid = 456, .tcpFlags = 2},
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// Set common fields.
|
||||||
|
for (PacketTrace& pkt : input) {
|
||||||
|
pkt.ipProto = IPPROTO_TCP;
|
||||||
|
}
|
||||||
|
|
||||||
std::vector<TracePacket> events;
|
std::vector<TracePacket> events;
|
||||||
ASSERT_TRUE(TraceAndSortPackets(input, &events, config));
|
ASSERT_TRUE(TraceAndSortPackets(input, &events, config));
|
||||||
|
|
||||||
|
|||||||
@@ -30,15 +30,33 @@
|
|||||||
namespace android {
|
namespace android {
|
||||||
namespace bpf {
|
namespace bpf {
|
||||||
|
|
||||||
// BundleKeys are PacketTraces where timestamp and length are ignored.
|
// BundleKey encodes a PacketTrace minus timestamp and length. The key should
|
||||||
using BundleKey = PacketTrace;
|
// match many packets over time for interning. For convenience, sport/dport
|
||||||
|
// are parsed here as either local/remote port or icmp type/code.
|
||||||
|
struct BundleKey {
|
||||||
|
explicit BundleKey(const PacketTrace& pkt);
|
||||||
|
|
||||||
// BundleKeys are hashed using all fields except timestamp/length.
|
uint32_t ifindex;
|
||||||
|
uint32_t uid;
|
||||||
|
uint32_t tag;
|
||||||
|
|
||||||
|
bool egress;
|
||||||
|
uint8_t ipProto;
|
||||||
|
uint8_t ipVersion;
|
||||||
|
|
||||||
|
std::optional<uint8_t> tcpFlags;
|
||||||
|
std::optional<uint16_t> localPort;
|
||||||
|
std::optional<uint16_t> remotePort;
|
||||||
|
std::optional<uint8_t> icmpType;
|
||||||
|
std::optional<uint8_t> icmpCode;
|
||||||
|
};
|
||||||
|
|
||||||
|
// BundleKeys are hashed using a simple hash combine.
|
||||||
struct BundleHash {
|
struct BundleHash {
|
||||||
std::size_t operator()(const BundleKey& a) const;
|
std::size_t operator()(const BundleKey& a) const;
|
||||||
};
|
};
|
||||||
|
|
||||||
// BundleKeys are equal if all fields except timestamp/length are equal.
|
// BundleKeys are equal if all fields are equal.
|
||||||
struct BundleEq {
|
struct BundleEq {
|
||||||
bool operator()(const BundleKey& a, const BundleKey& b) const;
|
bool operator()(const BundleKey& a, const BundleKey& b) const;
|
||||||
};
|
};
|
||||||
@@ -84,13 +102,13 @@ class NetworkTraceHandler
|
|||||||
NetworkTraceHandler::TraceContext& ctx);
|
NetworkTraceHandler::TraceContext& ctx);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
// Convert a PacketTrace into a Perfetto trace packet.
|
// Fills in contextual information from a bundle without interning.
|
||||||
void Fill(const PacketTrace& src,
|
void Fill(const BundleKey& src,
|
||||||
::perfetto::protos::pbzero::NetworkPacketEvent* event);
|
::perfetto::protos::pbzero::NetworkPacketEvent* event);
|
||||||
|
|
||||||
// Fills in contextual information either inline or via interning.
|
// Fills in contextual information either inline or via interning.
|
||||||
::perfetto::protos::pbzero::NetworkPacketBundle* FillWithInterning(
|
::perfetto::protos::pbzero::NetworkPacketBundle* FillWithInterning(
|
||||||
NetworkTraceState* state, const BundleKey& key,
|
NetworkTraceState* state, const BundleKey& src,
|
||||||
::perfetto::protos::pbzero::TracePacket* dst);
|
::perfetto::protos::pbzero::TracePacket* dst);
|
||||||
|
|
||||||
static internal::NetworkTracePoller sPoller;
|
static internal::NetworkTracePoller sPoller;
|
||||||
|
|||||||
Reference in New Issue
Block a user