Skip to content

Linux 4.0.0 - #253

Merged
Anof-cyber merged 26 commits into
masterfrom
dev
Oct 14, 2026
Merged

Anof-cyber merged 26 commits into
masterfrom
dev

Conversation

@Anof-cyber

Copy link
Copy Markdown
Member

Pull Request

⚠️ IMPORTANT - READ BEFORE SUBMITTING

Required Pre-Submission Checklist

  • I have read the CONTRIBUTING.md file
  • I have created a GitHub issue for this change and linked it below
  • I have forked the dev branch (NOT master)
  • I understand that the master branch is ONLY updated when a new release is created
  • I understand that pull requests to master will be REJECTED

⛔ Critical Information

DO NOT submit a pull request directly to the master branch. The master branch is only updated when a new release is created. No direct commits or merges are accepted to master - neither from the InterceptSuite team nor from contributors.

All changes MUST be submitted to the dev branch, which contains the latest code and commits.

You MUST create a GitHub issue first before submitting a pull request. This allows the InterceptSuite team to:

  • Verify if the bug is already fixed in the development branch
  • Confirm if a new feature is accepted and approved
  • Avoid duplicate work

Pull requests without an associated issue will be rejected.


Pull Request Details

Type of Change

  • Bug Fix
  • New Feature
  • Code Improvement/Refactoring
  • Documentation Update
  • Other (please describe):

Related GitHub Issue

Issue Link:

Platform(s) Affected

  • Windows
  • macOS
  • Linux
  • Cross-platform/All

macOS Application Changes

Does this pull request include changes to the macOS application?

  • Yes
  • No

If YES, you MUST answer the following:

macOS Code Signing & Verification

  • I have signed the macOS app with a valid Apple Developer account
  • I have run the signed application on macOS to verify my changes work as expected
  • I confirm the application launches and functions correctly with my changes

⚠️ Important: If you cannot sign the macOS app to verify your code changes work, please DO NOT submit this pull request. Instead, use the GitHub issue you created to let the InterceptSuite team fix the bug or add the feature. You will need to wait for the next release.


Description of Changes

What does this pull request do?

How has this been tested?

  • Test environment (OS, version, etc.):
  • Test steps:
  • Test results:

Compilation & Dependencies

  • I have reviewed the CONTRIBUTING.md file for compilation instructions
  • I have verified all required libraries and packages are documented
  • My changes compile without errors
  • My changes do not introduce new dependencies (or I have documented them below)

New dependencies (if any):


Screenshots/Logs (if applicable)


Additional Context


Final Checklist

  • My code follows the project's coding style
  • I have commented my code, particularly in hard-to-understand areas
  • I have updated documentation as needed
  • My changes do not generate new warnings or errors
  • I have tested my changes on the target platform(s)
  • I have verified this change is based on the latest dev branch
  • I understand that pull requests without an associated GitHub issue will be rejected

Selected processes' connect()/sendmsg() are redirected by cgroup sock_addr
hooks to a local relay instead of queueing every packet to userspace. The
data path is untouched, there is no per-packet work, and the kernel removes
the programs by itself when the process exits or crashes.

- eBPF: connect4/6, sendmsg4/6, recvmsg4/6, getpeername4/6 and sockops.
  Processes are identified by executable (dev, inode) and mapped to a class
  of compiled rules; anything the kernel cannot decide goes to the relay once
  and the result is cached in the kernel. Sockets of other network
  namespaces are ignored.
- Rule engine: validated globs, IPv4/IPv6, CIDR, ranges, octet patterns,
  port lists/ranges, protocol, first-match-wins, and domain names.
- Multiple proxies, each rule can pick its own (SOCKS5, SOCKS5 with auth,
  HTTP CONNECT). A PROXY rule whose proxy is missing blocks.
- Domain rules via a DNS tap: DNS answers (UDP and TCP) seen by the relay
  build a bounded IP -> name map, armed in the kernel before the answer is
  delivered; also loads /etc/hosts.
- epoll relay with splice(), bounded memory (idle connections cost ~100
  bytes), SOCKS5 UDP ASSOCIATE, direct forwarding, idle timeouts, connection
  cap and hostname proxy resolution with a deadline and cache.
- Public API gains proxy management, per-rule proxy ids, rule validation,
  reordering, and keeps rules across Stop/Start.
- repeatable --proxy [name=]type://[user:pass@]host:port (IPv6 hosts in [])
- --rule process:hosts:ports:protocol:action[=proxy]
- hosts accept domain names (github.com, *.github.com)
- validation errors from the library are printed
- no work in signal handlers; --cleanup is now a documented no-op
- proxy list manager with per-proxy test, default proxy and validation
- profiles (new, duplicate, rename, delete, switch without restarting the
  engine), each with its own proxies, rules and default proxy; old configs
  migrate to a profile named "Default"
- rule editor: proxy selection, inline validation, move up/down,
  JSON import/export with a validation summary
