Skip to content

Commit

Permalink
Parse local address from socket info if remote addr is known (#1833)
Browse files Browse the repository at this point in the history
Summary: Add condition before `InferConnInfo` to parse the local address
from socket information if missing when the remote address is known.
Note that this most likely occurs for syscalls (i.e. `connect`) for
which we are currently unable to trace the local IP using socket
information [from
bpf](https://github.com/pixie-io/pixie/blob/561ec31a619e9265cfae319a0aa7ec36e19e29d2/src/stirling/source_connectors/socket_tracer/bcc_bpf/socket_trace.c#L632).

Type of change: /kind feature

Test Plan: Existing E2E tests. Skaffolded to a cluster with kafka and
redis demos deployed and inspected pem logs (added logs to print
successfully inferred local IPs when remote IP is present). Ran
standalone pem on a node with kafka/redis demos running and inspected
logs.

Note that I've opted for an E2E test because writing a test case that
reliably reproduces the conditions for inference has been tricky. We'd
need to ensure that

1. remote address is known while local IP is not
2. short-lived processes/sockets don’t terminate or close before we
check `/proc/[pid]/fd/[n]`
3. connections are not bound to the loopback network interface

---------

Signed-off-by: Benjamin Kilimnik <[email protected]>
  • Loading branch information
benkilimnik authored Feb 22, 2024
1 parent e633c50 commit f6324ce
Showing 1 changed file with 49 additions and 18 deletions.
67 changes: 49 additions & 18 deletions src/stirling/source_connectors/socket_tracer/conn_tracker.cc
Original file line number Diff line number Diff line change
Expand Up @@ -797,9 +797,12 @@ void ConnTracker::IterationPreTick(

// If remote_addr is missing, it means the connect/accept syscall was not traced.
// Attempt to infer the connection information, to populate remote_addr and local_addr.
if (open_info_.remote_addr.family == SockAddrFamily::kUnspecified && socket_info_mgr != nullptr) {
InferConnInfo(proc_parser, socket_info_mgr);
const bool raddr_found = open_info_.remote_addr.family != SockAddrFamily::kUnspecified;
const bool laddr_found = open_info_.local_addr.family != SockAddrFamily::kUnspecified;
const bool info_mgr_ok = socket_info_mgr != nullptr;

if ((!raddr_found || !laddr_found) && info_mgr_ok) {
InferConnInfo(proc_parser, socket_info_mgr);
// TODO(oazizi): If connection resolves to SockAddr type "Other",
// we should mark the state in BPF to Other too, so BPF stops tracing.
// We should also mark the ConnTracker for death.
Expand Down Expand Up @@ -901,24 +904,17 @@ double ConnTracker::StitchFailureRate() const {

namespace {

Status ParseSocketInfoAddr(const system::SocketInfo& socket_info, SockAddr* remote_addr,
SockAddr* local_addr) {
Status ParseSocketInfoLocalAddr(const system::SocketInfo& socket_info, SockAddr* local_addr) {
switch (socket_info.family) {
case AF_INET:
PopulateInetAddr(std::get<struct in_addr>(socket_info.remote_addr), socket_info.remote_port,
remote_addr);
PopulateInetAddr(std::get<struct in_addr>(socket_info.local_addr), socket_info.local_port,
local_addr);
break;
case AF_INET6:
PopulateInet6Addr(std::get<struct in6_addr>(socket_info.remote_addr), socket_info.remote_port,
remote_addr);
PopulateInet6Addr(std::get<struct in6_addr>(socket_info.local_addr), socket_info.local_port,
local_addr);
break;
case AF_UNIX:
PopulateUnixAddr(std::get<struct un_path_t>(socket_info.remote_addr).path,
socket_info.remote_port, remote_addr);
PopulateUnixAddr(std::get<struct un_path_t>(socket_info.local_addr).path,
socket_info.local_port, local_addr);
break;
Expand All @@ -929,6 +925,27 @@ Status ParseSocketInfoAddr(const system::SocketInfo& socket_info, SockAddr* remo
return Status::OK();
}

Status ParseSocketInfoRemoteAddr(const system::SocketInfo& socket_info, SockAddr* remote_addr) {
switch (socket_info.family) {
case AF_INET:
PopulateInetAddr(std::get<struct in_addr>(socket_info.remote_addr), socket_info.remote_port,
remote_addr);
break;
case AF_INET6:
PopulateInet6Addr(std::get<struct in6_addr>(socket_info.remote_addr), socket_info.remote_port,
remote_addr);
break;
case AF_UNIX:
PopulateUnixAddr(std::get<struct un_path_t>(socket_info.remote_addr).path,
socket_info.remote_port, remote_addr);
break;
default:
return error::Internal("Unknown socket_info family: $0", socket_info.family);
}

return Status::OK();
}

endpoint_role_t TranslateRole(system::ClientServerRole role) {
switch (role) {
case system::ClientServerRole::kClient:
Expand All @@ -949,6 +966,9 @@ void ConnTracker::InferConnInfo(system::ProcParser* proc_parser,
DCHECK(proc_parser != nullptr);
DCHECK(socket_info_mgr != nullptr);

const bool raddr_found = open_info_.remote_addr.family != SockAddrFamily::kUnspecified;
const bool laddr_found = open_info_.local_addr.family != SockAddrFamily::kUnspecified;

if (conn_resolution_failed_) {
// We've previously tried and failed to perform connection inference,
// so don't waste any time...a connection only gets one chance.
Expand All @@ -962,7 +982,9 @@ void ConnTracker::InferConnInfo(system::ProcParser* proc_parser,
bool success = conn_resolver_->Setup();
if (!success) {
conn_resolver_.reset();
conn_resolution_failed_ = true;
if (!raddr_found) {
conn_resolution_failed_ = true;
}
CONN_TRACE(2) << "Can't infer remote endpoint. Setup failed.";
} else {
CONN_TRACE(2) << "FDResolver has been created.";
Expand Down Expand Up @@ -1024,13 +1046,22 @@ void ConnTracker::InferConnInfo(system::ProcParser* proc_parser,

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

Status s = ParseSocketInfoAddr(socket_info, &open_info_.remote_addr, &open_info_.local_addr);
if (!s.ok()) {
conn_resolver_.reset();
conn_resolution_failed_ = true;
LOG(ERROR) << absl::Substitute("Remote and local address (type=$0) parsing failed. Message: $1",
socket_info.family, s.msg());
return;
if (!laddr_found) {
Status s = ParseSocketInfoLocalAddr(socket_info, &open_info_.local_addr);
if (!s.ok()) {
CONN_TRACE(2) << absl::Substitute("Local address (type=$0) parsing failed. Message: $1",
socket_info.family, s.msg());
}
}
if (!raddr_found) {
Status s = ParseSocketInfoRemoteAddr(socket_info, &open_info_.remote_addr);
if (!s.ok()) {
conn_resolver_.reset();
conn_resolution_failed_ = true;
CONN_TRACE(2) << absl::Substitute("Remote address (type=$0) parsing failed. Message: $1",
socket_info.family, s.msg());
return;
}
}

endpoint_role_t inferred_role = TranslateRole(socket_info.role);
Expand Down

0 comments on commit f6324ce

Please sign in to comment.