Skip to content

Commit 0cc5c66

Browse files
saxena-anuragCopilotCopilotmikeagunMichael Agun
authored
Cherry-pick fixes to release/1.3 (#5362)
* Fix bpf2bpf calls: global helper index, runtime_context propagation, and map access from subprograms (#5274) * fix * add missing sample program * fix * update tests * update expected files * update tests * cleanup * fix helper update callback, add tests * Potential fix for pull request finding Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com> --------- Co-authored-by: copilot-swe-agent[bot] <198982749+Copilot@users.noreply.github.com> Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com> * update verifier submodule (#5335) * Implement sock_addr bind hook (#5272) * Align bind hook with Linux: add CGROUP_INET4/6_BIND attach types Add new attach types BPF_CGROUP_INET4_BIND and BPF_CGROUP_INET6_BIND under the existing BPF_PROG_TYPE_CGROUP_SOCK_ADDR program type. This provides a Linux-aligned bind hook using bpf_sock_addr_t, enabling cross-platform eBPF programs that use 'cgroup/bind4' / 'cgroup/bind6' SEC names. Key changes: - New attach type GUIDs in ebpf_program_attach_type_guids.h - New BPF enum values in ebpf_structs.h - ELF section name mappings 'cgroup/bind4' / 'cgroup/bind6' - New WFP callouts at ALE_RESOURCE_ASSIGNMENT_V4/V6 (distinct GUIDs from legacy bind callouts so both can coexist at the same WFP layer) - New net_ebpf_extension_sock_addr_bind_classify() with bind-specific bpf_sock_addr context population (user_ip/user_port = local bind address, msg_src_* = 0) - Multi-attach support via ATTACH_CAPABILITY_MULTI_ATTACH_WITH_WILDCARD (satisfies issue #5180) - bpf_sock_addr_get_network_context extended to support bind hook IDs Out of scope: address modification (bind hook is allow/deny only in this phase), post-bind, unbind/release. Legacy BPF_PROG_TYPE_BIND with bind_md_t remains unchanged for backward compatibility. Issue: #333 Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> * Add tests for sock_addr-aligned bind hook (issue #333) Adds sample BPF program (cgroup_sock_addr_bind.c) and socket driver tests for the new BPF_CGROUP_INET4/6_BIND attach types. * Document sock_addr-aligned bind hook (#333) New docs/BindHook.md documents BPF_CGROUP_INET4_BIND and BPF_CGROUP_INET6_BIND attach types under BPF_PROG_TYPE_CGROUP_SOCK_ADDR, including their relationship to the legacy bind hook, context semantics, helper support, and Linux compatibility. Cross-references added to docs/ConnectAuthorizationAttachTypes.md and docs/ListenHook.md. Sample tests/sample/cgroup_sock_addr_bind.c (added earlier in this branch) is registered in tests/bpf2c_tests/elf_bpf.cpp, and the corresponding expected outputs in tests/bpf2c_tests/expected/ are checked in. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> * Expose interface_type/tunnel_type WFP fields for bind hooks Add FWPS_FIELD_ALE_RESOURCE_ASSIGNMENT_V4/V6_INTERFACE_TYPE and _TUNNEL_TYPE to the wfp_bind_fields[] table and populate them in _net_ebpf_extension_sock_addr_copy_wfp_bind_fields(). The get_network_context helper was already extended to accept bind hook IDs on this branch. Update documentation comments on bpf_sock_addr_network_context_t, the helper, and BindHook.md to list BIND as a supported attach type. Add cgroup/bind4 and cgroup/bind6 programs to cgroup_sock_addr_helpers.c that call bpf_sock_addr_get_network_context and store results in the network_context_map. Add driver tests bind_helper_functions_validation_tcp_v4 and _v6 that bind a socket and verify the network context map contains valid interface_type and tunnel_type values. Regenerate bpf2c expected output for the updated helper programs. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> * Fix bpf2c expected output: regenerate with clang 18.1.8 matching CI Previous regeneration used clang 19.1.4 (NuGet) but CI installs clang 18.1.8 from llvm-project GitHub releases. The different LLVM versions produce different bytecode and #line numbers, causing bpf2c_tests to fail on the Verify test case. Regenerated using clang 18.1.8 from C:\Program Files\LLVM\bin\clang.exe with the official scripts\generate_expected_bpf2c_output.ps1 script. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> * Add matching comments to V6 entry in wfp_bind_fields Add descriptive comments for zeroed fields in the V6 entry of wfp_bind_fields[] to match the V4 entry style. Addresses review feedback from FetoiuCatalin. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> * Address PR review feedback for bind hook (#5272) Documentation: - Update 'bind() requests' and 'bind() calls' wording to 'bind operations' in attach type/program type doc comments across ebpf_program_attach_type_guids.h, ebpf_structs.h, and ebpf_nethooks.h. INET4/INET6_BIND comments now read 'when an IPv4/IPv6 socket is bound to an address/port' (dthaler, shankarseal). Sample code: - Document the counter_key scheme used by cgroup_sock_addr_helpers programs (1=connect_v4, 2=connect_v6, 3=recv_accept_v4, 4=bind_v4, 5=bind_v6) near the connection_count_map declaration. - Clarify the IPv6 connection ID hash with a comment explaining that ctx->user_ip6[0] and ctx->user_ip6[3] are the first and last dwords of the IPv6 address (dthaler). Tests: - Refactor bind_helper_functions_validation_tcp_v4 / _v6 into a single parameterized bind_helper_functions_validation_test(ADDRESS_FAMILY) helper with two thin TEST_CASE wrappers, matching the existing helper_functions_validation_test pattern (dthaler). Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> * Clarify that bind hook fires on explicit and implicit binds shankarseal's PR review feedback noted that "bind() calls" is misleading since the cgroup/bind hook fires whenever a socket gets bound to a local address/port, including implicit binds triggered by connect()/listen()/ sendto() on an unbound socket (the ALE_RESOURCE_ASSIGNMENT WFP layer intercepts all of them). Update BindHook.md and ConnectAuthorizationAttachTypes.md to: - Use "bind operations" in prose (consistent with the header doc comment fixes in 6716dc7). - Explicitly call out both explicit and implicit bind in the "Hook Integration and Flow" section and the architecture diagram. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> * Use dual-stack AF_INET6 socket in bind helper validation test Per shankarseal's PR review feedback. Matches the dual-stack pattern used by helper_functions_validation_test (connect_authorization): one AF_INET6 socket with IPV6_V6ONLY=0, one sockaddr_in6 populated via IN6ADDR_SETV4MAPPED or IN6ADDR_SETLOOPBACK. The bind() + SAFE_REQUIRE moves outside the if/else, which now contains only the address and connection_id hash (per-family by necessity). Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> * Regenerate bpf2c expected files for cgroup_sock_addr_helpers The goldens were last regenerated in `ac9b46e69` ("Fix bpf2c expected output: regenerate with clang 18.1.8 matching CI"), but a later commit `a65aa6c21` ("Address PR review feedback for bind hook (#5272)") added a struct field in `tests/sample/cgroup_sock_addr_helpers.c` near line 37, shifting every function below it down by one line. The shifted `#line` directives in the goldens no longer matched bpf2c's output (golden expected `#line 53` but bpf2c emits `#line 54` for `test_sock_addr_helpers_v4`), causing `cgroup_sock_addr_helpers _test_mode::Verify` to fail. Regenerated via `scripts/generate_expected_bpf2c_output.ps1 .\x64\Debug\` to bring the goldens back in sync with the current sample source. Only the three `cgroup_sock_addr_helpers_{dll,raw,sys}.c` files changed; the other sample goldens are byte-identical to their prior state. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> * Address PR #5272 review feedback Bind multi-attach correctness and documented-contract enforcement: * Add bind-specific process_verdict that accumulates the most-restrictive verdict and short-circuits on REJECT (fixes [REJECT, PROCEED_SOFT] incorrectly permitting the bind under multi-attach). * Normalize unknown sock_addr return values to REJECT in both the bind and shared (connect / auth_connect) verdict paths per ebpf_nethooks.h. * NULL-safe `bpf_get_current_logon_id` / `bpf_is_current_admin` for system binds with no user token. Helper test coverage at the bind attach point: * Exercise all bind-supported helpers from the v4/v6 bind sample and assert per-helper return values + the existing connection_count_map updates. * Add unknown-verdict regression tests for bind and connect_authorization. Docs: * BindHook.md: note address/port modification divergence from Linux; document socket_cookie / logon_id / is_admin behavior at the bind layer. * ebpf_nethooks.h: mark next_hop_interface_luid / sub_interface_index as unspecified at BIND. Regenerate cgroup_sock_addr_helpers bpf2c expected files. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> * Fix brace-elision bug in connection_test_connect_authorization_unknown_verdict The single-program initializer was using a two-program brace pattern, leaving program_spec.attach_type uninitialized and the program name truncated. Validated on the test VM: the 4 affected cases now pass. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> * bpf_is_current_admin: return -1 when admin status cannot be determined Per the helper's documented contract in include/bpf_helper_defs.h (<0 = error, 0 = not admin, 1 = admin), returning 0 for a missing user token incorrectly indicates `not admin`. Return -1 (error) instead and update BindHook.md helper table. Addresses #5272 (comment) Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> --------- Co-authored-by: Michael Agun <danielagun@microsoft.com> Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> * Add listen hook implementation to sock_addr (#5268) * Add listen hook to sock_addr: WFP callout, classify, types, and registration Add support for eBPF programs that hook into listen() socket calls via WFP ALE_AUTH_LISTEN_V4/V6 layers, enabling security/observability for listen operations. Closes #4480. - Define EBPF_ATTACH_TYPE_CGROUP_INET4/6_LISTEN GUIDs and BPF_CGROUP_INET4/6_LISTEN enum values. - Add EBPF_HOOK_ALE_AUTH_LISTEN_V4/V6 hook IDs. - Register WFP callouts for ALE_AUTH_LISTEN_V4/V6 layers. - Implement net_ebpf_extension_sock_addr_authorize_listen_classify() with permit/block/hard-permit verdict handling. - Add _net_ebpf_extension_sock_addr_copy_wfp_listen_fields() for extracting listen-specific WFP fields (local address/port only). - Add cgroup/listen4 and cgroup/listen6 section definitions. - Wire up _does_attach_type_support_attachable_fd() for listen types. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> * Expose interface_type/tunnel_type WFP fields for listen hooks Add FWPS_FIELD_ALE_AUTH_LISTEN_V4/V6_INTERFACE_TYPE and _TUNNEL_TYPE to the wfp_connection_fields[] table and populate them in _net_ebpf_extension_sock_addr_copy_wfp_listen_fields(). Extend the bpf_sock_addr_get_network_context helper to accept listen hook IDs so BPF programs at the listen attach point can retrieve interface_type and tunnel_type via the versioned helper struct. Update documentation comments on bpf_sock_addr_network_context_t and the get_network_context helper to list LISTEN as a supported attach type. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> * Add verbose log for missing USER_ID in listen hook Add a VERBOSE-level trace log when the USER_ID field is not present in the listen classify callback, matching the existing pattern for PROCESS_ID. Addresses review feedback from FetoiuCatalin. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> * Document why next_hop_interface_luid/sub_interface_index are unspecified at listen Expand the inline comment on the NET_IF*_UNSPECIFIED assignments in _net_ebpf_extension_sock_addr_copy_wfp_listen_fields to explain that the ALE_AUTH_LISTEN WFP layer has no next-hop concept and does not expose a SUB_INTERFACE_INDEX field (unlike ALE_AUTH_CONNECT/RECV_ACCEPT), and to point readers at sock_addr_ctx->base.interface_luid (populated from FWPS_FIELD_ALE_AUTH_LISTEN_V*_IP_LOCAL_INTERFACE) for the local interface LUID, which is exposed to BPF programs via bpf_sock_addr_t::interface_luid. Addresses review feedback from FetoiuCatalin and keith-horton on PR #5268. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> * Add driver-level socket tests for listen hook Move driver-level integration tests for the new listen hook from the follow-up test PR into the implementation PR so the implementation ships with end-to-end test coverage. In-process tests (netebpfext_unit, netebpfext_fuzzer) and the supporting usersim submodule bump remain on the follow-up PR. - Add `authorize_listen4` / `authorize_listen6` programs. - Add `listen_connection_policy_map` for policy-based listen filtering. - Add a listen network-context helper sample. - Regenerate the bpf2c golden outputs (`_dll.c`, `_raw.c`, `_sys.c` for both `cgroup_sock_addr` and `cgroup_sock_addr_helpers`) to match the updated samples. Generated with clang 18.1.8 to match the CI configuration. Three driver-level test cases via the `execute_connection_test` framework (test infra is already on main from #5267): - `listen_hook_enforcement_framework` — exercises the native `ebpf_program_attach` path with INADDR_ANY bind (zero `local_ip` branch of `_copy_wfp_listen_fields`). - `listen_enforcement_libbpf` — exercises the libbpf-compat `bpf_prog_attach` path with LOOPBACK bind (non-zero `local_ip` branch of `_copy_wfp_listen_fields`). - `listen_hard_permit_verdict` — verifies the hard-permit verdict short-circuits subsequent layer evaluation. Each test covers IPv4 and IPv6, block and allow verdicts, and asserts the exact `WSAEACCES` error code on block. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> * Move user-mode test updates for listen sections to listen impl PR Updates the netsh `show sections` golden output and the libbpf JIT test map-iteration assertions to include the new `listen_connection_policy_map` and the cgroup/listen4 + cgroup/listen6 attach types. These test updates were previously held back on the listen-tests PR (#5269), but the production code that emits the new netsh sections and the new map ships on this PR. Without these matching test changes, the CI runs on this PR fail at: tests/end_to_end/netsh_test.cpp(440): REQUIRE(output_matches) tests/unit/libbpf_test_jit.cpp(462): REQUIRE(map == nullptr) tests/unit/libbpf_test_jit.cpp(544): REQUIRE(map == nullptr) Moving the matching test deltas with the impl keeps CI green here. The remaining in-process tests, fuzzer additions, and usersim bump stay on #5269. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> * Defensively null-check IP_LOCAL_INTERFACE in listen field copy Mirror the bind hook's null-check pattern when copying the LUID at the ALE_AUTH_LISTEN_V4/V6 layer. If WFP does not provide the LUID (the `value.uint64` pointer is NULL), default to 0 (NET_IFLUID_UNSPECIFIED) instead of dereferencing the NULL pointer. Official documentation does not state whether IP_LOCAL_INTERFACE can be NULL at AUTH_LISTEN, so defensive consistency with the bind hook is the safer choice. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> * Test all netebpfext sock_addr helpers at the listen attach point Extends test_listen_helpers_v4/v6 in cgroup_sock_addr_helpers.c to call all five additional sock_addr helpers and write the captured values to bind_helper_results_map (keys 8 and 9 for v4/v6 respectively). Extends listen_helper_functions_validation_tcp_v4/v6 in socket_tests.cpp to read the results back and assert the listen-appropriate value for each helper: - bpf_get_current_pid_tgid: upper 32 bits == GetCurrentProcessId() - bpf_get_current_logon_id: non-zero - bpf_is_current_admin: 0 or 1 (not -1) - bpf_sock_addr_set_redirect_context: -1 (explicit rejection at listen) - bpf_get_socket_cookie: 0 (transport_endpoint_handle zeroed at listen) Mirrors the bind helper validation pattern introduced in PR #5272. Addresses review feedback on PR #5268 (thread r3376380448) asking whether other sock_addr helpers apply to listen and whether they need test coverage. The five helpers all behave correctly at listen without any implementation change: four work as expected (the listen classify callback populates process_id, access_information, and the network context fields), while set_redirect_context is explicitly rejected and socket_cookie returns 0 because the WFP transport endpoint is not allocated/exposed at the ALE_AUTH_LISTEN layer. Regenerates the cgroup_sock_addr_helpers bpf2c expected files to reflect the new helper calls in the sample program. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> --------- Co-authored-by: Michael Agun <danielagun@microsoft.com> Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> --------- Co-authored-by: copilot-swe-agent[bot] <198982749+Copilot@users.noreply.github.com> Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com> Co-authored-by: D. Michael Agun <mikeagun@gmail.com> Co-authored-by: Michael Agun <danielagun@microsoft.com> Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
1 parent aad8c3a commit 0cc5c66

40 files changed

Lines changed: 13857 additions & 2092 deletions

docs/BindHook.md

Lines changed: 311 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,311 @@
1+
# eBPF for Windows sock_addr Bind Hook
2+
3+
## Contents
4+
5+
- [eBPF for Windows sock\_addr Bind Hook](#ebpf-for-windows-sock_addr-bind-hook)
6+
- [Contents](#contents)
7+
- [Purpose](#purpose)
8+
- [Relationship to the Legacy Bind Hook](#relationship-to-the-legacy-bind-hook)
9+
- [Design Rationale](#design-rationale)
10+
- [eBPF Design](#ebpf-design)
11+
- [Program Type](#program-type)
12+
- [Attach Types](#attach-types)
13+
- [Context Structure](#context-structure)
14+
- [Return Values](#return-values)
15+
- [Architecture](#architecture)
16+
- [Hook Integration and Flow](#hook-integration-and-flow)
17+
- [WFP Layer Integration](#wfp-layer-integration)
18+
- [Coexistence with the Legacy Bind Hook](#coexistence-with-the-legacy-bind-hook)
19+
- [Helper Function Support](#helper-function-support)
20+
- [Example eBPF Program](#example-ebpf-program)
21+
- [Linux Compatibility](#linux-compatibility)
22+
- [Aligned](#aligned)
23+
- [Divergences](#divergences)
24+
25+
---
26+
27+
## Purpose
28+
29+
Provide a cross-platform eBPF interface for intercepting socket bind operations on
30+
Windows that mirrors Linux's `BPF_PROG_TYPE_CGROUP_SOCK_ADDR` with `BPF_CGROUP_INET4_BIND`
31+
/ `BPF_CGROUP_INET6_BIND` attach types. This enables eBPF programs written for Linux
32+
(using `cgroup/bind4` / `cgroup/bind6` ELF section names and the `bpf_sock_addr` context)
33+
to run on Windows without source modifications.
34+
35+
Programs attached to these hooks can:
36+
37+
- Inspect the local address, port, protocol, compartment, and interface for each bind
38+
- Allow or deny the bind operation
39+
- Retrieve process and user information via helper functions
40+
41+
> **Note:** Unlike Linux's `cgroup/bind4` / `cgroup/bind6` hooks, the Windows bind hook
42+
> does **not** support modifying the local bind address or port from the BPF program —
43+
> writes to `user_ip*` / `user_port` are silently ignored. See [Divergences](#divergences)
44+
> for the full list.
45+
46+
This addresses [issue #333](https://github.com/microsoft/ebpf-for-windows/issues/333) and
47+
the multi-attach requirement from [issue #5180](https://github.com/microsoft/ebpf-for-windows/issues/5180).
48+
49+
## Relationship to the Legacy Bind Hook
50+
51+
eBPF for Windows continues to support the legacy `EBPF_PROGRAM_TYPE_BIND` /
52+
`EBPF_ATTACH_TYPE_BIND` hook with the Windows-specific `bind_md_t` context. Existing
53+
programs that use the legacy hook continue to work without changes — the legacy hook
54+
and the new sock_addr-aligned bind hook coexist at the same WFP layers.
55+
56+
| Aspect | Legacy bind hook | New sock_addr bind hook |
57+
|---|---|---|
58+
| Program type | `BPF_PROG_TYPE_BIND` | `BPF_PROG_TYPE_CGROUP_SOCK_ADDR` |
59+
| Attach type | `BPF_ATTACH_TYPE_BIND` (one) | `BPF_CGROUP_INET4_BIND` / `BPF_CGROUP_INET6_BIND` |
60+
| Context | `bind_md_t` (Windows-specific) | `bpf_sock_addr_t` (Linux-compatible) |
61+
| ELF section | n/a (Windows-only) | `cgroup/bind4` / `cgroup/bind6` |
62+
| Multi-attach | No (single attach) | Yes (`MULTI_ATTACH_WITH_WILDCARD`) |
63+
| Verdicts | `bind_action_t` (PERMIT_SOFT/HARD, DENY, REDIRECT) | `ebpf_sock_addr_verdict_t` (PROCEED_SOFT/HARD, REJECT) |
64+
| Address modification | Declared via `BIND_REDIRECT` but not actually enforced by WFP | Not supported |
65+
| Release / unbind notifications | Yes (`BIND_OPERATION_UNBIND`) | No |
66+
67+
Choose the legacy hook when you need release/unbind notifications or are extending an
68+
existing Windows-specific program. Choose the new sock_addr-aligned hook for any new
69+
work, especially when cross-platform compatibility with Linux is desired.
70+
71+
## Design Rationale
72+
73+
Linux exposes bind operations through `BPF_PROG_TYPE_CGROUP_SOCK_ADDR` with
74+
`BPF_CGROUP_INET4_BIND` / `BPF_CGROUP_INET6_BIND` attach types. This work adds the
75+
matching attach types to the existing `BPF_PROG_TYPE_CGROUP_SOCK_ADDR` implementation
76+
on Windows, following the same pattern used for connect, recv_accept, and
77+
connect_authorization.
78+
79+
## eBPF Design
80+
81+
### Program Type
82+
83+
The bind hook uses the existing **`BPF_PROG_TYPE_CGROUP_SOCK_ADDR`** program type.
84+
85+
### Attach Types
86+
87+
Two attach types are added to the `bpf_attach_type` enum:
88+
89+
```c
90+
// Added to bpf_attach_type enum:
91+
BPF_CGROUP_INET4_BIND, ///< IPv4 socket bind operations
92+
BPF_CGROUP_INET6_BIND, ///< IPv6 socket bind operations
93+
```
94+
95+
Each attach type has a corresponding GUID identifier:
96+
97+
```c
98+
#define EBPF_ATTACH_TYPE_CGROUP_INET4_BIND_GUID \
99+
{ \
100+
0x0d7ce21a, 0x7773, 0x405c, { 0x93, 0xb6, 0xd5, 0xbf, 0xb9, 0x2e, 0x74, 0xbc } \
101+
}
102+
__declspec(selectany) ebpf_attach_type_t EBPF_ATTACH_TYPE_CGROUP_INET4_BIND =
103+
EBPF_ATTACH_TYPE_CGROUP_INET4_BIND_GUID;
104+
105+
#define EBPF_ATTACH_TYPE_CGROUP_INET6_BIND_GUID \
106+
{ \
107+
0x81de64c0, 0x2973, 0x468d, { 0x83, 0x82, 0x67, 0x69, 0xf0, 0x33, 0xd7, 0x59 } \
108+
}
109+
__declspec(selectany) ebpf_attach_type_t EBPF_ATTACH_TYPE_CGROUP_INET6_BIND =
110+
EBPF_ATTACH_TYPE_CGROUP_INET6_BIND_GUID;
111+
```
112+
113+
Programs are loaded by section name using the standard Linux conventions:
114+
115+
- `SEC("cgroup/bind4")` → `BPF_CGROUP_INET4_BIND`
116+
- `SEC("cgroup/bind6")` → `BPF_CGROUP_INET6_BIND`
117+
118+
### Context Structure
119+
120+
The bind hook uses the existing `bpf_sock_addr_t` context (unchanged):
121+
122+
```c
123+
typedef struct bpf_sock_addr
124+
{
125+
uint32_t family; ///< IP address family (AF_INET or AF_INET6).
126+
struct {
127+
union { uint32_t msg_src_ip4; uint32_t msg_src_ip6[4]; };
128+
uint16_t msg_src_port;
129+
};
130+
struct {
131+
union { uint32_t user_ip4; uint32_t user_ip6[4]; };
132+
uint16_t user_port;
133+
};
134+
uint32_t protocol;
135+
uint32_t compartment_id;
136+
uint64_t interface_luid;
137+
} bpf_sock_addr_t;
138+
```
139+
140+
For bind operations, fields are populated as follows:
141+
142+
| Field | Value for bind |
143+
|---|---|
144+
| `family` | `AF_INET` (v4) or `AF_INET6` (v6) |
145+
| `user_ip4` / `user_ip6` | Local IP being bound to (network byte order) |
146+
| `user_port` | Local port being bound to (network byte order) |
147+
| `msg_src_ip4` / `msg_src_ip6` | `0` — no remote endpoint at bind layer |
148+
| `msg_src_port` | `0` — no remote endpoint at bind layer |
149+
| `protocol` | IP protocol (e.g., `IPPROTO_TCP`, `IPPROTO_UDP`) |
150+
| `compartment_id` | Network compartment ID |
151+
| `interface_luid` | Local interface LUID |
152+
153+
### Return Values
154+
155+
The bind hook uses the existing `ebpf_sock_addr_verdict_t` return values:
156+
157+
```c
158+
typedef enum _ebpf_sock_addr_verdict
159+
{
160+
BPF_SOCK_ADDR_VERDICT_REJECT = 0, ///< Block the bind operation.
161+
BPF_SOCK_ADDR_VERDICT_PROCEED_SOFT = 1, ///< Allow with soft permit (can be overridden).
162+
BPF_SOCK_ADDR_VERDICT_PROCEED_HARD = 2 ///< Allow with hard permit (cannot be overridden).
163+
} ebpf_sock_addr_verdict_t;
164+
```
165+
166+
When multiple bind programs are attached, the verdicts are combined: if any program
167+
rejects, the bind is blocked.
168+
169+
## Architecture
170+
171+
### Hook Integration and Flow
172+
173+
1. **Application bind request**: A socket is bound to a local address/port. This
174+
can happen explicitly via `bind()`, or implicitly when an unbound socket is
175+
used with `connect()`, `listen()`, `sendto()`, etc.
176+
2. **WFP interception**: WFP `ALE_RESOURCE_ASSIGNMENT` layer intercepts the operation
177+
3. **eBPF invocation**: Registered eBPF programs are invoked with the `bpf_sock_addr_t` context
178+
4. **Program execution**: Each program inspects the context and returns a verdict
179+
5. **Action processing**: WFP processes the verdict and allows or blocks the bind
180+
181+
```
182+
Application
183+
|
184+
| bind (explicit via bind(), or implicit
185+
| via connect()/listen()/sendto()/...)
186+
v
187+
Windows Socket Layer
188+
|
189+
| WFP ALE_RESOURCE_ASSIGNMENT callout
190+
v
191+
sock_addr Bind Hook (this hook) Legacy Bind Hook (independent)
192+
| |
193+
| bpf_sock_addr_t context | bind_md_t context
194+
v v
195+
CGROUP_SOCK_ADDR eBPF Program(s) EBPF_PROGRAM_TYPE_BIND program (single)
196+
| |
197+
| BPF_SOCK_ADDR_VERDICT_* | bind_action_t
198+
v v
199+
WFP Action Processing (both verdicts must allow for bind to succeed)
200+
|
201+
| Allow / Block
202+
v
203+
Socket Operation Result
204+
```
205+
206+
### WFP Layer Integration
207+
208+
The sock_addr bind hook integrates with the WFP ALE Resource Assignment layers:
209+
210+
- **`FWPM_LAYER_ALE_RESOURCE_ASSIGNMENT_V4`** — IPv4 bind operations
211+
- **`FWPM_LAYER_ALE_RESOURCE_ASSIGNMENT_V6`** — IPv6 bind operations
212+
213+
### Coexistence with the Legacy Bind Hook
214+
215+
Both hooks register filters at the same `ALE_RESOURCE_ASSIGNMENT` layers but use
216+
distinct WFP callout GUIDs, so WFP invokes each filter's bound callout independently.
217+
If both a legacy bind program and a sock_addr bind program are attached, both run for
218+
each bind, and the bind succeeds only if both allow it.
219+
220+
## Helper Function Support
221+
222+
Bind programs can use the following helpers in addition to standard map and
223+
control-flow helpers:
224+
225+
| Helper | Behavior at bind |
226+
|---|---|
227+
| `bpf_get_current_pid_tgid` | Returns the bind caller's PID/TID |
228+
| `bpf_get_current_logon_id` | Returns the logon session ID; returns `0` when the bind has no associated user token (e.g. some system binds) |
229+
| `bpf_is_current_admin` | Returns `1` if the caller has Administrator privileges, `0` otherwise; returns `-1` when no user token is available (e.g. some system binds) |
230+
| `bpf_get_socket_cookie` | Returns `0` at the bind layer — the WFP transport endpoint is not yet allocated at `ALE_RESOURCE_ASSIGNMENT`, so no cookie is available |
231+
| `bpf_sock_addr_get_network_context` | Returns interface metadata; `interface_type` and `tunnel_type` are available; `next_hop_interface_luid` and `sub_interface_index` are not available at the bind layer and are returned as their unspecified defaults |
232+
| `bpf_sock_addr_set_redirect_context` | **Not supported** at the bind layer — returns `-1` |
233+
234+
## Example eBPF Program
235+
236+
```c
237+
// SPDX-License-Identifier: MIT
238+
#include "bpf_helpers.h"
239+
#include "ebpf_nethooks.h"
240+
241+
typedef struct _bind_deny_key
242+
{
243+
uint16_t port;
244+
uint8_t protocol;
245+
uint8_t pad;
246+
} bind_deny_key_t;
247+
248+
struct
249+
{
250+
__uint(type, BPF_MAP_TYPE_HASH);
251+
__type(key, bind_deny_key_t);
252+
__type(value, uint32_t);
253+
__uint(max_entries, 256);
254+
} bind_deny_map SEC(".maps");
255+
256+
__inline int
257+
authorize_bind(bpf_sock_addr_t* ctx)
258+
{
259+
bind_deny_key_t key = {0};
260+
key.port = (uint16_t)ctx->user_port;
261+
key.protocol = (uint8_t)ctx->protocol;
262+
263+
uint32_t* deny = bpf_map_lookup_elem(&bind_deny_map, &key);
264+
if (deny != NULL && *deny != 0) {
265+
return BPF_SOCK_ADDR_VERDICT_REJECT;
266+
}
267+
return BPF_SOCK_ADDR_VERDICT_PROCEED_SOFT;
268+
}
269+
270+
SEC("cgroup/bind4")
271+
int authorize_bind4(bpf_sock_addr_t* ctx) { return authorize_bind(ctx); }
272+
273+
SEC("cgroup/bind6")
274+
int authorize_bind6(bpf_sock_addr_t* ctx) { return authorize_bind(ctx); }
275+
```
276+
277+
A complete working sample is available at `tests/sample/cgroup_sock_addr_bind.c`.
278+
279+
## Linux Compatibility
280+
281+
This section documents alignment with and divergences from the Linux
282+
`BPF_CGROUP_INET4_BIND` / `BPF_CGROUP_INET6_BIND` implementation.
283+
284+
### Aligned
285+
286+
| Aspect | Details |
287+
|---|---|
288+
| Program type | `BPF_PROG_TYPE_CGROUP_SOCK_ADDR` (same as Linux) |
289+
| Attach type names | `BPF_CGROUP_INET4_BIND` / `BPF_CGROUP_INET6_BIND` (same as Linux) |
290+
| Context structure | `bpf_sock_addr` / `bpf_sock_addr_t` with same field names |
291+
| Field semantics | `user_*` contains the local bind address/port (matches Linux) |
292+
| ELF section names | `cgroup/bind4` / `cgroup/bind6` |
293+
294+
### Divergences
295+
296+
The following divergences are pre-existing characteristics of the Windows `bpf_sock_addr`
297+
shared by all sock_addr hooks; they are not specific to the bind hook.
298+
299+
| Aspect | Linux | Windows | Reason |
300+
|---|---|---|---|
301+
| Port field type | `__u32` | `uint16_t` | Pre-existing Windows design choice |
302+
| `type` field | Present (`SOCK_STREAM`, etc.) | Absent | Not available from WFP |
303+
| `sk` field | Present (socket pointer) | Absent | Kernel-internal, not exposed |
304+
| `compartment_id` | Absent | Present | Windows networking concept |
305+
| `interface_luid` | Absent | Present | Windows networking concept |
306+
| `msg_src_*` for bind | Mirrors local address | Zero | Bind has no remote endpoint; zeroing is unambiguous |
307+
| Return values | `0` (deny) / `1` (allow) | 3-value verdict enum (`PROCEED_SOFT`, `PROCEED_HARD`, `REJECT`) | Pre-existing Windows design choice |
308+
| Address rewriting | Supported (program may modify `user_*`) | Not supported (modifications are silently ignored) | WFP `ALE_RESOURCE_ASSIGNMENT` rewrite not implemented |
309+
310+
Programs that access `user_ip4`, `user_port`, `family`, and `protocol` and return only
311+
allow/deny verdicts are portable between Linux and Windows bind hooks.

docs/ConnectAuthorizationAttachTypes.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ eBPF for Windows already supports several attach types for socket address operat
1010

1111
- `BPF_CGROUP_INET4_CONNECT` / `BPF_CGROUP_INET6_CONNECT` - Inspect outbound connections, with the capability to modify the destination address or port (redirection). Invoked at the redirect layer before route selection.
1212
- `BPF_CGROUP_INET4_RECV_ACCEPT` / `BPF_CGROUP_INET6_RECV_ACCEPT` - Inspect inbound connections on receive/accept and allow or reject them.
13+
- `BPF_CGROUP_INET4_BIND` / `BPF_CGROUP_INET6_BIND` - Inspect socket bind operations (explicit `bind()` or implicit binds from `connect()`/`listen()`/etc.) and allow or reject them. See [BindHook.md](BindHook.md).
1314

1415
However, there was a gap in functionality that required the addition of new attach points.
1516

docs/ListenHook.md

Lines changed: 10 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -63,6 +63,7 @@ while being implemented via the Windows Filtering Platform (WFP) ALE Authorizati
6363
This design adds new listen attach types to the existing `BPF_PROG_TYPE_CGROUP_SOCK_ADDR` program type, following the same pattern used for:
6464
- `BPF_CGROUP_INET4_CONNECT` / `BPF_CGROUP_INET6_CONNECT`
6565
- `BPF_CGROUP_INET4_RECV_ACCEPT` / `BPF_CGROUP_INET6_RECV_ACCEPT`
66+
- `BPF_CGROUP_INET4_BIND` / `BPF_CGROUP_INET6_BIND` (see [BindHook.md](BindHook.md))
6667

6768
Key considerations:
6869
- Reusing the existing program type and context structure provides consistency and reduces implementation complexity
@@ -79,6 +80,7 @@ The listen hook extends the existing `BPF_PROG_TYPE_CGROUP_SOCK_ADDR` program ty
7980
The listen hook reuses the existing **BPF_PROG_TYPE_CGROUP_SOCK_ADDR** program type, which is already used for:
8081
- `BPF_CGROUP_INET4_CONNECT` / `BPF_CGROUP_INET6_CONNECT`
8182
- `BPF_CGROUP_INET4_RECV_ACCEPT` / `BPF_CGROUP_INET6_RECV_ACCEPT`
83+
- `BPF_CGROUP_INET4_BIND` / `BPF_CGROUP_INET6_BIND`
8284

8385
This provides consistency with existing socket address hooks and allows programs to share helper functions and infrastructure.
8486

@@ -226,10 +228,13 @@ The following WFP fields from the ALE_AUTH_LISTEN layers are made available to e
226228
|---------------------|-------------|---------------|
227229
| FWPM_CONDITION_IP_LOCAL_ADDRESS | Local IP address | msg_src_ip4/msg_src_ip6 and user_ip4/user_ip6 |
228230
| FWPM_CONDITION_IP_LOCAL_PORT | Local port | msg_src_port and user_port |
229-
| FWPM_CONDITION_IP_PROTOCOL | IP protocol | protocol |
231+
| *(not available)* | IP protocol | protocol (hardcoded to `IPPROTO_TCP`) |
230232
| FWPM_CONDITION_COMPARTMENT_ID | Compartment ID | compartment_id |
231233
| FWPM_CONDITION_IP_LOCAL_INTERFACE | Interface LUID | interface_luid |
232234

235+
> **Note:** The WFP ALE_AUTH_LISTEN layers do not expose an `IP_PROTOCOL` field.
236+
> Since listen operations are TCP-only, the `protocol` field is hardcoded to `IPPROTO_TCP`.
237+
233238
Process ID can be obtained via the `bpf_get_current_pid_tgid()` helper function.
234239

235240
### Filter Configuration
@@ -242,17 +247,17 @@ net_ebpf_extension_wfp_filter_parameters_t _cgroup_inet4_listen_filter_parameter
242247
{&FWPM_LAYER_ALE_AUTH_LISTEN_V4,
243248
NULL, // Default sublayer
244249
&EBPF_HOOK_ALE_AUTH_LISTEN_V4_CALLOUT,
245-
L"net eBPF listen hook",
246-
L"net eBPF listen hook WFP filter"}
250+
L"net eBPF sock_addr listen hook",
251+
L"net eBPF sock_addr listen hook WFP filter"}
247252
};
248253

249254
// IPv6 Listen Hook Filter Configuration
250255
net_ebpf_extension_wfp_filter_parameters_t _cgroup_inet6_listen_filter_parameters[] = {
251256
{&FWPM_LAYER_ALE_AUTH_LISTEN_V6,
252257
NULL, // Default sublayer
253258
&EBPF_HOOK_ALE_AUTH_LISTEN_V6_CALLOUT,
254-
L"net eBPF listen hook",
255-
L"net eBPF listen hook WFP filter"}
259+
L"net eBPF sock_addr listen hook",
260+
L"net eBPF sock_addr listen hook WFP filter"}
256261
};
257262
```
258263

external/ebpf-verifier

Submodule ebpf-verifier updated 81 files

0 commit comments

Comments
 (0)