- domain names accepted in the hosts field; logs show the domain
- PROXYBRIDGE_CONFIG_DIR overrides the config directory
- signal handling and callback memory safety fixes
- config/profile unit test
Run against the real kernel datapath (root): e2e.sh, e2e_rules.sh (every
rule form and proxy type), e2e_domains.sh (real dig + curl), api_live.sh
(live rule/proxy edits), e2e_openssh.sh (interop with a real third-party
SOCKS5 server), gui_headless.sh (real GUI under the GTK Broadway backend),
scale.sh (10,000 concurrent connections), plus hookbench/thrbench/dnsbench.
Includes fake SOCKS5/HTTP/DNS servers.
README: new architecture, domain rules and their limits, multiple proxies,
requirements (kernel >= 5.8 with BTF and cgroup v2) and testing.
setup/deploy: depend on libbpf instead of netfilter/iptables.
.gitignore: Linux build artifacts and generated skeletons; keep test scripts.
build.sh now compiles in a temporary directory and copies just
libproxybridge.so, ProxyBridge and ProxyBridgeGUI into output/. The source
tree no longer collects .o files, the generated eBPF skeleton or libraries,
even when a build fails. The Makefiles take BUILD_DIR/LIBDIR for this; a
plain `make` still builds in place for development.

- check for gcc, make, clang, bpftool and libbpf with install hints
- OUTPUT_DIR can be overridden; a clear error if output/ cannot be removed
- fix the GUI rpath: $ORIGIN was expanded to an empty string by the shell
- test scripts use the built binaries from output/ (override with PB_DIR)
The Linux workflows still installed libnetfilter-queue and libnfnetlink.
The eBPF build needs clang (BPF object), bpftool (skeleton) and libbpf-dev.
The build workflow now also runs the rule engine, DNS parser and relay unit
tests, which need no root.
- run on push and pull requests for Linux-ebpf as well as master and dev
- the GUI is now required to build (GTK3 is installed on the runner)
- smoke test: CLI help, library linkage, GUI $ORIGIN runpath, exported public
  API, and no libnetfilter dependency
- fail if the build leaves untracked or ignored files outside Linux/output
- best-effort end-to-end job (e2e, e2e_rules, e2e_domains, api_live) against
  the real kernel datapath; reported but non-blocking since it needs a BTF
  kernel with cgroup v2 and root
On Ubuntu 24.04 "bpftool" is a virtual package, so `apt install bpftool`
fails. Install linux-tools-common/linux-tools-generic and locate the real
binary under /usr/lib/linux-tools, exporting it as BPFTOOL.

build.sh and the end-to-end scripts honour $BPFTOOL, so a bpftool that is not
on PATH works; the error message now suggests the right package per distro.
The relay test harness called unshare(CLONE_NEWUSER|CLONE_NEWNET) in its main
process. On GitHub's Ubuntu 24.04 runners the namespace is created but
loopback cannot be configured, which stranded the whole run in a network
namespace without loopback and made every later test fail or hang.

- run the port-53 DNS tests in a forked child; the main process never changes
  namespaces; if the child cannot isolate itself the tests are skipped
- global watchdog (PB_TEST_WATCHDOG, default 300s), line-buffered output and
  bounded client timeouts so a broken environment fails fast
- start-up loopback self-check with a clear FATAL message
- workflows: job and step timeouts so a hung step cannot hold a runner
- fake decision state is now accessed under a mutex (it was a plain struct
  shared with relay threads)
- fd-leak checks settle and assert end <= base instead of equality
- wait budgets for closes, UDP replies and slot reuse are generous; hangs are
  still bounded by the watchdog
- expect_closed prints fd, poll, errno and recv details when it fails

Verified with 12 runs on 2 shared CPUs under 4 busy loops, plus plain,
ASan+UBSan and the simulated no-loopback mode.
The remaining intermittent CI failure ("TCP DECIDE -> BLOCK") happens before
expect_closed is reached, i.e. relay_connect() returned -1. Print the failing
call, the source port and errno so the next occurrence is diagnosable.
For flows the relay aborts (BLOCK decisions, connection cap) it closes with a
RST right after accept. On a fast loopback the RST can arrive before the
client's connect() has returned, so connect() itself fails with ECONNRESET.
That is a valid way for such a flow to end, but the tests only accepted a
reset after connect(); GitHub runners hit the race in ~2/3 of the runs.

closed_by_relay() treats ECONNRESET from connect() or EOF/RST afterwards as
closed, and is used by every "the relay must close this" assertion.
…ding, TCP and UDP)

Runs ProxyBridge in front of two Nexus instances (open and username/password)
with real origin servers and a real DNS server, instead of fakes:
rules by IP, port, port range, process, CIDR and domain; DNS itself carried
by the proxy (UDP and TCP); 200 MB download and 100 MB upload with checksums;
100 parallel and 1,500 sequential connections; one UDP socket talking to two
proxies, connected UDP, 59 KB datagrams, 300 concurrent UDP sockets, a
sustained stream; proxy restart (TCP fails closed, UDP re-associates).

