Skip to content

M5: network collector with pid correlation - #5

Merged
dylanpatriarchi merged 1 commit into
feat/m4-file-collectorfrom
feat/m5-network-collector
Aug 1, 2026
Merged

dylanpatriarchi merged 1 commit into
feat/m4-file-collectorfrom
feat/m5-network-collector

Conversation

@dylanpatriarchi

Copy link
Copy Markdown
Owner

Milestone 5 — network collector

Stack: 4 of 6 · base feat/m4-file-collector (PR #4) · next: M6 correlation

Educational project — read-only. This collector reads /proc/net/* and /proc/*/fd. It never opens a socket, connects anywhere, or interferes with a connection.

procnet.nim — pure parsing, where most of the risk lives

  • Addresses are hex and little-endian per 32-bit word. 0100007F is 127.0.0.1, not 1.0.0.127. IPv6 is four little-endian words, so 2001:db8::1 is stored as B80D0120… — a whole-string reversal gets that wrong while still passing a ::1 test, so the suite tests a global address specifically.
  • Output is RFC 5952 canonical, so rules can match literal address strings.
  • State codes, ports, uid and the socket inode all parsed and tested, including malformed, truncated and header lines.

network.nim — diffing and pid attribution

  • socketKey deliberately excludes state, so syn_sent → established is one connection rather than two, and includes the inode so a reused four-tuple is correctly new.
  • Direction is partly inference, and says so. SYN_SENT and LISTEN are observed facts; ESTABLISHED is a judgement based on whether the local port is also listened on. Every event carries raw.direction_basis with the reasoning, and the two knowable wrong cases are documented.
  • Teardown states emit nothing. Seeing FIN_WAIT first means the poll missed the connection's life; dating the event at teardown would mislead.
  • Unattributed connections are still emitted, with pid UnknownId and a raw.pid_reason distinguishing "no inode in the table" from "cannot read another user's /proc/<pid>/fd — run as root". The uid is still known even when the pid isn't, so ownership isn't lost.
  • /proc/*/fd is only walked when a new socket actually appeared, so a quiet interval costs nothing.

Verification

117 tests (64 parsing + 53 collector), 594 total at this point, all green. Verified in an isolated worktree.

Reads the kernel's socket tables and turns changes into normalized
net_connect / net_listen / net_accept events, correlated to a pid where
that is possible.

procnet.nim is pure parsing, which is where most of the risk lives:

- Addresses are hex and LITTLE-ENDIAN PER 32-BIT WORD. 0100007F is
  127.0.0.1, not 1.0.0.127. IPv6 is four little-endian words, so 2001:db8::1
  is stored as B80D0120... — a whole-string reversal gets that wrong while
  still passing a ::1 test, so the suite tests a global address specifically.
- Output is RFC 5952 canonical, so rules can match literal address strings.
- State codes, ports, uid and the socket inode all parsed and tested,
  including malformed, truncated and header lines.

network.nim diffs consecutive tables and attributes sockets to processes:

- socketKey deliberately excludes state, so syn_sent -> established is one
  connection rather than two, and includes the inode so a reused four-tuple
  is correctly new.
- Direction is partly inference and says so. SYN_SENT and LISTEN are facts;
  ESTABLISHED is a judgement based on whether the local port is also
  listened on. Every event carries raw.direction_basis with the reasoning,
  and the two knowable wrong cases are documented.
- Teardown states emit nothing. Seeing FIN_WAIT first means the poll missed
  the connection's life, and dating the event at teardown would mislead.
- Unattributed connections are still emitted, with pid UnknownId and a
  raw.pid_reason distinguishing 'no inode in the table' from 'cannot read
  another user's /proc/<pid>/fd — run as root'. The uid is still known even
  when the pid is not, so ownership is not lost.
- /proc/*/fd is only walked when a new socket actually appeared, so a quiet
  interval costs nothing.

117 tests.
@dylanpatriarchi
dylanpatriarchi merged commit 94653e0 into feat/m4-file-collector Aug 1, 2026
1 check passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant