Skip to content
Open
Show file tree
Hide file tree
Changes from 2 commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
17 changes: 17 additions & 0 deletions .github/workflows/reusable-test.yml
Original file line number Diff line number Diff line change
Expand Up @@ -449,6 +449,23 @@ jobs:
run: |
copy ${{github.workspace}}\${{env.BUILD_PLATFORM}}\${{env.BUILD_CONFIGURATION}}\*.log ${{github.workspace}}\${{env.BUILD_PLATFORM}}\${{env.BUILD_CONFIGURATION}}\TestLogs

- name: Copy fault injection logs to TestLogs
if: always() && (inputs.fault_injection == true) && (steps.skip_check.outputs.should_skip != 'true')
shell: pwsh
run: |
$outputDirectory = "${{github.workspace}}\${{env.BUILD_PLATFORM}}\${{env.BUILD_CONFIGURATION}}"
[array]$faultInjectionLogs =
Get-ChildItem -LiteralPath $outputDirectory -File |
Where-Object { $_.Name -match '\.(fault|passed)\.log$' }
if ($faultInjectionLogs.Count -eq 0) {
Write-Warning "No fault injection logs were found in $outputDirectory"
return
}

$testLogDirectory = Join-Path $outputDirectory "TestLogs"
New-Item -ItemType Directory -Path $testLogDirectory -Force | Out-Null
Copy-Item -LiteralPath $faultInjectionLogs.FullName -Destination $testLogDirectory

- name: Check for crash dumps
# Check for crash dumps even if the workflow failed.
if: always() && (steps.skip_check.outputs.should_skip != 'true')
Expand Down
6 changes: 4 additions & 2 deletions ebpf-for-windows.sln
Original file line number Diff line number Diff line change
Expand Up @@ -2749,8 +2749,10 @@ Global
{E619B985-44B3-4292-A585-5CD0C4315ED3}.MinSizeRel|x64.Build.0 = Debug|x64
{E619B985-44B3-4292-A585-5CD0C4315ED3}.MinSizeRel|x86.ActiveCfg = NativeOnlyDebug|x64
{E619B985-44B3-4292-A585-5CD0C4315ED3}.MinSizeRel|x86.Build.0 = NativeOnlyDebug|x64
{E619B985-44B3-4292-A585-5CD0C4315ED3}.NativeOnlyDebug|ARM64.ActiveCfg = Debug|ARM64
{E619B985-44B3-4292-A585-5CD0C4315ED3}.NativeOnlyDebug|x64.ActiveCfg = Debug|x64
{E619B985-44B3-4292-A585-5CD0C4315ED3}.NativeOnlyDebug|ARM64.ActiveCfg = NativeOnlyDebug|ARM64
{E619B985-44B3-4292-A585-5CD0C4315ED3}.NativeOnlyDebug|ARM64.Build.0 = NativeOnlyDebug|ARM64
{E619B985-44B3-4292-A585-5CD0C4315ED3}.NativeOnlyDebug|x64.ActiveCfg = NativeOnlyDebug|x64
{E619B985-44B3-4292-A585-5CD0C4315ED3}.NativeOnlyDebug|x64.Build.0 = NativeOnlyDebug|x64
{E619B985-44B3-4292-A585-5CD0C4315ED3}.NativeOnlyDebug|x86.ActiveCfg = NativeOnlyDebug|x64
{E619B985-44B3-4292-A585-5CD0C4315ED3}.NativeOnlyDebug|x86.Build.0 = NativeOnlyDebug|x64
{E619B985-44B3-4292-A585-5CD0C4315ED3}.NativeOnlyRelease|ARM64.ActiveCfg = Release|ARM64
Expand Down
88 changes: 83 additions & 5 deletions tests/netebpfext_unit/netebpf_ext_helper.cpp
Original file line number Diff line number Diff line change
@@ -1,12 +1,55 @@
// Copyright (c) eBPF for Windows contributors
// SPDX-License-Identifier: MIT