Stock Nexus cannot do UDP ASSOCIATE on Linux (IPv6 socket bound to 0.0.0.0,
IPv4 destinations not sent as ::ffff:, mapped replies not matched);
tests/nexus-linux-udp.patch fixes it and the UDP checks are skipped without it.
fake_dns.py gains a PB_DNS_ZONE override.
The eBPF program calls bpf_get_current_task_btf(), added in Linux 5.11, to
read the calling executable. Correct the README, the public header comment
and the load-failure message.
…al proxies, slim the datapath

Security
- The kernel now keys a pending TCP flow by the client's source address AND
  port; the relay passes both when it claims the flow. A process connecting
  from another loopback address (e.g. 127.0.0.2) can no longer take another
  process's destination mapping.

Behaviour
- Processes that listen on a configured local proxy address are exempted
  automatically (per process, refreshed every 2 s), so a wildcard-process rule
  can no longer send a proxy's own connections back through itself.
- A UDP rule that points at an HTTP proxy (which cannot carry UDP) is logged
  with the rule id and proxy name instead of silently going direct.
- /etc/hosts is reloaded when it changes.

Robustness
- Shutdown detaches the hooks first and frees the BPF maps only after the
  relay has stopped; relay threads could previously touch freed maps.
- Executable paths are stored with their full length (a fixed 240-byte buffer
  could truncate and misclassify a long path).
- The skeleton header is generated atomically so a failed bpftool run cannot
  leave an empty file behind.

Leaner code
- Config lives in BPF global data (no map lookup per field), the never-read
  stats map is gone, IPv4/IPv6 hooks share their helpers, cached rule views
  replace per-decision allocation, the event reader sleeps instead of
  polling, the library exports only the 23 ProxyBridge_* functions, unused
  helpers and interface parameters are removed, comments are cut to the
  non-obvious ones. Compiled datapath is ~7% smaller.
- Makefile accepts EXTRA_CFLAGS/EXTRA_LDFLAGS for sanitizer builds.
… leaner code

- claim a pending TCP flow with the accepted peer's address and port
- every error and teardown path audited for fd, memory, thread and session
  leaks; a churn test (100,000 TCP connections across nine flow types, 20,000
  UDP sessions) asserts that descriptors return to baseline and RSS stays flat
- the UDP flow lookup (a BPF map syscall per datagram) is cached per client
  for one second; SOCKS5 headers are sent by scatter-gather; accepted sockets
  inherit their options from the listener
- banner and narration comments, benchmark-only hooks and dead helpers removed
- tests: hijack attempts from other loopback addresses, stress-tolerant
  assertions, a PB_TEST_ONLY filter, smaller sizes under the sanitizers
…he version macro

--proxy specs are wiped from argv after parsing so `ps` no longer shows
user:pass; the banner prints PROXYBRIDGE_VERSION; narration comments removed.
… JSON parser out

- profiles, proxies and rules are freed at exit (the only leak found)
- gui_json.c holds the rules import/export parser: it finds the end of an
  object properly (a '}' inside a string no longer breaks an import) and is
  unit-tested with hostile input (unterminated quotes, wrong types, a 20 MB
  string, 1M open braces, a 100k-rule round trip)
- duplicated dialog, combo and idle-callback code merged into small helpers;
  unused includes, structs and exported functions removed; comments cut to
  under 2%
- config tests run clean under ASan/LeakSanitizer and include a 20,000-cycle
  profile create/rename/duplicate/delete + save/load stress
…ty model

- e2e_security.sh plants a pending flow in the real kernel map and checks that
  a connection from 127.0.0.2 is refused, the owner still works, and an entry
  cannot be replayed
- churn.sh / churn_test.c: thousands of add/edit/move/disable/delete operations
  on rules and proxies plus restarts under live traffic; in-use heap,
  descriptors and threads must stay flat
- e2e_nexus.sh no longer needs a DIRECT rule for the proxy's own process; a
  second node process that is not the proxy must still be proxied
- e2e_rules.sh checks the UDP-over-HTTP-proxy warning; api_live.sh and the CI
  end-to-end job gain the new tests
- README: security model, automatic proxy exemption, UDP over HTTP behaviour
)

Out-of-tree builds with LTO, FORTIFY=3, PIE, RELRO/BIND_NOW, CET and a
pinned static PIC libbpf. A glibc 2.34 symbol floor and a version script
keep the binaries portable and the export list minimal; build.sh runs
tests/check_binaries.sh and fails if an audit check does not pass.
…ixes

Connection events go through a bounded queue drained on the GTK main
loop, with unit tests including a TSan run.
@Anof-cyber
Anof-cyber merged commit d697164 into master Oct 14, 2026
7 of 10 checks 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