Add netebpfext user mode tests (#2124)

* fix

* add more tests, address cr comments

* fix cicd

* fix cicd

* fix analyze build

* switch to read / write lock

* fix fuzzer

* add concurrent test

* fix

* cr comments

* add sock_addr concurrency tests

* add tests

* add tests

* fix, cleanup

* cr comments

* bugfix
This commit is contained in:
Anurag Saxena 2023-02-27 21:30:38 -08:00 коммит произвёл GitHub
Родитель 87a88593a4
Коммит 30f96a64aa
Не найден ключ, соответствующий данной подписи
Идентификатор ключа GPG: 4AEE18F83AFDEB23
10 изменённых файлов: 851 добавлений и 256 удалений

15
.github/workflows/cicd.yml поставляемый
Просмотреть файл

@ -121,6 +121,21 @@ jobs:
gather_dumps: true
capture_etw: true
netebpfext_unit_leak_detection:
# Always run this job.
needs: regular
if: github.event_name == 'schedule' || github.event_name == 'pull_request' || github.event_name == 'push' || github.event_name == 'merge_group'
uses: ./.github/workflows/reusable-test.yml
with:
name: netebpfext_unit_leak_detection
test_command: .\netebpfext_unit.exe -d yes
build_artifact: Build-x64
environment: windows-2022
code_coverage: true
gather_dumps: true
capture_etw: true
leak_detection: true
# Run the bpf2c tests in GitHub.
bpf2c:
# Always run this job.

Просмотреть файл

@ -25,6 +25,11 @@ bool _ebpf_platform_code_integrity_enabled = false;
// Permit the test to simulate non-preemptible execution.
bool _ebpf_platform_is_preemptible = true;
// Global variable to track the number of times ebpf_platform has been
// initialized. In user mode it is possible for ebpf_platform_{initiate|terminate}
// to be called multiple times.
int32_t _ebpf_platform_initiate_count = 0;
extern "C" bool ebpf_fuzzing_enabled = false;
extern "C" size_t ebpf_fuzzing_memory_limit = MAXSIZE_T;
@ -296,6 +301,11 @@ _get_environment_variable_as_size_t(const std::string& name)
_Must_inspect_result_ ebpf_result_t
ebpf_platform_initiate()
{
int32_t count = ebpf_interlocked_increment_int32(&_ebpf_platform_initiate_count);
if (count > 1) {
// Platform library already initialized, return.
return EBPF_SUCCESS;
}
try {
_ebpf_platform_maximum_group_count = GetMaximumProcessorGroupCount();
@ -336,6 +346,11 @@ ebpf_platform_initiate()
void
ebpf_platform_terminate()
{
int32_t count = ebpf_interlocked_decrement_int32(&_ebpf_platform_initiate_count);
if (count != 0) {
return;
}
_clean_up_thread_pool();
_ebpf_emulated_dpcs.resize(0);
if (_ebpf_leak_detector_ptr) {

Просмотреть файл

@ -171,11 +171,11 @@ _net_ebpf_extension_detach_client_completion(_In_ DEVICE_OBJECT* device_object,
// Wait for any in progress callbacks to complete.
_ebpf_ext_attach_wait_for_rundown(&hook_client->rundown);
IoFreeWorkItem(work_item);
// Note: This frees the provider binding context (hook_client).
NmrProviderDetachClientComplete(hook_client->nmr_binding_handle);
IoFreeWorkItem(work_item);
NET_EBPF_EXT_LOG_EXIT();
}

Просмотреть файл

@ -1067,8 +1067,11 @@ net_ebpf_extension_sock_addr_authorize_recv_accept_classify(
goto Exit;
}
if (net_ebpf_extension_hook_invoke_program(attached_client, sock_addr_ctx, &result) != EBPF_SUCCESS)
if (net_ebpf_extension_hook_invoke_program(attached_client, sock_addr_ctx, &result) != EBPF_SUCCESS) {
// Block the request if we failed to invoke the eBPF program.
classify_output->actionType = FWP_ACTION_BLOCK;
goto Exit;
}
classify_output->actionType = (result == BPF_SOCK_ADDR_VERDICT_PROCEED) ? FWP_ACTION_PERMIT : FWP_ACTION_BLOCK;
if (classify_output->actionType == FWP_ACTION_BLOCK)
@ -1304,7 +1307,7 @@ net_ebpf_extension_sock_addr_redirect_connection_classify(
net_ebpf_extension_connection_context_t* connection_context_original = NULL;
net_ebpf_extension_connection_context_t* connection_context_redirected = NULL;
BOOLEAN redirected = FALSE;
FWP_ACTION_TYPE action = FWP_ACTION_BLOCK;
FWP_ACTION_TYPE action = FWP_ACTION_PERMIT;
BOOLEAN classify_handle_acquired = FALSE;
BOOLEAN v4_mapped = FALSE;
BOOLEAN is_original_connection;

Просмотреть файл

@ -5,6 +5,8 @@
#include "net_ebpf_ext_sock_addr.h"
#include "netebpfext_platform.h"
thread_local static FWPS_CONNECT_REQUEST0* _fwp_um_connect_request = nullptr;
// 98849e12-b07d-11ec-9a30-18602489beee
DEFINE_GUID(
EBPF_HOOK_CGROUP_CONNECT_V4_SUBLAYER, 0x98849e12, 0xb07d, 0x11ec, 0x9a, 0x30, 0x18, 0x60, 0x24, 0x89, 0xbe, 0xee);
@ -24,12 +26,12 @@ std::unique_ptr<_fwp_engine> _fwp_engine::_engine;
FWP_ACTION_TYPE
_fwp_engine::classify_test_packet(_In_ const GUID* layer_guid, NET_IFINDEX if_index)
{
std::unique_lock l(lock);
const GUID* callout_key = get_callout_key_from_layer_guid(layer_guid);
shared_lock_t l(lock);
const GUID* callout_key = get_callout_key_from_layer_guid_under_lock(layer_guid);
if (callout_key == nullptr) {
return FWP_ACTION_CALLOUT_UNKNOWN;
}
const FWPS_CALLOUT3* callout = get_callout_from_key(callout_key);
const FWPS_CALLOUT3* callout = get_callout_from_key_under_lock(callout_key);
if (callout == nullptr) {
return FWP_ACTION_CALLOUT_UNKNOWN;
}
@ -38,7 +40,7 @@ _fwp_engine::classify_test_packet(_In_ const GUID* layer_guid, NET_IFINDEX if_in
FWPS_INCOMING_VALUES incoming_fixed_values = {.incomingValue = incoming_value};
incoming_fixed_values.incomingValue[FWPS_FIELD_INBOUND_MAC_FRAME_NATIVE_INTERFACE_INDEX].value.uint32 = if_index;
FWPS_INCOMING_METADATA_VALUES incoming_metadata_values = {};
const FWPM_FILTER* fwpm_filter = get_fwpm_filter_with_context(*layer_guid);
const FWPM_FILTER* fwpm_filter = get_fwpm_filter_with_context_under_lock(*layer_guid);
if (!fwpm_filter) {
return FWP_ACTION_CALLOUT_UNKNOWN;
}
@ -82,32 +84,16 @@ _fwp_engine::classify_test_packet(_In_ const GUID* layer_guid, NET_IFINDEX if_in
return result.actionType;
}
constexpr uint32_t _test_destination_ipv4_address = 0x01020304;
static FWP_BYTE_ARRAY16 _test_destination_ipv6_address = {1, 2, 3, 4};
constexpr uint16_t _test_destination_port = 1234;
constexpr uint32_t _test_source_ipv4_address = 0x05060708;
static FWP_BYTE_ARRAY16 _test_source_ipv6_address = {5, 6, 7, 8};
constexpr uint16_t _test_source_port = 5678;
constexpr uint8_t _test_protocol = IPPROTO_TCP;
constexpr uint32_t _test_compartment_id = 1;
static FWP_BYTE_BLOB _test_app_id = {.size = 2, .data = (uint8_t*)"\\"};
static uint64_t _test_interface_luid = 1;
static TOKEN_ACCESS_INFORMATION _test_token_access_information = {0};
static FWP_BYTE_BLOB _test_user_id = {
.size = (sizeof(TOKEN_ACCESS_INFORMATION)), .data = (uint8_t*)&_test_token_access_information};
// This is used to test the bind hook.
FWP_ACTION_TYPE
_fwp_engine::test_bind_ipv4()
_fwp_engine::test_bind_ipv4(_In_ fwp_classify_parameters_t* parameters)
{
std::unique_lock l(lock);
FWPS_INCOMING_VALUE0 incoming_value[FWPS_FIELD_ALE_RESOURCE_ASSIGNMENT_V4_MAX] = {};
incoming_value[FWPS_FIELD_ALE_RESOURCE_ASSIGNMENT_V4_IP_LOCAL_PORT].value.uint16 = _test_destination_port;
incoming_value[FWPS_FIELD_ALE_RESOURCE_ASSIGNMENT_V4_IP_LOCAL_PORT].value.uint16 = parameters->destination_port;
incoming_value[FWPS_FIELD_ALE_RESOURCE_ASSIGNMENT_V4_IP_LOCAL_ADDRESS].value.uint32 =
_test_destination_ipv4_address;
incoming_value[FWPS_FIELD_ALE_RESOURCE_ASSIGNMENT_V4_IP_PROTOCOL].value.uint8 = _test_protocol;
incoming_value[FWPS_FIELD_ALE_RESOURCE_ASSIGNMENT_V4_ALE_APP_ID].value.byteBlob = &_test_app_id;
parameters->destination_ipv4_address;
incoming_value[FWPS_FIELD_ALE_RESOURCE_ASSIGNMENT_V4_IP_PROTOCOL].value.uint8 = parameters->protocol;
incoming_value[FWPS_FIELD_ALE_RESOURCE_ASSIGNMENT_V4_ALE_APP_ID].value.byteBlob = &parameters->app_id;
return test_callout(
FWPS_LAYER_ALE_RESOURCE_ASSIGNMENT_V4,
@ -116,8 +102,7 @@ _fwp_engine::test_bind_ipv4()
incoming_value);
}
FWP_ACTION_TYPE
_fwp_engine::test_callout(
_Requires_lock_not_held_(this->lock) FWP_ACTION_TYPE _fwp_engine::test_callout(
uint16_t layer_id,
_In_ const GUID& layer_guid,
_In_ const GUID& sublayer_guid,
@ -125,20 +110,28 @@ _fwp_engine::test_callout(
{
FWPS_INCOMING_VALUES incoming_fixed_values = {.layerId = layer_id, .incomingValue = incoming_value};
FWPS_INCOMING_METADATA_VALUES incoming_metadata_values = {};
const FWPM_FILTER* fwpm_filter = get_fwpm_filter_with_context(layer_guid, sublayer_guid);
if (!fwpm_filter) {
return FWP_ACTION_CALLOUT_UNKNOWN;
}
FWPS_FILTER fwps_filter = {.context = fwpm_filter->rawContext};
FWPS_FILTER fwps_filter = {};
const FWPS_CALLOUT3* callout = nullptr;
const GUID* callout_key = get_callout_key_from_layer_guid(&layer_guid);
if (callout_key == nullptr) {
return FWP_ACTION_CALLOUT_UNKNOWN;
}
{
shared_lock_t l(lock);
const FWPM_FILTER* fwpm_filter = get_fwpm_filter_with_context_under_lock(layer_guid, sublayer_guid);
if (!fwpm_filter) {
return FWP_ACTION_CALLOUT_UNKNOWN;
}
fwps_filter.context = fwpm_filter->rawContext;
const FWPS_CALLOUT3* callout = get_callout_from_key(callout_key);
if (callout == nullptr) {
return FWP_ACTION_CALLOUT_UNKNOWN;
const GUID* callout_key = get_callout_key_from_layer_guid_under_lock(&layer_guid);
if (callout_key == nullptr) {
return FWP_ACTION_CALLOUT_UNKNOWN;
}
callout = get_callout_from_key_under_lock(callout_key);
if (callout == nullptr) {
return FWP_ACTION_CALLOUT_UNKNOWN;
}
incoming_metadata_values.flowHandle = next_flow_id++;
}
FWPS_CLASSIFY_OUT0 result = {};
@ -155,162 +148,248 @@ _fwp_engine::test_callout(
return result.actionType;
}
// This is used to test CGROUP_SOCK_ADDR hooks.
// This is used to test the INET4_RECV_ACCEPT hook.
FWP_ACTION_TYPE
_fwp_engine::test_cgroup_sock_addr(
uint16_t layer_id,
_In_ const GUID& layer_guid,
_In_ const GUID& sublayer_guid,
_In_ FWPS_INCOMING_VALUE0* incoming_value)
_fwp_engine::test_cgroup_inet4_recv_accept(_In_ fwp_classify_parameters_t* parameters)
{
GUID filter_key = {};
FWPS_FILTER filter = {};
net_ebpf_ext_connect_redirect_filter_change_notify(FWPS_CALLOUT_NOTIFY_ADD_FILTER, &filter_key, &filter);
FWP_ACTION_TYPE action_type = test_callout(layer_id, layer_guid, sublayer_guid, incoming_value);
net_ebpf_ext_connect_redirect_filter_change_notify(FWPS_CALLOUT_NOTIFY_DELETE_FILTER, &filter_key, &filter);
return action_type;
}
// This is used to test the INET4_RECV_ADDEPT hook.
FWP_ACTION_TYPE
_fwp_engine::test_cgroup_inet4_recv_accept()
{
std::unique_lock l(lock);
FWPS_INCOMING_VALUE0 incoming_value[FWPS_FIELD_ALE_AUTH_RECV_ACCEPT_V4_MAX] = {};
incoming_value[FWPS_FIELD_ALE_AUTH_RECV_ACCEPT_V4_IP_LOCAL_ADDRESS].value.uint32 = _test_destination_ipv4_address;
incoming_value[FWPS_FIELD_ALE_AUTH_RECV_ACCEPT_V4_IP_LOCAL_PORT].value.uint16 = _test_destination_port;
incoming_value[FWPS_FIELD_ALE_AUTH_RECV_ACCEPT_V4_IP_REMOTE_ADDRESS].value.uint32 = _test_source_ipv4_address;
incoming_value[FWPS_FIELD_ALE_AUTH_RECV_ACCEPT_V4_IP_REMOTE_PORT].value.uint16 = _test_source_port;
incoming_value[FWPS_FIELD_ALE_AUTH_RECV_ACCEPT_V4_IP_PROTOCOL].value.uint8 = _test_protocol;
incoming_value[FWPS_FIELD_ALE_AUTH_RECV_ACCEPT_V4_COMPARTMENT_ID].value.uint32 = _test_compartment_id;
incoming_value[FWPS_FIELD_ALE_AUTH_RECV_ACCEPT_V4_IP_LOCAL_INTERFACE].value.uint64 = &_test_interface_luid;
incoming_value[FWPS_FIELD_ALE_AUTH_RECV_ACCEPT_V4_ALE_APP_ID].value.byteBlob = &_test_app_id;
incoming_value[FWPS_FIELD_ALE_AUTH_RECV_ACCEPT_V4_ALE_USER_ID].value.byteBlob = &_test_user_id;
incoming_value[FWPS_FIELD_ALE_AUTH_RECV_ACCEPT_V4_IP_LOCAL_ADDRESS].value.uint32 =
parameters->destination_ipv4_address;
incoming_value[FWPS_FIELD_ALE_AUTH_RECV_ACCEPT_V4_IP_LOCAL_PORT].value.uint16 = parameters->destination_port;
incoming_value[FWPS_FIELD_ALE_AUTH_RECV_ACCEPT_V4_IP_REMOTE_ADDRESS].value.uint32 = parameters->source_ipv4_address;
incoming_value[FWPS_FIELD_ALE_AUTH_RECV_ACCEPT_V4_IP_REMOTE_PORT].value.uint16 = parameters->source_port;
incoming_value[FWPS_FIELD_ALE_AUTH_RECV_ACCEPT_V4_IP_PROTOCOL].value.uint8 = parameters->protocol;
incoming_value[FWPS_FIELD_ALE_AUTH_RECV_ACCEPT_V4_COMPARTMENT_ID].value.uint32 = parameters->compartment_id;
incoming_value[FWPS_FIELD_ALE_AUTH_RECV_ACCEPT_V4_IP_LOCAL_INTERFACE].value.uint64 =
const_cast<UINT64*>(&parameters->interface_luid);
incoming_value[FWPS_FIELD_ALE_AUTH_RECV_ACCEPT_V4_ALE_APP_ID].value.byteBlob =
const_cast<FWP_BYTE_BLOB*>(&parameters->app_id);
incoming_value[FWPS_FIELD_ALE_AUTH_RECV_ACCEPT_V4_ALE_USER_ID].value.byteBlob =
const_cast<FWP_BYTE_BLOB*>(&parameters->user_id);
return test_cgroup_sock_addr(
return test_callout(
FWPS_LAYER_ALE_AUTH_RECV_ACCEPT_V4, FWPM_LAYER_ALE_AUTH_RECV_ACCEPT_V4, EBPF_DEFAULT_SUBLAYER, incoming_value);
}
// This is used to test the INET6_RECV_ADDEPT hook.
// This is used to test the INET6_RECV_ACCEPT hook.
FWP_ACTION_TYPE
_fwp_engine::test_cgroup_inet6_recv_accept()
_fwp_engine::test_cgroup_inet6_recv_accept(_In_ fwp_classify_parameters_t* parameters)
{
std::unique_lock l(lock);
FWPS_INCOMING_VALUE0 incoming_value[FWPS_FIELD_ALE_AUTH_RECV_ACCEPT_V6_MAX] = {};
incoming_value[FWPS_FIELD_ALE_AUTH_RECV_ACCEPT_V6_IP_LOCAL_ADDRESS].value.byteArray16 =
&_test_destination_ipv6_address;
incoming_value[FWPS_FIELD_ALE_AUTH_RECV_ACCEPT_V6_IP_LOCAL_PORT].value.uint16 = _test_destination_port;
incoming_value[FWPS_FIELD_ALE_AUTH_RECV_ACCEPT_V6_IP_REMOTE_ADDRESS].value.byteArray16 = &_test_source_ipv6_address;
incoming_value[FWPS_FIELD_ALE_AUTH_RECV_ACCEPT_V6_IP_REMOTE_PORT].value.uint16 = _test_source_port;
incoming_value[FWPS_FIELD_ALE_AUTH_RECV_ACCEPT_V6_IP_PROTOCOL].value.uint8 = _test_protocol;
incoming_value[FWPS_FIELD_ALE_AUTH_RECV_ACCEPT_V6_COMPARTMENT_ID].value.uint32 = _test_compartment_id;
incoming_value[FWPS_FIELD_ALE_AUTH_RECV_ACCEPT_V6_IP_LOCAL_INTERFACE].value.uint64 = &_test_interface_luid;
incoming_value[FWPS_FIELD_ALE_AUTH_RECV_ACCEPT_V6_ALE_APP_ID].value.byteBlob = &_test_app_id;
incoming_value[FWPS_FIELD_ALE_AUTH_RECV_ACCEPT_V6_ALE_USER_ID].value.byteBlob = &_test_user_id;
&parameters->destination_ipv6_address;
incoming_value[FWPS_FIELD_ALE_AUTH_RECV_ACCEPT_V6_IP_LOCAL_PORT].value.uint16 = parameters->destination_port;
incoming_value[FWPS_FIELD_ALE_AUTH_RECV_ACCEPT_V6_IP_REMOTE_ADDRESS].value.byteArray16 =
&parameters->source_ipv6_address;
incoming_value[FWPS_FIELD_ALE_AUTH_RECV_ACCEPT_V6_IP_REMOTE_PORT].value.uint16 = parameters->source_port;
incoming_value[FWPS_FIELD_ALE_AUTH_RECV_ACCEPT_V6_IP_PROTOCOL].value.uint8 = parameters->protocol;
incoming_value[FWPS_FIELD_ALE_AUTH_RECV_ACCEPT_V6_COMPARTMENT_ID].value.uint32 = parameters->compartment_id;
incoming_value[FWPS_FIELD_ALE_AUTH_RECV_ACCEPT_V6_IP_LOCAL_INTERFACE].value.uint64 = &parameters->interface_luid;
incoming_value[FWPS_FIELD_ALE_AUTH_RECV_ACCEPT_V6_ALE_APP_ID].value.byteBlob = &parameters->app_id;
incoming_value[FWPS_FIELD_ALE_AUTH_RECV_ACCEPT_V6_ALE_USER_ID].value.byteBlob = &parameters->user_id;
return test_cgroup_sock_addr(
return test_callout(
FWPS_LAYER_ALE_AUTH_RECV_ACCEPT_V6, FWPM_LAYER_ALE_AUTH_RECV_ACCEPT_V6, EBPF_DEFAULT_SUBLAYER, incoming_value);
}
bool
_is_connection_redirected(
_In_ const fwp_classify_parameters_t* parameters,
_In_ const FWPS_CONNECT_REQUEST0* request,
_Out_ uint16_t* redirected_port,
_Out_ uint8_t** redirected_address)
{
// Test ebpf program modifies both port and IP address. Only check the port
// to know if redirection happened.
*redirected_port = INETADDR_PORT((PSOCKADDR)&request->remoteAddressAndPort);
*redirected_address = INETADDR_ADDRESS((PSOCKADDR)&request->remoteAddressAndPort);
if (parameters->destination_port == *redirected_port) {
return false;
}
if (request->remoteAddressAndPort.ss_family == AF_INET) {
if (parameters->destination_ipv4_address == *((uint32_t*)*redirected_address)) {
return false;
}
} else if (memcmp(*redirected_address, parameters->destination_ipv6_address.byteArray16, 16) == 0) {
return false;
}
return true;
}
// Allocate and initialize FWPS_CONNECT_REQUEST0.
void static _allocate_and_initialize_connection_request(
ADDRESS_FAMILY family, _In_ const fwp_classify_parameters_t* parameters)
{
ebpf_assert(_fwp_um_connect_request == nullptr);
_fwp_um_connect_request = (FWPS_CONNECT_REQUEST0*)ebpf_allocate(sizeof(FWPS_CONNECT_REQUEST0));
if (_fwp_um_connect_request == nullptr) {
// Most likely we are under low memory simulation. Return.
return;
}
_fwp_um_connect_request->remoteAddressAndPort.ss_family = family;
INETADDR_SET_PORT((PSOCKADDR)&_fwp_um_connect_request->remoteAddressAndPort, parameters->destination_port);
return;
}
void static _free_connection_request()
{
ebpf_free(_fwp_um_connect_request);
_fwp_um_connect_request = nullptr;
}
// This is used to test the INET4_CONNECT hook.
FWP_ACTION_TYPE
_fwp_engine::test_cgroup_inet4_connect()
_fwp_engine::test_cgroup_inet4_connect(_In_ fwp_classify_parameters_t* parameters)
{
FWP_ACTION_TYPE action;
std::unique_lock l(lock);
bool redirected = false;
uint16_t redirected_port = 0;
uint8_t* redirected_address = nullptr;
_allocate_and_initialize_connection_request(AF_INET, parameters);
// For CGROUP_CONNECT* attach type, first CONNECT_REDIRECT callout is invoked, followed by
// AUTH_CONNECT.
FWPS_INCOMING_VALUE0 incoming_value[FWPS_FIELD_ALE_CONNECT_REDIRECT_V4_MAX] = {};
incoming_value[FWPS_FIELD_ALE_CONNECT_REDIRECT_V4_IP_LOCAL_ADDRESS].value.uint32 = _test_source_ipv4_address;
incoming_value[FWPS_FIELD_ALE_CONNECT_REDIRECT_V4_IP_LOCAL_PORT].value.uint16 = _test_source_port;
incoming_value[FWPS_FIELD_ALE_CONNECT_REDIRECT_V4_IP_REMOTE_ADDRESS].value.uint32 = _test_destination_ipv4_address;
incoming_value[FWPS_FIELD_ALE_CONNECT_REDIRECT_V4_IP_REMOTE_PORT].value.uint16 = _test_destination_port;
incoming_value[FWPS_FIELD_ALE_CONNECT_REDIRECT_V4_IP_PROTOCOL].value.uint8 = _test_protocol;
incoming_value[FWPS_FIELD_ALE_CONNECT_REDIRECT_V4_COMPARTMENT_ID].value.uint32 = _test_compartment_id;
incoming_value[FWPS_FIELD_ALE_CONNECT_REDIRECT_V4_ALE_APP_ID].value.byteBlob = &_test_app_id;
incoming_value[FWPS_FIELD_ALE_CONNECT_REDIRECT_V4_ALE_USER_ID].value.byteBlob = &_test_user_id;
incoming_value[FWPS_FIELD_ALE_CONNECT_REDIRECT_V4_IP_LOCAL_ADDRESS].value.uint32 = parameters->source_ipv4_address;
incoming_value[FWPS_FIELD_ALE_CONNECT_REDIRECT_V4_IP_LOCAL_PORT].value.uint16 = parameters->source_port;
incoming_value[FWPS_FIELD_ALE_CONNECT_REDIRECT_V4_IP_REMOTE_ADDRESS].value.uint32 =
parameters->destination_ipv4_address;
incoming_value[FWPS_FIELD_ALE_CONNECT_REDIRECT_V4_IP_REMOTE_PORT].value.uint16 = parameters->destination_port;
incoming_value[FWPS_FIELD_ALE_CONNECT_REDIRECT_V4_IP_PROTOCOL].value.uint8 = parameters->protocol;
incoming_value[FWPS_FIELD_ALE_CONNECT_REDIRECT_V4_COMPARTMENT_ID].value.uint32 = parameters->compartment_id;
incoming_value[FWPS_FIELD_ALE_CONNECT_REDIRECT_V4_ALE_APP_ID].value.byteBlob = &parameters->app_id;
incoming_value[FWPS_FIELD_ALE_CONNECT_REDIRECT_V4_ALE_USER_ID].value.byteBlob = &parameters->user_id;
action = test_cgroup_sock_addr(
action = test_callout(
FWPS_LAYER_ALE_CONNECT_REDIRECT_V4, FWPM_LAYER_ALE_CONNECT_REDIRECT_V4, EBPF_DEFAULT_SUBLAYER, incoming_value);
ebpf_assert(action == FWP_ACTION_PERMIT);
FWPS_INCOMING_VALUE0 incoming_value2[FWPS_FIELD_ALE_AUTH_CONNECT_V4_MAX] = {};
incoming_value2[FWPS_FIELD_ALE_AUTH_CONNECT_V4_IP_LOCAL_ADDRESS].value.uint32 = _test_source_ipv4_address;
incoming_value2[FWPS_FIELD_ALE_AUTH_CONNECT_V4_IP_LOCAL_PORT].value.uint16 = _test_source_port;
incoming_value2[FWPS_FIELD_ALE_AUTH_CONNECT_V4_IP_REMOTE_ADDRESS].value.uint32 = _test_destination_ipv4_address;
incoming_value2[FWPS_FIELD_ALE_AUTH_CONNECT_V4_IP_REMOTE_PORT].value.uint16 = _test_destination_port;
incoming_value2[FWPS_FIELD_ALE_AUTH_CONNECT_V4_IP_PROTOCOL].value.uint8 = _test_protocol;
incoming_value2[FWPS_FIELD_ALE_AUTH_CONNECT_V4_COMPARTMENT_ID].value.uint32 = _test_compartment_id;
incoming_value2[FWPS_FIELD_ALE_AUTH_CONNECT_V4_ALE_APP_ID].value.byteBlob = &_test_app_id;
incoming_value2[FWPS_FIELD_ALE_AUTH_CONNECT_V4_ALE_USER_ID].value.byteBlob = &_test_user_id;
incoming_value2[FWPS_FIELD_ALE_AUTH_CONNECT_V4_IP_LOCAL_INTERFACE].value.uint64 = &_test_interface_luid;
if (_fwp_um_connect_request != nullptr) {
redirected =
_is_connection_redirected(parameters, _fwp_um_connect_request, &redirected_port, &redirected_address);
}
return test_cgroup_sock_addr(
FWPS_INCOMING_VALUE0 incoming_value2[FWPS_FIELD_ALE_AUTH_CONNECT_V4_MAX] = {};
incoming_value2[FWPS_FIELD_ALE_AUTH_CONNECT_V4_IP_LOCAL_ADDRESS].value.uint32 = parameters->source_ipv4_address;
incoming_value2[FWPS_FIELD_ALE_AUTH_CONNECT_V4_IP_LOCAL_PORT].value.uint16 = parameters->source_port;
incoming_value2[FWPS_FIELD_ALE_AUTH_CONNECT_V4_IP_REMOTE_ADDRESS].value.uint32 =
parameters->destination_ipv4_address;
incoming_value2[FWPS_FIELD_ALE_AUTH_CONNECT_V4_IP_REMOTE_PORT].value.uint16 = parameters->destination_port;
incoming_value2[FWPS_FIELD_ALE_AUTH_CONNECT_V4_IP_PROTOCOL].value.uint8 = parameters->protocol;
incoming_value2[FWPS_FIELD_ALE_AUTH_CONNECT_V4_COMPARTMENT_ID].value.uint32 = parameters->compartment_id;
incoming_value2[FWPS_FIELD_ALE_AUTH_CONNECT_V4_ALE_APP_ID].value.byteBlob = &parameters->app_id;
incoming_value2[FWPS_FIELD_ALE_AUTH_CONNECT_V4_ALE_USER_ID].value.byteBlob = &parameters->user_id;
incoming_value2[FWPS_FIELD_ALE_AUTH_CONNECT_V4_IP_LOCAL_INTERFACE].value.uint64 = &parameters->interface_luid;
action = test_callout(
FWPS_LAYER_ALE_AUTH_CONNECT_V4, FWPM_LAYER_ALE_AUTH_CONNECT_V4, EBPF_DEFAULT_SUBLAYER, incoming_value2);
if (redirected) {
// In case the connection is redirected, AUTH_CONNECT callout will be invoked twice.
ebpf_assert(action == FWP_ACTION_PERMIT);
incoming_value2[FWPS_FIELD_ALE_AUTH_CONNECT_V4_IP_REMOTE_PORT].value.uint16 = ntohs(redirected_port);
incoming_value2[FWPS_FIELD_ALE_AUTH_CONNECT_V4_IP_REMOTE_ADDRESS].value.uint32 =
ntohl(*((uint32_t*)redirected_address));
action = test_callout(
FWPS_LAYER_ALE_AUTH_CONNECT_V4, FWPM_LAYER_ALE_AUTH_CONNECT_V4, EBPF_DEFAULT_SUBLAYER, incoming_value2);
}
_free_connection_request();
return action;
}
// This is used to test the INET6_CONNECT hook.
FWP_ACTION_TYPE
_fwp_engine::test_cgroup_inet6_connect()
_fwp_engine::test_cgroup_inet6_connect(_In_ fwp_classify_parameters_t* parameters)
{
FWP_ACTION_TYPE action;
std::unique_lock l(lock);
bool redirected = false;
uint16_t redirected_port = 0;
uint8_t* redirected_address = nullptr;
_allocate_and_initialize_connection_request(AF_INET6, parameters);
FWPS_INCOMING_VALUE0 incoming_value[FWPS_FIELD_ALE_CONNECT_REDIRECT_V6_MAX] = {};
incoming_value[FWPS_FIELD_ALE_CONNECT_REDIRECT_V6_IP_LOCAL_ADDRESS].value.byteArray16 = &_test_source_ipv6_address;
incoming_value[FWPS_FIELD_ALE_CONNECT_REDIRECT_V6_IP_LOCAL_PORT].value.uint16 = _test_source_port;
incoming_value[FWPS_FIELD_ALE_CONNECT_REDIRECT_V6_IP_LOCAL_ADDRESS].value.byteArray16 =
&parameters->source_ipv6_address;
incoming_value[FWPS_FIELD_ALE_CONNECT_REDIRECT_V6_IP_LOCAL_PORT].value.uint16 = parameters->source_port;
incoming_value[FWPS_FIELD_ALE_CONNECT_REDIRECT_V6_IP_REMOTE_ADDRESS].value.byteArray16 =
&_test_destination_ipv6_address;
incoming_value[FWPS_FIELD_ALE_CONNECT_REDIRECT_V6_IP_REMOTE_PORT].value.uint16 = _test_destination_port;
incoming_value[FWPS_FIELD_ALE_CONNECT_REDIRECT_V6_IP_PROTOCOL].value.uint8 = _test_protocol;
incoming_value[FWPS_FIELD_ALE_CONNECT_REDIRECT_V6_COMPARTMENT_ID].value.uint32 = _test_compartment_id;
incoming_value[FWPS_FIELD_ALE_CONNECT_REDIRECT_V6_ALE_APP_ID].value.byteBlob = &_test_app_id;
incoming_value[FWPS_FIELD_ALE_CONNECT_REDIRECT_V6_ALE_USER_ID].value.byteBlob = &_test_user_id;
&parameters->destination_ipv6_address;
incoming_value[FWPS_FIELD_ALE_CONNECT_REDIRECT_V6_IP_REMOTE_PORT].value.uint16 = parameters->destination_port;
incoming_value[FWPS_FIELD_ALE_CONNECT_REDIRECT_V6_IP_PROTOCOL].value.uint8 = parameters->protocol;
incoming_value[FWPS_FIELD_ALE_CONNECT_REDIRECT_V6_COMPARTMENT_ID].value.uint32 = parameters->compartment_id;
incoming_value[FWPS_FIELD_ALE_CONNECT_REDIRECT_V6_ALE_APP_ID].value.byteBlob = &parameters->app_id;
incoming_value[FWPS_FIELD_ALE_CONNECT_REDIRECT_V6_ALE_USER_ID].value.byteBlob = &parameters->user_id;
action = test_cgroup_sock_addr(
action = test_callout(
FWPS_LAYER_ALE_CONNECT_REDIRECT_V6,
FWPM_LAYER_ALE_CONNECT_REDIRECT_V6,
EBPF_HOOK_CGROUP_CONNECT_V6_SUBLAYER,
incoming_value);
ebpf_assert(action == FWP_ACTION_PERMIT);
FWPS_INCOMING_VALUE0 incoming_value2[FWPS_FIELD_ALE_AUTH_CONNECT_V6_MAX] = {};
incoming_value2[FWPS_FIELD_ALE_AUTH_CONNECT_V6_IP_LOCAL_ADDRESS].value.byteArray16 = &_test_source_ipv6_address;
incoming_value2[FWPS_FIELD_ALE_AUTH_CONNECT_V6_IP_LOCAL_PORT].value.uint16 = _test_source_port;
incoming_value2[FWPS_FIELD_ALE_AUTH_CONNECT_V6_IP_REMOTE_ADDRESS].value.byteArray16 =
&_test_destination_ipv6_address;
incoming_value2[FWPS_FIELD_ALE_AUTH_CONNECT_V6_IP_REMOTE_PORT].value.uint16 = _test_destination_port;
incoming_value2[FWPS_FIELD_ALE_AUTH_CONNECT_V6_IP_PROTOCOL].value.uint8 = _test_protocol;
incoming_value2[FWPS_FIELD_ALE_AUTH_CONNECT_V6_COMPARTMENT_ID].value.uint32 = _test_compartment_id;
incoming_value2[FWPS_FIELD_ALE_AUTH_CONNECT_V6_ALE_APP_ID].value.byteBlob = &_test_app_id;
incoming_value2[FWPS_FIELD_ALE_AUTH_CONNECT_V6_ALE_USER_ID].value.byteBlob = &_test_user_id;
incoming_value2[FWPS_FIELD_ALE_AUTH_CONNECT_V6_IP_LOCAL_INTERFACE].value.uint64 = &_test_interface_luid;
if (_fwp_um_connect_request != nullptr) {
redirected =
_is_connection_redirected(parameters, _fwp_um_connect_request, &redirected_port, &redirected_address);
}
return test_cgroup_sock_addr(
FWPS_INCOMING_VALUE0 incoming_value2[FWPS_FIELD_ALE_AUTH_CONNECT_V6_MAX] = {};
incoming_value2[FWPS_FIELD_ALE_AUTH_CONNECT_V6_IP_LOCAL_ADDRESS].value.byteArray16 =
&parameters->source_ipv6_address;
incoming_value2[FWPS_FIELD_ALE_AUTH_CONNECT_V6_IP_LOCAL_PORT].value.uint16 = parameters->source_port;
incoming_value2[FWPS_FIELD_ALE_AUTH_CONNECT_V6_IP_REMOTE_ADDRESS].value.byteArray16 =
&parameters->destination_ipv6_address;
incoming_value2[FWPS_FIELD_ALE_AUTH_CONNECT_V6_IP_REMOTE_PORT].value.uint16 = parameters->destination_port;
incoming_value2[FWPS_FIELD_ALE_AUTH_CONNECT_V6_IP_PROTOCOL].value.uint8 = parameters->protocol;
incoming_value2[FWPS_FIELD_ALE_AUTH_CONNECT_V6_COMPARTMENT_ID].value.uint32 = parameters->compartment_id;
incoming_value2[FWPS_FIELD_ALE_AUTH_CONNECT_V6_ALE_APP_ID].value.byteBlob = &parameters->app_id;
incoming_value2[FWPS_FIELD_ALE_AUTH_CONNECT_V6_ALE_USER_ID].value.byteBlob = &parameters->user_id;
incoming_value2[FWPS_FIELD_ALE_AUTH_CONNECT_V6_IP_LOCAL_INTERFACE].value.uint64 = &parameters->interface_luid;
action = test_callout(
FWPS_LAYER_ALE_AUTH_CONNECT_V6, FWPM_LAYER_ALE_AUTH_CONNECT_V6, EBPF_DEFAULT_SUBLAYER, incoming_value2);
if (redirected) {
// In case the connection is redirected, AUTH_CONNECT callout will be invoked twice.
ebpf_assert(action == FWP_ACTION_PERMIT);
FWP_BYTE_ARRAY16 destination_ip = {0};
memcpy(destination_ip.byteArray16, redirected_address, 16);
incoming_value2[FWPS_FIELD_ALE_AUTH_CONNECT_V6_IP_REMOTE_PORT].value.uint16 = ntohs(redirected_port);
incoming_value2[FWPS_FIELD_ALE_AUTH_CONNECT_V6_IP_REMOTE_ADDRESS].value.byteArray16 = &destination_ip;
action = test_callout(
FWPS_LAYER_ALE_AUTH_CONNECT_V6, FWPM_LAYER_ALE_AUTH_CONNECT_V6, EBPF_DEFAULT_SUBLAYER, incoming_value2);
}
_free_connection_request();
return action;
}
// This is used to test the SOCK_OPS hook for IPv4 traffic.
FWP_ACTION_TYPE
_fwp_engine::test_sock_ops_v4()
_fwp_engine::test_sock_ops_v4(_In_ fwp_classify_parameters_t* parameters)
{
std::unique_lock l(lock);
FWPS_INCOMING_VALUE0 incoming_value[FWPS_FIELD_ALE_FLOW_ESTABLISHED_V4_MAX] = {};
incoming_value[FWPS_FIELD_ALE_FLOW_ESTABLISHED_V4_IP_LOCAL_ADDRESS].value.uint32 = _test_destination_ipv4_address;
incoming_value[FWPS_FIELD_ALE_FLOW_ESTABLISHED_V4_IP_LOCAL_PORT].value.uint16 = _test_destination_port;
incoming_value[FWPS_FIELD_ALE_FLOW_ESTABLISHED_V4_IP_REMOTE_ADDRESS].value.uint32 = _test_source_ipv4_address;
incoming_value[FWPS_FIELD_ALE_FLOW_ESTABLISHED_V4_IP_REMOTE_PORT].value.uint16 = _test_source_port;
incoming_value[FWPS_FIELD_ALE_FLOW_ESTABLISHED_V4_IP_PROTOCOL].value.uint8 = _test_protocol;
incoming_value[FWPS_FIELD_ALE_FLOW_ESTABLISHED_V4_COMPARTMENT_ID].value.uint32 = _test_compartment_id;
incoming_value[FWPS_FIELD_ALE_FLOW_ESTABLISHED_V4_IP_LOCAL_INTERFACE].value.uint64 = &_test_interface_luid;
incoming_value[FWPS_FIELD_ALE_FLOW_ESTABLISHED_V4_ALE_APP_ID].value.byteBlob = &_test_app_id;
incoming_value[FWPS_FIELD_ALE_FLOW_ESTABLISHED_V4_IP_LOCAL_ADDRESS].value.uint32 =
parameters->destination_ipv4_address;
incoming_value[FWPS_FIELD_ALE_FLOW_ESTABLISHED_V4_IP_LOCAL_PORT].value.uint16 = parameters->destination_port;
incoming_value[FWPS_FIELD_ALE_FLOW_ESTABLISHED_V4_IP_REMOTE_ADDRESS].value.uint32 = parameters->source_ipv4_address;
incoming_value[FWPS_FIELD_ALE_FLOW_ESTABLISHED_V4_IP_REMOTE_PORT].value.uint16 = parameters->source_port;
incoming_value[FWPS_FIELD_ALE_FLOW_ESTABLISHED_V4_IP_PROTOCOL].value.uint8 = parameters->protocol;
incoming_value[FWPS_FIELD_ALE_FLOW_ESTABLISHED_V4_COMPARTMENT_ID].value.uint32 = parameters->compartment_id;
incoming_value[FWPS_FIELD_ALE_FLOW_ESTABLISHED_V4_IP_LOCAL_INTERFACE].value.uint64 = &parameters->interface_luid;
incoming_value[FWPS_FIELD_ALE_FLOW_ESTABLISHED_V4_ALE_APP_ID].value.byteBlob = &parameters->app_id;
return test_callout(
FWPS_LAYER_ALE_FLOW_ESTABLISHED_V4, FWPM_LAYER_ALE_FLOW_ESTABLISHED_V4, EBPF_DEFAULT_SUBLAYER, incoming_value);
@ -318,20 +397,19 @@ _fwp_engine::test_sock_ops_v4()
// This is used to test the SOCK_OPS hook for IPv6 traffic.
FWP_ACTION_TYPE
_fwp_engine::test_sock_ops_v6()
_fwp_engine::test_sock_ops_v6(_In_ fwp_classify_parameters_t* parameters)
{
std::unique_lock l(lock);
FWPS_INCOMING_VALUE0 incoming_value[FWPS_FIELD_ALE_FLOW_ESTABLISHED_V6_MAX] = {};
incoming_value[FWPS_FIELD_ALE_FLOW_ESTABLISHED_V6_IP_LOCAL_ADDRESS].value.byteArray16 =
&_test_destination_ipv6_address;
incoming_value[FWPS_FIELD_ALE_FLOW_ESTABLISHED_V6_IP_LOCAL_PORT].value.uint16 = _test_destination_port;
incoming_value[FWPS_FIELD_ALE_FLOW_ESTABLISHED_V6_IP_REMOTE_ADDRESS].value.byteArray16 = &_test_source_ipv6_address;
incoming_value[FWPS_FIELD_ALE_FLOW_ESTABLISHED_V6_IP_REMOTE_PORT].value.uint16 = _test_source_port;
incoming_value[FWPS_FIELD_ALE_FLOW_ESTABLISHED_V6_IP_PROTOCOL].value.uint8 = _test_protocol;
incoming_value[FWPS_FIELD_ALE_FLOW_ESTABLISHED_V6_COMPARTMENT_ID].value.uint32 = _test_compartment_id;
incoming_value[FWPS_FIELD_ALE_FLOW_ESTABLISHED_V6_IP_LOCAL_INTERFACE].value.uint64 = &_test_interface_luid;
incoming_value[FWPS_FIELD_ALE_FLOW_ESTABLISHED_V6_ALE_APP_ID].value.byteBlob = &_test_app_id;
&parameters->destination_ipv6_address;
incoming_value[FWPS_FIELD_ALE_FLOW_ESTABLISHED_V6_IP_LOCAL_PORT].value.uint16 = parameters->destination_port;
incoming_value[FWPS_FIELD_ALE_FLOW_ESTABLISHED_V6_IP_REMOTE_ADDRESS].value.byteArray16 =
&parameters->source_ipv6_address;
incoming_value[FWPS_FIELD_ALE_FLOW_ESTABLISHED_V6_IP_REMOTE_PORT].value.uint16 = parameters->source_port;
incoming_value[FWPS_FIELD_ALE_FLOW_ESTABLISHED_V6_IP_PROTOCOL].value.uint8 = parameters->protocol;
incoming_value[FWPS_FIELD_ALE_FLOW_ESTABLISHED_V6_COMPARTMENT_ID].value.uint32 = parameters->compartment_id;
incoming_value[FWPS_FIELD_ALE_FLOW_ESTABLISHED_V6_IP_LOCAL_INTERFACE].value.uint64 = &parameters->interface_luid;
incoming_value[FWPS_FIELD_ALE_FLOW_ESTABLISHED_V6_ALE_APP_ID].value.byteBlob = &parameters->app_id;
return test_callout(
FWPS_LAYER_ALE_FLOW_ESTABLISHED_V6, FWPM_LAYER_ALE_FLOW_ESTABLISHED_V6, EBPF_DEFAULT_SUBLAYER, incoming_value);
@ -497,19 +575,20 @@ _IRQL_requires_max_(PASSIVE_LEVEL) NTSTATUS FwpsInjectionHandleDestroy0(_In_ HAN
_IRQL_requires_max_(DISPATCH_LEVEL) NTSTATUS
FwpsFlowRemoveContext0(_In_ uint64_t flow_id, _In_ UINT16 layer_id, _In_ uint32_t callout_id)
{
UNREFERENCED_PARAMETER(flow_id);
UNREFERENCED_PARAMETER(layer_id);
UNREFERENCED_PARAMETER(callout_id);
auto& engine = *_fwp_engine::get()->get();
engine.delete_flow_context(flow_id, layer_id, callout_id);
return STATUS_SUCCESS;
}
_IRQL_requires_max_(DISPATCH_LEVEL) NTSTATUS FwpsFlowAssociateContext0(
_In_ uint64_t flow_id, _In_ UINT16 layer_id, _In_ uint32_t callout_id, _In_ uint64_t flowContext)
{
UNREFERENCED_PARAMETER(flow_id);
UNREFERENCED_PARAMETER(layer_id);
UNREFERENCED_PARAMETER(callout_id);
UNREFERENCED_PARAMETER(flowContext);
auto& engine = *_fwp_engine::get()->get();
engine.associate_flow_context(flow_id, callout_id, flowContext);
return STATUS_SUCCESS;
}
@ -651,14 +730,18 @@ _IRQL_requires_max_(DISPATCH_LEVEL) NTSTATUS NTAPI FwpsAcquireWritableLayerDataP
_Out_ void** writableLayerData,
_Inout_opt_ FWPS_CLASSIFY_OUT0* classifyOut)
{
NTSTATUS status = STATUS_SUCCESS;
UNREFERENCED_PARAMETER(classifyHandle);
UNREFERENCED_PARAMETER(filterId);
UNREFERENCED_PARAMETER(flags);
UNREFERENCED_PARAMETER(classifyOut);
*writableLayerData = NULL;
*writableLayerData = _fwp_um_connect_request;
if (*writableLayerData == nullptr) {
status = STATUS_INSUFFICIENT_RESOURCES;
}
return STATUS_SUCCESS;
return status;
}
_IRQL_requires_max_(DISPATCH_LEVEL) NTSTATUS NTAPI
@ -682,8 +765,9 @@ _IRQL_requires_max_(DISPATCH_LEVEL) void NTAPI
FwpsApplyModifiedLayerData0(_In_ UINT64 classifyHandle, _In_ void* modifiedLayerData, _In_ UINT32 flags)
{
UNREFERENCED_PARAMETER(classifyHandle);
UNREFERENCED_PARAMETER(modifiedLayerData);
UNREFERENCED_PARAMETER(flags);
ebpf_assert(modifiedLayerData != nullptr);
}
_IRQL_requires_(PASSIVE_LEVEL) NTSTATUS NTAPI

Просмотреть файл

@ -4,18 +4,38 @@
#include "net_ebpf_ext.h"
#include <mutex>
#include <shared_mutex>
#include <unordered_map>
typedef std::unique_lock<std::shared_mutex> exclusive_lock_t;
typedef std::shared_lock<std::shared_mutex> shared_lock_t;
typedef struct _fwp_classify_parameters
{
ADDRESS_FAMILY family;
uint32_t destination_ipv4_address;
FWP_BYTE_ARRAY16 destination_ipv6_address;
uint16_t destination_port;
uint32_t source_ipv4_address;
FWP_BYTE_ARRAY16 source_ipv6_address;
uint16_t source_port;
uint8_t protocol;
uint32_t compartment_id;
FWP_BYTE_BLOB app_id;
uint64_t interface_luid;
TOKEN_ACCESS_INFORMATION token_access_information;
FWP_BYTE_BLOB user_id;
} fwp_classify_parameters_t;
typedef class _fwp_engine
{
public:
_fwp_engine() = default;
uint32_t
add_fwpm_callout(const FWPM_CALLOUT0* callout)
add_fwpm_callout(_In_ const FWPM_CALLOUT0* callout)
{
std::unique_lock l(lock);
exclusive_lock_t l(lock);
uint32_t id = next_id++;
fwpm_callouts.insert({id, *callout});
return id;
@ -24,55 +44,143 @@ typedef class _fwp_engine
bool
remove_fwpm_callout(size_t id)
{
std::unique_lock l(lock);
exclusive_lock_t l(lock);
return fwpm_callouts.erase(id) == 1;
}
uint32_t
register_fwps_callout(const FWPS_CALLOUT3* callout)
register_fwps_callout(_In_ const FWPS_CALLOUT3* callout)
{
std::unique_lock l(lock);
exclusive_lock_t l(lock);
uint32_t id = next_id++;
fwps_callouts.insert({id, *callout});
return id;
}
bool
remove_fwps_callout(size_t id)
_Requires_lock_held_(this->lock) FWPS_CALLOUT3* get_fwps_callout(_In_ const GUID* callout_key)
{
std::unique_lock l(lock);
for (auto& it : fwps_callouts) {
if (memcmp(&it.second.calloutKey, callout_key, sizeof(GUID)) == 0) {
return &it.second;
}
}
return nullptr;
}
_Requires_lock_held_(this->lock) FWPS_CALLOUT3* get_fwps_callout(uint32_t callout_id)
{
for (auto& it : fwps_callouts) {
if (it.first == callout_id) {
return &it.second;
}
}
return nullptr;
}
_Requires_lock_not_held_(this->lock) bool remove_fwps_callout(size_t id)
{
exclusive_lock_t l(lock);
return fwps_callouts.erase(id) == 1;
}
uint32_t
add_fwpm_filter(const FWPM_FILTER0* filter)
_Requires_lock_not_held_(this->lock) void associate_flow_context(
uint64_t flow_id, uint32_t callout_id, uint64_t flow_context)
{
std::unique_lock l(lock);
uint32_t id = next_id++;
fwpm_filters.insert({id, *filter});
UNREFERENCED_PARAMETER(callout_id);
exclusive_lock_t l(lock);
fwpm_flow_contexts.insert({flow_id, flow_context});
}
_Requires_lock_not_held_(this->lock) void delete_flow_context(
uint64_t flow_id, uint16_t layer_id, uint32_t callout_id)
{
FWPS_CALLOUT3* callout = nullptr;
FWPS_FILTER fwps_filter = {};
uint64_t flow_context = 0;
{
exclusive_lock_t l(lock);
for (auto& it : fwpm_flow_contexts) {
if (it.first == flow_id) {
callout = get_fwps_callout(callout_id);
ebpf_assert(callout != nullptr);
flow_context = it.second;
break;
}
}
fwpm_flow_contexts.erase(flow_id);
}
ebpf_assert(callout != nullptr);
__analysis_assume(callout != nullptr);
// Invoke flow delete notification callback.
callout->flowDeleteFn(layer_id, callout_id, flow_context);
}
_Requires_lock_not_held_(this->lock) uint32_t add_fwpm_filter(_In_ const FWPM_FILTER0* filter)
{
FWPS_CALLOUT3* callout = nullptr;
FWPS_FILTER fwps_filter = {};
uint32_t id;
{
exclusive_lock_t l(lock);
id = next_id++;
fwpm_filters.insert({id, *filter});
callout = get_fwps_callout(&filter->action.calloutKey);
ebpf_assert(callout != nullptr);
fwps_filter.context = filter->rawContext;
}
__analysis_assume(callout != nullptr);
// Invoke filter add notification callback.
callout->notifyFn(FWPS_CALLOUT_NOTIFY_ADD_FILTER, &filter->action.calloutKey, &fwps_filter);
return id;
}
bool
remove_fwpm_filter(size_t id)
_Requires_lock_not_held_(this->lock) bool remove_fwpm_filter(size_t id)
{
std::unique_lock l(lock);
return fwpm_filters.erase(id) == 1;
FWPS_CALLOUT3* callout = nullptr;
FWPS_FILTER fwps_filter = {};
bool return_value = false;
{
exclusive_lock_t l(lock);
for (auto& it : fwpm_filters) {
if (it.first == id) {
callout = get_fwps_callout(&it.second.action.calloutKey);
ebpf_assert(callout != nullptr);
fwps_filter.context = it.second.rawContext;
break;
}
}
return_value = fwpm_filters.erase(id) == 1;
}
ebpf_assert(callout != nullptr);
__analysis_assume(callout != nullptr);
// Invoke filter delete notification callback.
callout->notifyFn(FWPS_CALLOUT_NOTIFY_DELETE_FILTER, &callout->calloutKey, &fwps_filter);
return return_value;
}
uint32_t
add_fwpm_sub_layer(const FWPM_SUBLAYER0* sub_layer)
_Requires_lock_not_held_(this->lock) uint32_t add_fwpm_sub_layer(_In_ const FWPM_SUBLAYER0* sub_layer)
{
std::unique_lock l(lock);
exclusive_lock_t l(lock);
uint32_t id = next_id++;
fwpm_sub_layers.insert({id, *sub_layer});
return id;
}
bool
remove_fwpm_sub_layer(size_t id)
_Requires_lock_not_held_(this->lock) bool remove_fwpm_sub_layer(size_t id)
{
std::unique_lock l(lock);
exclusive_lock_t l(lock);
return fwpm_sub_layers.erase(id) == 1;
}
@ -80,25 +188,25 @@ typedef class _fwp_engine
classify_test_packet(_In_ const GUID* layer_guid, NET_IFINDEX if_index);
FWP_ACTION_TYPE
test_bind_ipv4();
test_bind_ipv4(_In_ fwp_classify_parameters_t* parameters);
FWP_ACTION_TYPE
test_cgroup_inet4_recv_accept();
test_cgroup_inet4_recv_accept(_In_ fwp_classify_parameters_t* parameters);
FWP_ACTION_TYPE
test_cgroup_inet6_recv_accept();
test_cgroup_inet6_recv_accept(_In_ fwp_classify_parameters_t* parameters);
FWP_ACTION_TYPE
test_cgroup_inet4_connect();
test_cgroup_inet4_connect(_In_ fwp_classify_parameters_t* parameters);
FWP_ACTION_TYPE
test_cgroup_inet6_connect();
test_cgroup_inet6_connect(_In_ fwp_classify_parameters_t* parameters);
FWP_ACTION_TYPE
test_sock_ops_v4();
test_sock_ops_v4(_In_ fwp_classify_parameters_t* parameters);
FWP_ACTION_TYPE
test_sock_ops_v6();
test_sock_ops_v6(_In_ fwp_classify_parameters_t* parameters);
static _fwp_engine*
get()
@ -109,22 +217,14 @@ typedef class _fwp_engine
}
private:
FWP_ACTION_TYPE
test_cgroup_sock_addr(
uint16_t layer_id,
_In_ const GUID& layer_guid,
_In_ const GUID& sublayer_guid,
_In_ FWPS_INCOMING_VALUE0* incomingValue);
FWP_ACTION_TYPE
test_callout(
_Requires_lock_not_held_(this->lock) FWP_ACTION_TYPE test_callout(
uint16_t layer_id,
_In_ const GUID& layer_guid,
_In_ const GUID& sublayer_guid,
_In_ FWPS_INCOMING_VALUE0* incoming_value);
_Ret_maybenull_ const FWPM_FILTER*
get_fwpm_filter_with_context(_In_ const GUID& layer_guid)
get_fwpm_filter_with_context_under_lock(_In_ const GUID& layer_guid)
{
for (auto& [first, filter] : fwpm_filters) {
if (memcmp(&filter.layerKey, &layer_guid, sizeof(GUID)) == 0 && filter.rawContext != 0) {
@ -135,7 +235,7 @@ typedef class _fwp_engine
}
_Ret_maybenull_ const FWPM_FILTER*
get_fwpm_filter_with_context(_In_ const GUID& layer_guid, _In_ const GUID& sublayer_guid)
get_fwpm_filter_with_context_under_lock(_In_ const GUID& layer_guid, _In_ const GUID& sublayer_guid)
{
for (auto& [first, filter] : fwpm_filters) {
if (memcmp(&filter.layerKey, &layer_guid, sizeof(GUID)) == 0 &&
@ -147,7 +247,7 @@ typedef class _fwp_engine
}
_Ret_maybenull_ const GUID*
get_callout_key_from_layer_guid(_In_ const GUID* layer_guid)
get_callout_key_from_layer_guid_under_lock(_In_ const GUID* layer_guid)
{
for (auto& [first, callout] : fwpm_callouts) {
if (callout.applicableLayer == *layer_guid) {
@ -158,7 +258,7 @@ typedef class _fwp_engine
}
_Ret_maybenull_ const FWPS_CALLOUT3*
get_callout_from_key(_In_ const GUID* callout_key)
get_callout_from_key_under_lock(_In_ const GUID* callout_key)
{
for (auto& [first, callout] : fwps_callouts) {
if (callout.calloutKey == *callout_key) {
@ -170,10 +270,12 @@ typedef class _fwp_engine
static std::unique_ptr<_fwp_engine> _engine;
std::mutex lock;
std::shared_mutex lock;
uint32_t next_id = 1;
uint32_t next_flow_id = 1;
std::unordered_map<size_t, FWPS_CALLOUT3> fwps_callouts;
std::unordered_map<size_t, FWPM_CALLOUT0> fwpm_callouts;
std::unordered_map<size_t, FWPM_FILTER0> fwpm_filters;
std::unordered_map<size_t, FWPM_SUBLAYER0> fwpm_sub_layers;
std::unordered_map<uint64_t, uint64_t> fwpm_flow_contexts;
} fwp_engine;

Просмотреть файл

@ -103,23 +103,26 @@ FUZZ_EXPORT int __cdecl LLVMFuzzerTestOneInput(const uint8_t* data, size_t size)
return 0;
}
fwp_classify_parameters_t parameters = {};
netebpfext_initialize_fwp_classify_parameters(&parameters);
client_context.metadata = *metadata;
switch (prog_type) {
case BPF_PROG_TYPE_XDP:
(void)helper.classify_test_packet(&FWPM_LAYER_INBOUND_MAC_FRAME_NATIVE, if_index);
break;
case BPF_PROG_TYPE_BIND:
(void)helper.test_bind_ipv4();
(void)helper.test_bind_ipv4(&parameters);
break;
case BPF_PROG_TYPE_CGROUP_SOCK_ADDR:
(void)helper.test_cgroup_inet4_recv_accept();
(void)helper.test_cgroup_inet6_recv_accept();
(void)helper.test_cgroup_inet4_connect();
(void)helper.test_cgroup_inet6_connect();
(void)helper.test_cgroup_inet4_recv_accept(&parameters);
(void)helper.test_cgroup_inet6_recv_accept(&parameters);
(void)helper.test_cgroup_inet4_connect(&parameters);
(void)helper.test_cgroup_inet6_connect(&parameters);
break;
case BPF_PROG_TYPE_SOCK_OPS:
(void)helper.test_sock_ops_v4();
(void)helper.test_sock_ops_v6();
(void)helper.test_sock_ops_v4(&parameters);
(void)helper.test_sock_ops_v6(&parameters);
break;
}

Просмотреть файл

@ -6,6 +6,37 @@
ebpf_registry_key_t ebpf_root_registry_key = HKEY_CURRENT_USER;
DEVICE_OBJECT* _net_ebpf_ext_driver_device_object;
constexpr uint32_t _test_destination_ipv4_address = 0x01020304;
static FWP_BYTE_ARRAY16 _test_destination_ipv6_address = {1, 2, 3, 4};
constexpr uint16_t _test_destination_port = 1234;
constexpr uint32_t _test_source_ipv4_address = 0x05060708;
static FWP_BYTE_ARRAY16 _test_source_ipv6_address = {5, 6, 7, 8};
constexpr uint16_t _test_source_port = 5678;
constexpr uint8_t _test_protocol = IPPROTO_TCP;
constexpr uint32_t _test_compartment_id = 1;
static FWP_BYTE_BLOB _test_app_id = {.size = 2, .data = (uint8_t*)"\\"};
static uint64_t _test_interface_luid = 1;
static TOKEN_ACCESS_INFORMATION _test_token_access_information = {0};
static FWP_BYTE_BLOB _test_user_id = {
.size = (sizeof(TOKEN_ACCESS_INFORMATION)), .data = (uint8_t*)&_test_token_access_information};
void
netebpfext_initialize_fwp_classify_parameters(_Out_ fwp_classify_parameters_t* parameters)
{
parameters->destination_ipv4_address = _test_destination_ipv4_address;
parameters->destination_ipv6_address = _test_destination_ipv6_address;
parameters->source_ipv4_address = _test_source_ipv4_address;
parameters->source_ipv6_address = _test_source_ipv6_address;
parameters->source_port = _test_source_port;
parameters->destination_port = _test_destination_port;
parameters->protocol = _test_protocol;
parameters->compartment_id = _test_compartment_id;
parameters->app_id = _test_app_id;
parameters->interface_luid = _test_interface_luid;
parameters->token_access_information = _test_token_access_information;
parameters->user_id = _test_user_id;
}
_netebpf_ext_helper::_netebpf_ext_helper(
_In_opt_ const void* npi_specific_characteristics,
_In_opt_ _ebpf_extension_dispatch_function dispatch_function,
@ -16,6 +47,9 @@ _netebpf_ext_helper::_netebpf_ext_helper(
REQUIRE(NT_SUCCESS(status));
trace_initiated = true;
REQUIRE(ebpf_platform_initiate() == EBPF_SUCCESS);
platform_initialized = true;
status = net_ebpf_ext_initialize_ndis_handles(driver_object);
REQUIRE(NT_SUCCESS(status));
@ -71,6 +105,10 @@ _netebpf_ext_helper::~_netebpf_ext_helper()
net_ebpf_ext_uninitialize_ndis_handles();
}
if (platform_initialized) {
ebpf_platform_terminate();
}
if (trace_initiated) {
net_ebpf_ext_trace_terminate();
}

Просмотреть файл

@ -49,31 +49,53 @@ typedef class _netebpf_ext_helper
}
FWP_ACTION_TYPE
test_bind_ipv4() { return _fwp_engine::get()->test_bind_ipv4(); }
test_bind_ipv4(_In_ fwp_classify_parameters_t* parameters)
{
return _fwp_engine::get()->test_bind_ipv4(parameters);
}
FWP_ACTION_TYPE
test_cgroup_inet4_recv_accept() { return _fwp_engine::get()->test_cgroup_inet4_recv_accept(); }
test_cgroup_inet4_recv_accept(_In_ fwp_classify_parameters_t* parameters)
{
return _fwp_engine::get()->test_cgroup_inet4_recv_accept(parameters);
}
FWP_ACTION_TYPE
test_cgroup_inet6_recv_accept() { return _fwp_engine::get()->test_cgroup_inet6_recv_accept(); }
test_cgroup_inet6_recv_accept(_In_ fwp_classify_parameters_t* parameters)
{
return _fwp_engine::get()->test_cgroup_inet6_recv_accept(parameters);
}
FWP_ACTION_TYPE
test_cgroup_inet4_connect() { return _fwp_engine::get()->test_cgroup_inet4_connect(); }
test_cgroup_inet4_connect(_In_ fwp_classify_parameters_t* parameters)
{
return _fwp_engine::get()->test_cgroup_inet4_connect(parameters);
}
FWP_ACTION_TYPE
test_cgroup_inet6_connect() { return _fwp_engine::get()->test_cgroup_inet6_connect(); }
test_cgroup_inet6_connect(_In_ fwp_classify_parameters_t* parameters)
{
return _fwp_engine::get()->test_cgroup_inet6_connect(parameters);
}
FWP_ACTION_TYPE
test_sock_ops_v4() { return _fwp_engine::get()->test_sock_ops_v4(); }
test_sock_ops_v4(_In_ fwp_classify_parameters_t* parameters)
{
return _fwp_engine::get()->test_sock_ops_v4(parameters);
}
FWP_ACTION_TYPE
test_sock_ops_v6() { return _fwp_engine::get()->test_sock_ops_v6(); }
test_sock_ops_v6(_In_ fwp_classify_parameters_t* parameters)
{
return _fwp_engine::get()->test_sock_ops_v6(parameters);
}
private:
bool trace_initiated = false;
bool ndis_handle_initialized = false;
bool provider_registered = false;
bool wfp_initialized = false;
bool platform_initialized = false;
DRIVER_OBJECT* driver_object = reinterpret_cast<DRIVER_OBJECT*>(this);
DEVICE_OBJECT* device_object = reinterpret_cast<DEVICE_OBJECT*>(this);
@ -165,3 +187,6 @@ typedef class _netebpf_ext_helper
HANDLE nmr_hook_client_handle;
} netebpf_ext_helper_t;
void
netebpfext_initialize_fwp_classify_parameters(_Out_ fwp_classify_parameters_t* parameters);

Просмотреть файл

@ -7,6 +7,31 @@
#include <map>
#define CONCURRENT_THREAD_RUN_TIME_IN_SECONDS 10
typedef enum _sock_addr_test_type
{
SOCK_ADDR_TEST_TYPE_CONNECT,
SOCK_ADDR_TEST_TYPE_RECV_ACCEPT
} sock_addr_test_type_t;
typedef enum _sock_addr_test_action
{
SOCK_ADDR_TEST_ACTION_PERMIT,
SOCK_ADDR_TEST_ACTION_BLOCK,
SOCK_ADDR_TEST_ACTION_REDIRECT,
SOCK_ADDR_TEST_ACTION_FAILURE,
SOCK_ADDR_TEST_ACTION_ROUND_ROBIN
} sock_addr_test_action_t;
typedef enum _xdp_test_action
{
XDP_TEST_ACTION_PASS, ///< Allow the packet to pass.
XDP_TEST_ACTION_DROP, ///< Drop the packet.
XDP_TEST_ACTION_TX, ///< Bounce the received packet back out the same NIC it arrived on.
XDP_TEST_ACTION_FAILURE ///< Failed to invoke the eBPF program.
} xdp_test_action_t;
TEST_CASE("query program info", "[netebpfext]")
{
netebpf_ext_helper_t helper;
@ -44,7 +69,7 @@ typedef struct _test_xdp_client_context
{
netebpfext_helper_base_client_context_t base;
void* provider_binding_context;
xdp_action_t xdp_action;
xdp_test_action_t xdp_action;
} test_xdp_client_context_t;
// This callback occurs when netebpfext gets a packet and submits it to our dummy
@ -53,10 +78,29 @@ _Must_inspect_result_ ebpf_result_t
netebpfext_unit_invoke_xdp_program(
_In_ const void* client_binding_context, _In_ const void* context, _Out_ uint32_t* result)
{
ebpf_result_t return_result = EBPF_SUCCESS;
auto client_context = (test_xdp_client_context_t*)client_binding_context;
UNREFERENCED_PARAMETER(context);
*result = client_context->xdp_action;
return EBPF_SUCCESS;
switch (client_context->xdp_action) {
case XDP_TEST_ACTION_PASS:
*result = XDP_PASS;
break;
case XDP_TEST_ACTION_DROP:
*result = XDP_DROP;
break;
case XDP_TEST_ACTION_TX:
*result = XDP_TX;
break;
case XDP_TEST_ACTION_FAILURE:
return_result = EBPF_FAILED;
break;
default:
*result = XDP_DROP;
break;
}
return return_result;
}
TEST_CASE("classify_packet", "[netebpfext]")
@ -72,17 +116,22 @@ TEST_CASE("classify_packet", "[netebpfext]")
(netebpfext_helper_base_client_context_t*)&client_context);
// Classify an inbound packet that should pass.
client_context.xdp_action = XDP_PASS;
client_context.xdp_action = XDP_TEST_ACTION_PASS;
FWP_ACTION_TYPE result = helper.classify_test_packet(&FWPM_LAYER_INBOUND_MAC_FRAME_NATIVE, if_index);
REQUIRE(result == FWP_ACTION_PERMIT);
// Classify an inbound packet that should be hairpinned.
client_context.xdp_action = XDP_TX;
client_context.xdp_action = XDP_TEST_ACTION_TX;
result = helper.classify_test_packet(&FWPM_LAYER_INBOUND_MAC_FRAME_NATIVE, if_index);
REQUIRE(result == FWP_ACTION_BLOCK);
// Classify an inbound packet that should be dropped.
client_context.xdp_action = XDP_DROP;
client_context.xdp_action = XDP_TEST_ACTION_DROP;
result = helper.classify_test_packet(&FWPM_LAYER_INBOUND_MAC_FRAME_NATIVE, if_index);
REQUIRE(result == FWP_ACTION_BLOCK);
// Classify an inbound packet when eBPF program invocation failed.
client_context.xdp_action = XDP_TEST_ACTION_FAILURE;
result = helper.classify_test_packet(&FWPM_LAYER_INBOUND_MAC_FRAME_NATIVE, if_index);
REQUIRE(result == FWP_ACTION_BLOCK);
}
@ -168,25 +217,28 @@ TEST_CASE("bind_invoke", "[netebpfext]")
{
ebpf_extension_data_t npi_specific_characteristics = {};
test_bind_client_context_t client_context = {};
fwp_classify_parameters_t parameters = {};
netebpf_ext_helper_t helper(
&npi_specific_characteristics,
(_ebpf_extension_dispatch_function)netebpfext_unit_invoke_bind_program,
(netebpfext_helper_base_client_context_t*)&client_context);
netebpfext_initialize_fwp_classify_parameters(&parameters);
// Classify a bind that should be allowed.
client_context.bind_action = BIND_PERMIT;
FWP_ACTION_TYPE result = helper.test_bind_ipv4(); // TODO(issue #526): support IPv6.
FWP_ACTION_TYPE result = helper.test_bind_ipv4(&parameters); // TODO(issue #526): support IPv6.
REQUIRE(result == FWP_ACTION_PERMIT);
// Classify a bind that should be redirected.
client_context.bind_action = BIND_REDIRECT;
result = helper.test_bind_ipv4();
result = helper.test_bind_ipv4(&parameters);
REQUIRE(result == FWP_ACTION_PERMIT);
// Classify a bind that should be blocked.
client_context.bind_action = BIND_DENY;
result = helper.test_bind_ipv4();
result = helper.test_bind_ipv4(&parameters);
REQUIRE(result == FWP_ACTION_BLOCK);
}
@ -271,69 +323,324 @@ typedef struct test_sock_addr_client_context_t
{
netebpfext_helper_base_client_context_t base;
int sock_addr_action;
bool validate_sock_addr_entries = true;
} test_sock_addr_client_context_t;
static inline sock_addr_test_action_t
_get_sock_addr_action(uint16_t destination_port)
{
return (sock_addr_test_action_t)(destination_port % SOCK_ADDR_TEST_ACTION_ROUND_ROBIN);
}
static inline FWP_ACTION_TYPE
_get_fwp_sock_addr_action(uint16_t destination_port)
{
sock_addr_test_action_t action = _get_sock_addr_action(destination_port);
if (action == SOCK_ADDR_TEST_ACTION_PERMIT || action == SOCK_ADDR_TEST_ACTION_REDIRECT) {
return FWP_ACTION_PERMIT;
}
return FWP_ACTION_BLOCK;
}
_Must_inspect_result_ ebpf_result_t
netebpfext_unit_invoke_sock_addr_program(
_In_ const void* client_binding_context, _In_ const void* context, _Out_ uint32_t* result)
{
ebpf_result_t return_result = EBPF_SUCCESS;
auto client_context = (test_sock_addr_client_context_t*)client_binding_context;
auto sock_addr_context = (bpf_sock_addr_t*)context;
int action;
// Verify context fields match what the netebpfext helper set.
// Note that the helper sets the first four bytes of the address to the
// same value regardless of whether it is IPv4 or IPv6, so we just look
// at the first four bytes as if it were an IPv4 address.
REQUIRE((sock_addr_context->family == AF_INET || sock_addr_context->family == AF_INET6));
REQUIRE(sock_addr_context->user_ip4 == htonl(0x01020304));
REQUIRE(sock_addr_context->msg_src_ip4 == htonl(0x05060708));
REQUIRE(sock_addr_context->protocol == IPPROTO_TCP);
REQUIRE(sock_addr_context->user_port == htons(1234));
REQUIRE(sock_addr_context->msg_src_port == htons(5678));
if (client_context->validate_sock_addr_entries) {
REQUIRE((sock_addr_context->family == AF_INET || sock_addr_context->family == AF_INET6));
REQUIRE(sock_addr_context->user_ip4 == htonl(0x01020304));
REQUIRE(sock_addr_context->msg_src_ip4 == htonl(0x05060708));
REQUIRE(sock_addr_context->protocol == IPPROTO_TCP);
REQUIRE(sock_addr_context->user_port == htons(1234));
REQUIRE(sock_addr_context->msg_src_port == htons(5678));
}
*result = client_context->sock_addr_action;
return EBPF_SUCCESS;
// If the action is round robin, decide the action based on the port number.
if (client_context->sock_addr_action == SOCK_ADDR_TEST_ACTION_ROUND_ROBIN) {
action = _get_sock_addr_action(sock_addr_context->user_port);
} else {
action = client_context->sock_addr_action;
}
switch (action) {
case SOCK_ADDR_TEST_ACTION_PERMIT:
*result = BPF_SOCK_ADDR_VERDICT_PROCEED;
break;
case SOCK_ADDR_TEST_ACTION_BLOCK:
*result = BPF_SOCK_ADDR_VERDICT_REJECT;
break;
case SOCK_ADDR_TEST_ACTION_REDIRECT:
sock_addr_context->user_port++;
if (sock_addr_context->family == AF_INET) {
sock_addr_context->user_ip4++;
} else {
auto first_octet = &sock_addr_context->user_ip6[0];
(*first_octet)++;
}
*result = BPF_SOCK_ADDR_VERDICT_PROCEED;
break;
case SOCK_ADDR_TEST_ACTION_FAILURE:
return_result = EBPF_FAILED;
break;
default:
*result = BPF_SOCK_ADDR_VERDICT_REJECT;
}
return return_result;
}
TEST_CASE("sock_addr_invoke", "[netebpfext]")
{
ebpf_extension_data_t npi_specific_characteristics = {};
test_sock_addr_client_context_t client_context = {};
fwp_classify_parameters_t parameters = {};
netebpf_ext_helper_t helper(
&npi_specific_characteristics,
(_ebpf_extension_dispatch_function)netebpfext_unit_invoke_sock_addr_program,
(netebpfext_helper_base_client_context_t*)&client_context);
netebpfext_initialize_fwp_classify_parameters(&parameters);
// Classify operations that should be allowed.
client_context.sock_addr_action = BPF_SOCK_ADDR_VERDICT_PROCEED;
client_context.sock_addr_action = SOCK_ADDR_TEST_ACTION_PERMIT;
client_context.validate_sock_addr_entries = true;
FWP_ACTION_TYPE result = helper.test_cgroup_inet4_recv_accept();
FWP_ACTION_TYPE result = helper.test_cgroup_inet4_recv_accept(&parameters);
REQUIRE(result == FWP_ACTION_PERMIT);
result = helper.test_cgroup_inet6_recv_accept();
result = helper.test_cgroup_inet6_recv_accept(&parameters);
REQUIRE(result == FWP_ACTION_PERMIT);
result = helper.test_cgroup_inet4_connect();
result = helper.test_cgroup_inet4_connect(&parameters);
REQUIRE(result == FWP_ACTION_PERMIT);
result = helper.test_cgroup_inet6_connect();
result = helper.test_cgroup_inet6_connect(&parameters);
REQUIRE(result == FWP_ACTION_PERMIT);
// Classify operations that should be blocked.
client_context.sock_addr_action = BPF_SOCK_ADDR_VERDICT_REJECT;
client_context.sock_addr_action = SOCK_ADDR_TEST_ACTION_BLOCK;
result = helper.test_cgroup_inet4_recv_accept();
result = helper.test_cgroup_inet4_recv_accept(&parameters);
REQUIRE(result == FWP_ACTION_BLOCK);
result = helper.test_cgroup_inet6_recv_accept();
result = helper.test_cgroup_inet6_recv_accept(&parameters);
REQUIRE(result == FWP_ACTION_BLOCK);
result = helper.test_cgroup_inet4_connect();
result = helper.test_cgroup_inet4_connect(&parameters);
REQUIRE(result == FWP_ACTION_BLOCK);
result = helper.test_cgroup_inet6_connect();
result = helper.test_cgroup_inet6_connect(&parameters);
REQUIRE(result == FWP_ACTION_BLOCK);
// Classify operations for redirect.
client_context.sock_addr_action = SOCK_ADDR_TEST_ACTION_REDIRECT;
result = helper.test_cgroup_inet4_connect(&parameters);
REQUIRE(result == FWP_ACTION_PERMIT);
result = helper.test_cgroup_inet6_connect(&parameters);
REQUIRE(result == FWP_ACTION_PERMIT);
// Test eBPF program invocation failure.
client_context.sock_addr_action = SOCK_ADDR_TEST_ACTION_FAILURE;
result = helper.test_cgroup_inet4_recv_accept(&parameters);
REQUIRE(result == FWP_ACTION_BLOCK);
result = helper.test_cgroup_inet6_recv_accept(&parameters);
REQUIRE(result == FWP_ACTION_BLOCK);
result = helper.test_cgroup_inet4_connect(&parameters);
REQUIRE(result == FWP_ACTION_BLOCK);
result = helper.test_cgroup_inet6_connect(&parameters);
REQUIRE(result == FWP_ACTION_BLOCK);
}
void
sock_addr_thread_function(
std::stop_token token,
_In_ netebpf_ext_helper_t* helper,
_In_ fwp_classify_parameters_t* parameters,
sock_addr_test_type_t type,
uint16_t start_port,
uint16_t end_port)
{
FWP_ACTION_TYPE result;
uint16_t port_number;
if (start_port != end_port) {
port_number = start_port - 1;
} else {
port_number = htons(parameters->destination_port);
}
while (!token.stop_requested()) {
// If start_port and end_port are same, then the port number for each
// invocation will remain the same.
if (start_port != end_port) {
port_number++;
if (port_number > end_port) {
port_number = start_port;
}
parameters->destination_port = htons(port_number);
}
switch (type) {
case SOCK_ADDR_TEST_TYPE_RECV_ACCEPT:
result = helper->test_cgroup_inet4_recv_accept(parameters);
break;
case SOCK_ADDR_TEST_TYPE_CONNECT:
default:
result = helper->test_cgroup_inet4_connect(parameters);
break;
}
REQUIRE(result == _get_fwp_sock_addr_action(port_number));
}
}
// Invoke SOCK_ADDR_CONNECT concurrently with same classify parameters.
TEST_CASE("sock_addr_invoke_concurrent1", "[netebpfext_concurrent]")
{
ebpf_extension_data_t npi_specific_characteristics = {};
test_sock_addr_client_context_t client_context = {};
fwp_classify_parameters_t parameters = {};
std::vector<std::jthread> threads;
netebpf_ext_helper_t helper(
&npi_specific_characteristics,
(_ebpf_extension_dispatch_function)netebpfext_unit_invoke_sock_addr_program,
(netebpfext_helper_base_client_context_t*)&client_context);
netebpfext_initialize_fwp_classify_parameters(&parameters);
client_context.validate_sock_addr_entries = true;
// Classify operations that should be allowed.
client_context.sock_addr_action = SOCK_ADDR_TEST_ACTION_PERMIT;
uint32_t thread_count = 2 * ebpf_get_cpu_count();
for (uint32_t i = 0; i < thread_count; i++) {
threads.emplace_back(
sock_addr_thread_function,
&helper,
&parameters,
SOCK_ADDR_TEST_TYPE_CONNECT,
parameters.destination_port,
parameters.destination_port);
}
// Wait for 10 seconds.
std::this_thread::sleep_for(std::chrono::seconds(CONCURRENT_THREAD_RUN_TIME_IN_SECONDS));
// Stop all threads.
for (auto& thread : threads) {
thread.request_stop();
}
// Wait for all threads to stop.
for (auto& thread : threads) {
thread.join();
}
}
// Invoke SOCK_ADDR_CONNECT concurrently with different classify parameters.
TEST_CASE("sock_addr_invoke_concurrent2", "[netebpfext_concurrent]")
{
ebpf_extension_data_t npi_specific_characteristics = {};
test_sock_addr_client_context_t client_context = {};
std::vector<std::jthread> threads;
std::vector<fwp_classify_parameters_t> parameters;
netebpf_ext_helper_t helper(
&npi_specific_characteristics,
(_ebpf_extension_dispatch_function)netebpfext_unit_invoke_sock_addr_program,
(netebpfext_helper_base_client_context_t*)&client_context);
client_context.sock_addr_action = SOCK_ADDR_TEST_ACTION_ROUND_ROBIN;
client_context.validate_sock_addr_entries = false;
uint32_t thread_count = 2 * ebpf_get_cpu_count();
parameters.resize(thread_count);
for (uint32_t i = 0; i < thread_count; i++) {
netebpfext_initialize_fwp_classify_parameters(&parameters[i]);
threads.emplace_back(
sock_addr_thread_function,
&helper,
&parameters[i],
SOCK_ADDR_TEST_TYPE_CONNECT,
(uint16_t)(i * 1000),
(uint16_t)(i * 1000 + 1000));
}
// Wait for 10 seconds.
std::this_thread::sleep_for(std::chrono::seconds(CONCURRENT_THREAD_RUN_TIME_IN_SECONDS));
// Stop all threads.
for (auto& thread : threads) {
thread.request_stop();
}
// Wait for all threads to stop.
for (auto& thread : threads) {
thread.join();
}
}
// Invoke SOCK_ADDR_RECV_ACCEPT concurrently with different classify parameters.
TEST_CASE("sock_addr_invoke_concurrent3", "[netebpfext_concurrent]")
{
ebpf_extension_data_t npi_specific_characteristics = {};
test_sock_addr_client_context_t client_context = {};
std::vector<std::jthread> threads;
std::vector<fwp_classify_parameters_t> parameters;
netebpf_ext_helper_t helper(
&npi_specific_characteristics,
(_ebpf_extension_dispatch_function)netebpfext_unit_invoke_sock_addr_program,
(netebpfext_helper_base_client_context_t*)&client_context);
client_context.sock_addr_action = SOCK_ADDR_TEST_ACTION_ROUND_ROBIN;
client_context.validate_sock_addr_entries = false;
uint32_t thread_count = 2 * ebpf_get_cpu_count();
parameters.resize(thread_count);
for (uint32_t i = 0; i < thread_count; i++) {
netebpfext_initialize_fwp_classify_parameters(&parameters[i]);
threads.emplace_back(
sock_addr_thread_function,
&helper,
&parameters[i],
SOCK_ADDR_TEST_TYPE_RECV_ACCEPT,
(uint16_t)(i * 1000),
(uint16_t)(i * 1000 + 1000));
}
// Wait for 10 seconds.
std::this_thread::sleep_for(std::chrono::seconds(CONCURRENT_THREAD_RUN_TIME_IN_SECONDS));
// Stop all threads.
for (auto& thread : threads) {
thread.request_stop();
}
// Wait for all threads to stop.
for (auto& thread : threads) {
thread.join();
}
}
TEST_CASE("sock_addr_context", "[netebpfext]")
@ -426,28 +733,31 @@ TEST_CASE("sock_ops_invoke", "[netebpfext]")
{
ebpf_extension_data_t npi_specific_characteristics = {};
test_sock_ops_client_context_t client_context = {};
fwp_classify_parameters_t parameters = {};
netebpf_ext_helper_t helper(
&npi_specific_characteristics,
(_ebpf_extension_dispatch_function)netebpfext_unit_invoke_sock_ops_program,
(netebpfext_helper_base_client_context_t*)&client_context);
netebpfext_initialize_fwp_classify_parameters(&parameters);
// Do some operations that return success.
client_context.sock_ops_action = 0;
FWP_ACTION_TYPE result = helper.test_sock_ops_v4();
FWP_ACTION_TYPE result = helper.test_sock_ops_v4(&parameters);
REQUIRE(result == FWP_ACTION_PERMIT);
result = helper.test_sock_ops_v6();
result = helper.test_sock_ops_v6(&parameters);
REQUIRE(result == FWP_ACTION_PERMIT);
// Do some operations that return failure.
client_context.sock_ops_action = -1;
result = helper.test_sock_ops_v4();
result = helper.test_sock_ops_v4(&parameters);
REQUIRE(result == FWP_ACTION_BLOCK);
result = helper.test_sock_ops_v6();
result = helper.test_sock_ops_v6(&parameters);
REQUIRE(result == FWP_ACTION_BLOCK);
}