#include "cxplat_fault_injection.h"
#include "ebpf_platform.h"
#include "net_ebpf_ext_sock_addr.h"
#include "netebpf_ext_helper.h"
#include "usersim/common.h"

DEVICE_OBJECT* _net_ebpf_ext_driver_device_object;
class _usersim_fault_injection_suspension
{
public:
_usersim_fault_injection_suspension(bool suspend) : _suspended(suspend)
{
if (_suspended) {
usersim_fault_injection_suspend();
}
}

~_usersim_fault_injection_suspension()
{
if (_suspended) {
usersim_fault_injection_resume();
}
}

private:
bool _suspended;
};

const GUID* const _expected_program_info_providers[] = {
&EBPF_PROGRAM_TYPE_BIND,
&EBPF_PROGRAM_TYPE_CGROUP_SOCK_ADDR,
&EBPF_PROGRAM_TYPE_SOCK_OPS,
};

constexpr bpf_attach_type_t _expected_hook_providers[] = {
BPF_ATTACH_TYPE_BIND,
BPF_CGROUP_INET4_CONNECT,
BPF_CGROUP_INET4_RECV_ACCEPT,
BPF_CGROUP_INET6_CONNECT,
BPF_CGROUP_INET6_RECV_ACCEPT,
BPF_CGROUP_INET4_CONNECT_AUTHORIZATION,
BPF_CGROUP_INET6_CONNECT_AUTHORIZATION,
BPF_CGROUP_INET4_BIND,
BPF_CGROUP_INET6_BIND,
BPF_CGROUP_INET4_LISTEN,
BPF_CGROUP_INET6_LISTEN,
BPF_CGROUP_SOCK_OPS,
};

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};
// _get_sock_addr_action() uses destination_port % SOCK_ADDR_TEST_ACTION_ROUND_ROBIN
Expand Down Expand Up @@ -41,17 +84,21 @@ netebpfext_initialize_fwp_classify_parameters(_Out_ fwp_classify_parameters_t* p
parameters->user_id = _test_user_id;
}

_netebpf_ext_helper::_netebpf_ext_helper(bool initialize_platform)
: _netebpf_ext_helper(nullptr, nullptr, nullptr, initialize_platform)
_netebpf_ext_helper::_netebpf_ext_helper(bool initialize_platform, fault_injection_policy_t fault_injection_policy)
: _netebpf_ext_helper(nullptr, nullptr, nullptr, initialize_platform, fault_injection_policy)
{
}

