Skip to content

Commit 0ec2c3a

Browse files
authored
[Add Local Addr & Port 1/3] Capture local addr from socket for accept syscalls (#1808)
Summary: Capture local address from socket if present for accept syscalls. This will support standalone pem entity relationships. Note that for syscalls without socket information (e.g. `connect`), we are currently unable to trace the local IP from bpf. For cases where we fail to trace connect/accept calls, we do try to parse the local address from socket information via `InferConnInfo`. Type of change: /kind feature Test Plan: Ran standalone pem on dev cluster and tracked local address inference from bpf. Ran PxL script for http events. --------- Signed-off-by: Benjamin Kilimnik <bkilimnik@pixielabs.ai>
1 parent ecb5353 commit 0ec2c3a

File tree

11 files changed

+110
-72
lines changed

11 files changed

+110
-72
lines changed

src/stirling/source_connectors/socket_tracer/bcc_bpf/socket_trace.c

Lines changed: 26 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -142,7 +142,8 @@ static __inline void init_conn_info(uint32_t tgid, int32_t fd, struct conn_info_
142142
init_conn_id(tgid, fd, &conn_info->conn_id);
143143
// NOTE: BCC code defaults to 0, because kRoleUnknown is not 0, must explicitly initialize.
144144
conn_info->role = kRoleUnknown;
145-
conn_info->addr.sa.sa_family = PX_AF_UNKNOWN;
145+
conn_info->laddr.sa.sa_family = PX_AF_UNKNOWN;
146+
conn_info->raddr.sa.sa_family = PX_AF_UNKNOWN;
146147
}
147148

148149
// Be careful calling this function. The automatic creation of BPF map entries can result in a
@@ -227,7 +228,8 @@ static __inline struct conn_stats_event_t* fill_conn_stats_event(
227228
}
228229

229230
event->conn_id = conn_info->conn_id;
230-
event->addr = conn_info->addr;
231+
event->laddr = conn_info->laddr;
232+
event->raddr = conn_info->raddr;
231233
event->role = conn_info->role;
232234
event->wr_bytes = conn_info->wr_bytes;
233235
event->rd_bytes = conn_info->rd_bytes;
@@ -251,7 +253,7 @@ static __inline bool should_trace_conn(struct conn_info_t* conn_info) {
251253
// we only send connections on INET or UNKNOWN to user-space.
252254
// Also, it's very important to send the UNKNOWN cases to user-space,
253255
// otherwise we may have a BPF map leak from the earlier call to get_or_create_conn_info().
254-
return should_trace_sockaddr_family(conn_info->addr.sa.sa_family);
256+
return should_trace_sockaddr_family(conn_info->raddr.sa.sa_family);
255257
}
256258

257259
// If this returns false, we still will trace summary stats.
@@ -351,19 +353,26 @@ static __inline void read_sockaddr_kernel(struct conn_info_t* conn_info,
351353

352354
struct sock_common* sk_common = &sk->__sk_common;
353355
uint16_t family = -1;
354-
uint16_t port = -1;
356+
uint16_t lport = -1;
357+
uint16_t rport = -1;
355358

356359
BPF_PROBE_READ_KERNEL_VAR(family, &sk_common->skc_family);
357-
BPF_PROBE_READ_KERNEL_VAR(port, &sk_common->skc_dport);
360+
BPF_PROBE_READ_KERNEL_VAR(lport, &sk_common->skc_num);
361+
BPF_PROBE_READ_KERNEL_VAR(rport, &sk_common->skc_dport);
358362

359-
conn_info->addr.sa.sa_family = family;
363+
conn_info->laddr.sa.sa_family = family;
364+
conn_info->raddr.sa.sa_family = family;
360365

361366
if (family == AF_INET) {
362-
conn_info->addr.in4.sin_port = port;
363-
BPF_PROBE_READ_KERNEL_VAR(conn_info->addr.in4.sin_addr.s_addr, &sk_common->skc_daddr);
367+
conn_info->laddr.in4.sin_port = lport;
368+
conn_info->raddr.in4.sin_port = rport;
369+
BPF_PROBE_READ_KERNEL_VAR(conn_info->laddr.in4.sin_addr.s_addr, &sk_common->skc_rcv_saddr);
370+
BPF_PROBE_READ_KERNEL_VAR(conn_info->raddr.in4.sin_addr.s_addr, &sk_common->skc_daddr);
364371
} else if (family == AF_INET6) {
365-
conn_info->addr.in6.sin6_port = port;
366-
BPF_PROBE_READ_KERNEL_VAR(conn_info->addr.in6.sin6_addr, &sk_common->skc_v6_daddr);
372+
conn_info->laddr.in6.sin6_port = lport;
373+
conn_info->raddr.in6.sin6_port = rport;
374+
BPF_PROBE_READ_KERNEL_VAR(conn_info->laddr.in6.sin6_addr, &sk_common->skc_v6_rcv_saddr);
375+
BPF_PROBE_READ_KERNEL_VAR(conn_info->raddr.in6.sin6_addr, &sk_common->skc_v6_daddr);
367376
}
368377
}
369378

@@ -372,10 +381,10 @@ static __inline void submit_new_conn(struct pt_regs* ctx, uint32_t tgid, int32_t
372381
enum endpoint_role_t role, enum source_function_t source_fn) {
373382
struct conn_info_t conn_info = {};
374383
init_conn_info(tgid, fd, &conn_info);
375-
if (addr != NULL) {
376-
conn_info.addr = *((union sockaddr_t*)addr);
377-
} else if (socket != NULL) {
384+
if (socket != NULL) {
378385
read_sockaddr_kernel(&conn_info, socket);
386+
} else if (addr != NULL) {
387+
conn_info.raddr = *((union sockaddr_t*)addr);
379388
}
380389
conn_info.role = role;
381390

@@ -385,7 +394,7 @@ static __inline void submit_new_conn(struct pt_regs* ctx, uint32_t tgid, int32_t
385394
// While we keep all sa_family types in conn_info_map,
386395
// we only send connections with supported protocols to user-space.
387396
// We use the same filter function to avoid sending data of unwanted connections as well.
388-
if (!should_trace_sockaddr_family(conn_info.addr.sa.sa_family)) {
397+
if (!should_trace_sockaddr_family(conn_info.raddr.sa.sa_family)) {
389398
return;
390399
}
391400

@@ -394,7 +403,8 @@ static __inline void submit_new_conn(struct pt_regs* ctx, uint32_t tgid, int32_t
394403
control_event.timestamp_ns = bpf_ktime_get_ns();
395404
control_event.conn_id = conn_info.conn_id;
396405
control_event.source_fn = source_fn;
397-
control_event.open.addr = conn_info.addr;
406+
control_event.open.raddr = conn_info.raddr;
407+
control_event.open.laddr = conn_info.laddr;
398408
control_event.open.role = conn_info.role;
399409

400410
socket_control_events.perf_submit(ctx, &control_event, sizeof(struct socket_control_event_t));
@@ -923,7 +933,7 @@ static __inline void process_syscall_close(struct pt_regs* ctx, uint64_t id,
923933

924934
// Only submit event to user-space if there was a corresponding open or data event reported.
925935
// This is to avoid polluting the perf buffer.
926-
if (should_trace_sockaddr_family(conn_info->addr.sa.sa_family) || conn_info->wr_bytes != 0 ||
936+
if (should_trace_sockaddr_family(conn_info->raddr.sa.sa_family) || conn_info->wr_bytes != 0 ||
927937
conn_info->rd_bytes != 0) {
928938
submit_close_event(ctx, conn_info, kSyscallClose);
929939

src/stirling/source_connectors/socket_tracer/bcc_bpf_intf/socket_trace.h

Lines changed: 8 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -47,8 +47,10 @@ struct conn_info_t {
4747
// Connection identifier (PID, FD, etc.).
4848
struct conn_id_t conn_id;
4949

50+
// IP address of the local endpoint.
51+
union sockaddr_t laddr;
5052
// IP address of the remote endpoint.
51-
union sockaddr_t addr;
53+
union sockaddr_t raddr;
5254

5355
// The protocol of traffic on the connection (HTTP, MySQL, etc.).
5456
enum traffic_protocol_t protocol;
@@ -92,7 +94,8 @@ struct conn_info_t {
9294
// This struct is a subset of conn_info_t. It is used to communicate connect/accept events.
9395
// See conn_info_t for descriptions of the members.
9496
struct conn_event_t {
95-
union sockaddr_t addr;
97+
union sockaddr_t laddr;
98+
union sockaddr_t raddr;
9699
enum endpoint_role_t role;
97100
};
98101

@@ -222,8 +225,10 @@ struct conn_stats_event_t {
222225

223226
struct conn_id_t conn_id;
224227

228+
// IP address of the local endpoint.
229+
union sockaddr_t laddr;
225230
// IP address of the remote endpoint.
226-
union sockaddr_t addr;
231+
union sockaddr_t raddr;
227232

228233
// The server-client role.
229234
enum endpoint_role_t role;

src/stirling/source_connectors/socket_tracer/bcc_bpf_intf/socket_trace.hpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -49,7 +49,7 @@ inline std::string ToString(const close_event_t& event) {
4949

5050
inline std::string ToString(const conn_event_t& event) {
5151
return absl::Substitute("[addr=$0]",
52-
::px::ToString(reinterpret_cast<const struct sockaddr*>(&event.addr)));
52+
::px::ToString(reinterpret_cast<const struct sockaddr*>(&event.raddr)));
5353
}
5454

5555
inline std::string ToString(const socket_control_event_t& event) {

src/stirling/source_connectors/socket_tracer/conn_stats_test.cc

Lines changed: 25 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -96,9 +96,9 @@ TEST_F(ConnStatsTest, Basic) {
9696
conn_stats_event.timestamp_ns = 0;
9797
conn_stats_event.conn_id = kConnID0;
9898
conn_stats_event.role = kRoleClient;
99-
conn_stats_event.addr.in4.sin_family = AF_INET;
100-
conn_stats_event.addr.in4.sin_port = htons(80);
101-
conn_stats_event.addr.in4.sin_addr.s_addr = 0x01010101; // 1.1.1.1
99+
conn_stats_event.raddr.in4.sin_family = AF_INET;
100+
conn_stats_event.raddr.in4.sin_port = htons(80);
101+
conn_stats_event.raddr.in4.sin_addr.s_addr = 0x01010101; // 1.1.1.1
102102
conn_stats_event.conn_events = 0;
103103
conn_stats_event.rd_bytes = 0;
104104
conn_stats_event.wr_bytes = 0;
@@ -171,9 +171,9 @@ TEST_F(ConnStatsTest, ServerSide) {
171171
conn0_stats_event.timestamp_ns = 0;
172172
conn0_stats_event.conn_id = kConnID0;
173173
conn0_stats_event.role = kRoleServer;
174-
conn0_stats_event.addr.in4.sin_family = AF_INET;
175-
conn0_stats_event.addr.in4.sin_port = 54321;
176-
conn0_stats_event.addr.in4.sin_addr.s_addr = 0x01010101; // 1.1.1.1
174+
conn0_stats_event.raddr.in4.sin_family = AF_INET;
175+
conn0_stats_event.raddr.in4.sin_port = 54321;
176+
conn0_stats_event.raddr.in4.sin_addr.s_addr = 0x01010101; // 1.1.1.1
177177
conn0_stats_event.conn_events = 0;
178178
conn0_stats_event.rd_bytes = 0;
179179
conn0_stats_event.wr_bytes = 0;
@@ -199,9 +199,9 @@ TEST_F(ConnStatsTest, ServerSide) {
199199
conn1_stats_event.timestamp_ns = 0;
200200
conn1_stats_event.conn_id = kConnID1;
201201
conn1_stats_event.role = kRoleServer;
202-
conn1_stats_event.addr.in4.sin_family = AF_INET;
203-
conn1_stats_event.addr.in4.sin_port = 65432;
204-
conn1_stats_event.addr.in4.sin_addr.s_addr = 0x01010101; // 1.1.1.1
202+
conn1_stats_event.raddr.in4.sin_family = AF_INET;
203+
conn1_stats_event.raddr.in4.sin_port = 65432;
204+
conn1_stats_event.raddr.in4.sin_addr.s_addr = 0x01010101; // 1.1.1.1
205205
conn1_stats_event.conn_events = 0;
206206
conn1_stats_event.rd_bytes = 0;
207207
conn1_stats_event.wr_bytes = 0;
@@ -227,9 +227,9 @@ TEST_F(ConnStatsTest, ServerSide) {
227227
conn2_stats_event.timestamp_ns = 0;
228228
conn2_stats_event.conn_id = kConnID3;
229229
conn2_stats_event.role = kRoleServer;
230-
conn2_stats_event.addr.in4.sin_family = AF_INET;
231-
conn2_stats_event.addr.in4.sin_port = 12345;
232-
conn2_stats_event.addr.in4.sin_addr.s_addr = 0x02020202; // 2.2.2.2
230+
conn2_stats_event.raddr.in4.sin_family = AF_INET;
231+
conn2_stats_event.raddr.in4.sin_port = 12345;
232+
conn2_stats_event.raddr.in4.sin_addr.s_addr = 0x02020202; // 2.2.2.2
233233
conn2_stats_event.conn_events = 0;
234234
conn2_stats_event.rd_bytes = 0;
235235
conn2_stats_event.wr_bytes = 0;
@@ -263,9 +263,9 @@ TEST_F(ConnStatsTest, ClientSide) {
263263
conn0_stats_event.timestamp_ns = 0;
264264
conn0_stats_event.conn_id = kConnID0;
265265
conn0_stats_event.role = kRoleClient;
266-
conn0_stats_event.addr.in4.sin_family = AF_INET;
267-
conn0_stats_event.addr.in4.sin_port = htons(80);
268-
conn0_stats_event.addr.in4.sin_addr.s_addr = 0x01010101; // 1.1.1.1
266+
conn0_stats_event.raddr.in4.sin_family = AF_INET;
267+
conn0_stats_event.raddr.in4.sin_port = htons(80);
268+
conn0_stats_event.raddr.in4.sin_addr.s_addr = 0x01010101; // 1.1.1.1
269269
conn0_stats_event.conn_events = 0;
270270
conn0_stats_event.rd_bytes = 0;
271271
conn0_stats_event.wr_bytes = 0;
@@ -291,9 +291,9 @@ TEST_F(ConnStatsTest, ClientSide) {
291291
conn1_stats_event.timestamp_ns = 0;
292292
conn1_stats_event.conn_id = kConnID1;
293293
conn1_stats_event.role = kRoleClient;
294-
conn1_stats_event.addr.in4.sin_family = AF_INET;
295-
conn1_stats_event.addr.in4.sin_port = htons(80);
296-
conn1_stats_event.addr.in4.sin_addr.s_addr = 0x01010101; // 1.1.1.1
294+
conn1_stats_event.raddr.in4.sin_family = AF_INET;
295+
conn1_stats_event.raddr.in4.sin_port = htons(80);
296+
conn1_stats_event.raddr.in4.sin_addr.s_addr = 0x01010101; // 1.1.1.1
297297
conn1_stats_event.conn_events = 0;
298298
conn1_stats_event.rd_bytes = 0;
299299
conn1_stats_event.wr_bytes = 0;
@@ -319,9 +319,9 @@ TEST_F(ConnStatsTest, ClientSide) {
319319
conn2_stats_event.timestamp_ns = 0;
320320
conn2_stats_event.conn_id = kConnID3;
321321
conn2_stats_event.role = kRoleClient;
322-
conn2_stats_event.addr.in4.sin_family = AF_INET;
323-
conn2_stats_event.addr.in4.sin_port = htons(21);
324-
conn2_stats_event.addr.in4.sin_addr.s_addr = 0x01010101; // 1.1.1.1
322+
conn2_stats_event.raddr.in4.sin_family = AF_INET;
323+
conn2_stats_event.raddr.in4.sin_port = htons(21);
324+
conn2_stats_event.raddr.in4.sin_addr.s_addr = 0x01010101; // 1.1.1.1
325325
conn2_stats_event.conn_events = 0;
326326
conn2_stats_event.rd_bytes = 0;
327327
conn2_stats_event.wr_bytes = 0;
@@ -350,7 +350,7 @@ TEST_F(ConnStatsTest, NoEventsIfNoRemoteAddr) {
350350
conn_stats_event.timestamp_ns = 0;
351351
conn_stats_event.conn_id = kConnID0;
352352
conn_stats_event.role = kRoleClient;
353-
conn_stats_event.addr.in4.sin_family = PX_AF_UNKNOWN;
353+
conn_stats_event.raddr.in4.sin_family = PX_AF_UNKNOWN;
354354
conn_stats_event.conn_events = 0;
355355
conn_stats_event.rd_bytes = 0;
356356
conn_stats_event.wr_bytes = 0;
@@ -377,9 +377,9 @@ TEST_F(ConnStatsTest, DisabledConnTracker) {
377377
conn_stats_event.timestamp_ns = 0;
378378
conn_stats_event.conn_id = kConnID0;
379379
conn_stats_event.role = kRoleClient;
380-
conn_stats_event.addr.in4.sin_family = AF_INET;
381-
conn_stats_event.addr.in4.sin_port = htons(80);
382-
conn_stats_event.addr.in4.sin_addr.s_addr = 0x01010101; // 1.1.1.1
380+
conn_stats_event.raddr.in4.sin_family = AF_INET;
381+
conn_stats_event.raddr.in4.sin_port = htons(80);
382+
conn_stats_event.raddr.in4.sin_addr.s_addr = 0x01010101; // 1.1.1.1
383383
conn_stats_event.conn_events = 0;
384384
conn_stats_event.rd_bytes = 0;
385385
conn_stats_event.wr_bytes = 0;

src/stirling/source_connectors/socket_tracer/conn_tracker.cc

Lines changed: 30 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -102,7 +102,8 @@ void ConnTracker::AddConnOpenEvent(const socket_control_event_t& event) {
102102
}
103103
open_info_.timestamp_ns = event.timestamp_ns;
104104

105-
SetRemoteAddr(event.open.addr, "Inferred from conn_open.");
105+
SetRemoteAddr(event.open.raddr, "Inferred from conn_open.");
106+
SetLocalAddr(event.open.laddr, "Inferred from conn_open.");
106107

107108
SetRole(event.open.role, "Inferred from conn_open.");
108109

@@ -192,7 +193,8 @@ void UpdateProtocolMetrics(traffic_protocol_t protocol, const conn_stats_event_t
192193

193194
void ConnTracker::AddConnStats(const conn_stats_event_t& event) {
194195
SetRole(event.role, "inferred from conn_stats event");
195-
SetRemoteAddr(event.addr, "conn_stats event");
196+
SetRemoteAddr(event.raddr, "conn_stats event");
197+
SetLocalAddr(event.laddr, "conn_stats event");
196198
UpdateTimestamps(event.timestamp_ns);
197199

198200
CONN_TRACE(1) << absl::Substitute("ConnStats timestamp=$0 wr=$1 rd=$2 close=$3",
@@ -476,6 +478,17 @@ void ConnTracker::SetRemoteAddr(const union sockaddr_t addr, std::string_view re
476478
}
477479
}
478480

481+
void ConnTracker::SetLocalAddr(const union sockaddr_t addr, std::string_view reason) {
482+
if (open_info_.local_addr.family == SockAddrFamily::kUnspecified) {
483+
PopulateSockAddr(&addr.sa, &open_info_.local_addr);
484+
if (addr.sa.sa_family == PX_AF_UNKNOWN) {
485+
open_info_.local_addr.family = SockAddrFamily::kUnspecified;
486+
}
487+
CONN_TRACE(1) << absl::Substitute("LocalAddr updated $0, reason=[$1]",
488+
open_info_.local_addr.AddrStr(), reason);
489+
}
490+
}
491+
479492
bool ConnTracker::SetRole(endpoint_role_t role, std::string_view reason) {
480493
// Don't allow changing active role, unless it is from unknown to something else.
481494
if (role_ != kRoleUnknown) {
@@ -782,8 +795,8 @@ void ConnTracker::IterationPreTick(
782795
return;
783796
}
784797

785-
// If remote_addr is missing, it means the connect/accept was not traced.
786-
// Attempt to infer the connection information, to populate remote_addr.
798+
// If remote_addr is missing, it means the connect/accept syscall was not traced.
799+
// Attempt to infer the connection information, to populate remote_addr and local_addr.
787800
if (open_info_.remote_addr.family == SockAddrFamily::kUnspecified && socket_info_mgr != nullptr) {
788801
InferConnInfo(proc_parser, socket_info_mgr);
789802

@@ -888,19 +901,26 @@ double ConnTracker::StitchFailureRate() const {
888901

889902
namespace {
890903

891-
Status ParseSocketInfoRemoteAddr(const system::SocketInfo& socket_info, SockAddr* addr) {
904+
Status ParseSocketInfoAddr(const system::SocketInfo& socket_info, SockAddr* remote_addr,
905+
SockAddr* local_addr) {
892906
switch (socket_info.family) {
893907
case AF_INET:
894908
PopulateInetAddr(std::get<struct in_addr>(socket_info.remote_addr), socket_info.remote_port,
895-
addr);
909+
remote_addr);
910+
PopulateInetAddr(std::get<struct in_addr>(socket_info.local_addr), socket_info.local_port,
911+
local_addr);
896912
break;
897913
case AF_INET6:
898914
PopulateInet6Addr(std::get<struct in6_addr>(socket_info.remote_addr), socket_info.remote_port,
899-
addr);
915+
remote_addr);
916+
PopulateInet6Addr(std::get<struct in6_addr>(socket_info.local_addr), socket_info.local_port,
917+
local_addr);
900918
break;
901919
case AF_UNIX:
902920
PopulateUnixAddr(std::get<struct un_path_t>(socket_info.remote_addr).path,
903-
socket_info.remote_port, addr);
921+
socket_info.remote_port, remote_addr);
922+
PopulateUnixAddr(std::get<struct un_path_t>(socket_info.local_addr).path,
923+
socket_info.local_port, local_addr);
904924
break;
905925
default:
906926
return error::Internal("Unknown socket_info family: $0", socket_info.family);
@@ -1004,11 +1024,11 @@ void ConnTracker::InferConnInfo(system::ProcParser* proc_parser,
10041024

10051025
// Success! Now copy the inferred socket information into the ConnTracker.
10061026

1007-
Status s = ParseSocketInfoRemoteAddr(socket_info, &open_info_.remote_addr);
1027+
Status s = ParseSocketInfoAddr(socket_info, &open_info_.remote_addr, &open_info_.local_addr);
10081028
if (!s.ok()) {
10091029
conn_resolver_.reset();
10101030
conn_resolution_failed_ = true;
1011-
LOG(ERROR) << absl::Substitute("Remote address (type=$0) parsing failed. Message: $1",
1031+
LOG(ERROR) << absl::Substitute("Remote and local address (type=$0) parsing failed. Message: $1",
10121032
socket_info.family, s.msg());
10131033
return;
10141034
}

src/stirling/source_connectors/socket_tracer/conn_tracker.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -68,6 +68,7 @@ struct SocketOpen {
6868
uint64_t timestamp_ns = 0;
6969
// TODO(yzhao): Consider using std::optional to indicate the address has not been initialized.
7070
SockAddr remote_addr;
71+
SockAddr local_addr;
7172
};
7273

7374
struct SocketClose {
@@ -577,6 +578,7 @@ class ConnTracker : NotCopyMoveable {
577578
void SetConnID(struct conn_id_t conn_id);
578579

579580
void SetRemoteAddr(const union sockaddr_t addr, std::string_view reason);
581+
void SetLocalAddr(const union sockaddr_t addr, std::string_view reason);
580582

581583
// Returns false if the protocol change is disallowed.
582584
bool SetProtocol(traffic_protocol_t protocol, std::string_view reason);

0 commit comments

Comments
 (0)