Skip to content

net_allow: scheme-prefixed rules with per-protocol destination filtering#41

Merged
congwang-mk merged 4 commits intomainfrom
net-allow-protocols
May 6, 2026
Merged

net_allow: scheme-prefixed rules with per-protocol destination filtering#41
congwang-mk merged 4 commits intomainfrom
net-allow-protocols

Conversation

@congwang-mk
Copy link
Copy Markdown
Contributor

@congwang-mk congwang-mk commented May 6, 2026

Summary

Three-commit rework of how net_allow expresses and enforces protocol gating. Pre-1.0, hard breaks — no compat shims for the old allow_udp / allow_icmp flags.

1. Scheme-prefixed rules (042472b). --net-allow (and the equivalent TOML / Rust / Python surfaces) now accepts an optional scheme:

  • bare host:port → tcp (unchanged)
  • tcp://... / udp://... / icmp://host / icmp://*

Protocol gating falls out of rule presence per scheme — no separate boolean flag. The allow_udp and allow_icmp policy fields, builder methods, FFI setters, CLI flags, TOML keys, and Python dataclass fields are all dropped. Raw ICMP is no longer exposed at all (the BPF unconditionally denies SOCK_RAW); workloads that need ping should rely on net.ipv4.ping_group_range and use the SOCK_DGRAM kernel ping socket via icmp://....

2. Per-protocol destination routing (de1e55b). After commit 1 a UDP/ICMP rule's host parsed but had no runtime effect — the on-behalf handlers consulted a single global NetworkPolicy mixing all rules, so a udp://1.1.1.1:53 rule would also let TCP connect to that endpoint. This commit splits the resolved set into tcp / udp / icmp slices, gives NetworkState three protocol-keyed policies, and routes the on-behalf check via getsockopt(SOL_SOCKET, SO_PROTOCOL) on the dup'd child fd. One pidfd_getfd per network call serves both the protocol probe and the eventual on-behalf send. Unknown protocols (raw, SCTP, etc.) fail closed.

3. sendmmsg coverage (549bab9). The most common UDP escape hatch — sendmmsg wasn't trapped, so an agent could batch sends to bypass per-message filtering. Factors out prescan_msghdr and send_msghdr_on_behalf from sendmsg, then sendmmsg_on_behalf pre-scans every entry (Continue if any has NULL msg_name or non-IP family), dups once, queries SO_PROTOCOL once, and loops with proper partial-failure semantics (returns N for "first N sent successfully," writes per-entry msg_len back). Capped at 256 entries per call. As a side effect the prescan close a TOCTOU window where the kernel re-reads child memory after our check on the Continue path.

Test plan

498/498 cargo tests pass. New coverage on this branch:

  • Parser: 11 new NetAllow::parse unit tests covering bare-form default, all four schemes, host-scoped raw rejection, port-on-icmp rejection, unknown-scheme rejection.
  • Resolver: test_resolve_per_protocol_isolation (UDP rule doesn't leak into TCP set), test_resolve_icmp_no_ports, test_resolve_icmp_wildcard.
  • Routing (Phase 2): test_udp_rule_scopes_destination_by_host (udp://127.0.0.1:53 allows sendto(127.0.0.1:53), denies sendto(1.1.1.1:53) with errno 111), test_udp_wildcard_allows_any_destination, test_udp_rule_does_not_authorize_tcp (cross-protocol leak prevention).
  • sendmmsg: test_sendmmsg_partial_failure_on_blocked_destination (vlen=2, first allowed second blocked → ret=1, msg_len=1), test_sendmmsg_single_blocked_returns_econnrefused.
  • Regression: test_seccomp_enforce::test_allow_icmp_* reworked to use rule-based opt-in; test_icmp_dgram_allowed_with_icmp_rule covers the kernel ping socket path; test_raw_icmp_always_denied proves SOCK_RAW stays denied even under icmp://*.

`--net-allow` (and the equivalent TOML/Rust/Python surfaces) now accepts
an optional scheme prefix:

  bare host:port  → tcp (unchanged)
  tcp://...       → explicit tcp
  udp://...       → udp; udp://*:* opts UDP socket creation in entirely
  icmp://host     → kernel ping socket (SOCK_DGRAM + IPPROTO_ICMP)
  icmp://*        → any ICMP echo destination

Signed-off-by: Cong Wang <cwang@multikernel.io>
Signed-off-by: Cong Wang <cwang@multikernel.io>
Signed-off-by: Cong Wang <cwang@multikernel.io>
Signed-off-by: Cong Wang <cwang@multikernel.io>
@congwang-mk congwang-mk merged commit c2e4630 into main May 6, 2026
8 checks passed
@congwang-mk congwang-mk deleted the net-allow-protocols branch May 6, 2026 22:54
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