_netebpf_ext_helper::_netebpf_ext_helper(
_In_opt_ const void* npi_specific_characteristics,
_In_opt_ _ebpf_extension_dispatch_function dispatch_function,
_In_opt_ netebpfext_helper_base_client_context_t* client_context,
bool initialize_platform)
bool initialize_platform,
fault_injection_policy_t fault_injection_policy)
{
_usersim_fault_injection_suspension fault_injection_suspension(
fault_injection_policy == fault_injection_policy_t::suspend);

// Do not use REQUIRE() in this constructor or the destructor will never be called
// to clean up any state allocated before the REQUIRE.

Expand Down Expand Up @@ -91,6 +138,7 @@ _netebpf_ext_helper::_netebpf_ext_helper(
if (dispatch_function != nullptr && client_context != nullptr) {
hook_client.ClientRegistrationInstance.NpiSpecificCharacteristics = npi_specific_characteristics;
client_context->helper = this;
hook_client_context = client_context;
nmr_hook_client_handle = std::make_unique<nmr_client_registration_t>(&hook_client, client_context);
}

Expand All @@ -100,6 +148,9 @@ _netebpf_ext_helper::_netebpf_ext_helper(

_netebpf_ext_helper::~_netebpf_ext_helper()
{
constexpr bool suspend_fault_injection = true;
_usersim_fault_injection_suspension fault_injection_suspension(suspend_fault_injection);

if (nmr_hook_client_handle) {
nmr_hook_client_handle.reset(nullptr);
}
Expand Down Expand Up @@ -130,6 +181,27 @@ _netebpf_ext_helper::~_netebpf_ext_helper()
}
}

void
_netebpf_ext_helper::require_initialized() const
{
REQUIRE(nmr_program_info_client_handle != nullptr);
REQUIRE(nmr_program_info_client_handle->is_registered());
REQUIRE(program_info_providers.size() == EBPF_COUNT_OF(_expected_program_info_providers));
for (const auto* provider : _expected_program_info_providers) {
REQUIRE(program_info_providers.contains(*provider));
}

if (hook_client_context != nullptr) {
REQUIRE(nmr_hook_client_handle != nullptr);
REQUIRE(nmr_hook_client_handle->is_registered());
REQUIRE(hook_client_context->provider_binding_context != nullptr);
REQUIRE(
hook_provider_binding_count == (hook_client_context->desired_attach_types.empty()
? EBPF_COUNT_OF(_expected_hook_providers)
: hook_client_context->desired_attach_types.size()));
}
}

std::vector<GUID>
_netebpf_ext_helper::program_info_provider_guids()
{
Expand Down Expand Up @@ -214,12 +286,18 @@ _netebpf_ext_helper::_hook_client_attach_provider(
return STATUS_ACCESS_DENIED;
}

return NmrClientAttachProvider(
NTSTATUS status = NmrClientAttachProvider(
nmr_binding_handle,
client_context, // Client binding context.
&client_dispatch_table,
&base_client_context->provider_binding_context,
&provider_dispatch_table);
if (NT_SUCCESS(status)) {
// No synchronization is required here because usersim NMR executes pending attach actions serially in
// perform_bind().
base_client_context->helper->hook_provider_binding_count++;
}
return status;
}

NTSTATUS
Expand Down
24 changes: 22 additions & 2 deletions tests/netebpfext_unit/netebpf_ext_helper.h
Original file line number Diff line number Diff line change
Expand Up @@ -34,16 +34,28 @@ typedef struct _netebpfext_helper_base_client_context
typedef class _netebpf_ext_helper
{
public:
enum class fault_injection_policy_t
{
suspend,
allow,
};

// If the caller invokes platform functions itself, the caller must pass initialize_platform = false
// and initialize/terminate the platform itself as needed.
_netebpf_ext_helper(bool initialize_platform = true);
_netebpf_ext_helper(
bool initialize_platform = true,
fault_injection_policy_t fault_injection_policy = fault_injection_policy_t::suspend);
_netebpf_ext_helper(
_In_opt_ const void* npi_specific_characteristics,
_In_opt_ _ebpf_extension_dispatch_function dispatch_function,
_In_opt_ netebpfext_helper_base_client_context_t* client_context,
bool initialize_platform = true);
bool initialize_platform = true,
fault_injection_policy_t fault_injection_policy = fault_injection_policy_t::suspend);
~_netebpf_ext_helper();

void
require_initialized() const;

std::vector<GUID>
program_info_provider_guids();

Expand Down Expand Up @@ -216,6 +228,12 @@ typedef class _netebpf_ext_helper
}
}

bool
is_registered() const
{
return nmr_client_handle != INVALID_HANDLE_VALUE;
}
Comment thread
shankarseal marked this conversation as resolved.

HANDLE nmr_client_handle;
} nmr_client_registration_t;

Expand Down Expand Up @@ -282,6 +300,8 @@ typedef class _netebpf_ext_helper
};

_ebpf_extension_dispatch_function hook_invoke_function = nullptr;
netebpfext_helper_base_client_context_t* hook_client_context = nullptr;
size_t hook_provider_binding_count = 0;

std::unique_ptr<nmr_client_registration_t> nmr_program_info_client_handle;
std::unique_ptr<nmr_client_registration_t> nmr_hook_client_handle;
Expand Down
Loading
Loading