Skip to content
Merged
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
24 changes: 20 additions & 4 deletions ebpf_extensions/neteventebpfext/netevent_ebpf_ext_event.c
Original file line number Diff line number Diff line change
Expand Up @@ -74,15 +74,16 @@ typedef struct netevent_ext_header
typedef struct netevent_ext_function_addresses
{
netevent_ext_header_t header;
netevent_capture_type_t capture_type;
uint32_t helper_function_count;
uint64_t* helper_function_address;
} netevent_ext_function_addresses_t;

// Dispatch table for the client module's helper functions
static const void* _ebpf_netevent_ext_helper_functions[] = {(void*)&_ebpf_netevent_push_event};
const netevent_ext_function_addresses_t _netevent_client_dispatch = {
.header =
{.version = EBPF_HELPER_FUNCTION_ADDRESSES_CURRENT_VERSION, .size = sizeof(netevent_ext_function_addresses_t)},
netevent_ext_function_addresses_t _netevent_client_dispatch = {
.header = {.version = 2, .size = sizeof(netevent_ext_function_addresses_t)},
.capture_type = NetevenCapture_Drop,
.helper_function_count = EBPF_COUNT_OF(_ebpf_netevent_ext_helper_functions),
.helper_function_address = (uint64_t*)_ebpf_netevent_ext_helper_functions};

Expand Down Expand Up @@ -211,12 +212,27 @@ _netevent_ebpf_extension_netevent_on_client_attach(
{
ebpf_result_t result = EBPF_SUCCESS;
bool push_lock_acquired = false;
netevent_attach_opts_t* attach_opts;
const ebpf_extension_data_t* client_data = ebpf_extension_hook_client_get_client_data(attaching_client);

EBPF_EXT_LOG_ENTRY();

UNREFERENCED_PARAMETER(attaching_client);
UNREFERENCED_PARAMETER(provider_context);

if (client_data != NULL && client_data->data != NULL) {
attach_opts = (netevent_attach_opts_t*)client_data->data;
if ((attach_opts->capture_type >= NeteventCapture_All) && (attach_opts->capture_type <= NetevenCapture_None)) {
_netevent_client_dispatch.capture_type = attach_opts->capture_type;
} else {
EBPF_EXT_LOG_MESSAGE(
EBPF_EXT_TRACELOG_LEVEL_ERROR,
EBPF_EXT_TRACELOG_KEYWORD_NETEVENT,
"Incorrect capture type in attach opts.");
result = EBPF_OPERATION_NOT_SUPPORTED;
goto Exit;
}
}

ExAcquirePushLockExclusive(&_ebpf_netevent_event_hook_provider_lock);
push_lock_acquired = true;

Expand Down
16 changes: 16 additions & 0 deletions include/ebpf_netevent_hooks.h
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,22 @@ typedef struct _netevent_event_md

} netevent_event_md_t;

//
// Packet capture type
//
typedef enum _netevent_capture_type
{
NeteventCapture_All = 1,
NetevenCapture_Flow,

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

"Neteven" - missing "t"

NetevenCapture_Drop,
NetevenCapture_None
} netevent_capture_type_t;

typedef struct _netevent_attach_opts
{
netevent_capture_type_t capture_type;
} netevent_attach_opts_t;

/*
* @brief Write an event into the ring buffer.
*
Expand Down
9 changes: 9 additions & 0 deletions tests/neteventebpfext/netevent_sim/netevent_npi_client.h
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,14 @@ typedef struct
} netevent_event_info_t;
typedef void (*netevent_push_event)(netevent_event_info_t*);

typedef enum _netevent_capture_type
{
NeteventCapture_All = 1,
NetevenCapture_Flow,
NetevenCapture_Drop,
NetevenCapture_None
} netevent_capture_type_t;

typedef struct netevent_ext_header
{
uint16_t version; ///< Version of the extension data structure.
Expand All @@ -24,6 +32,7 @@ typedef struct netevent_ext_header
typedef struct netevent_ext_function_addresses
{
netevent_ext_header_t header;
netevent_capture_type_t capture_type;
uint32_t helper_function_count;
uint64_t* helper_function_address;
} netevent_ext_function_addresses_t;
18 changes: 11 additions & 7 deletions tests/neteventebpfext/netevent_sim/netevent_sim.c
Original file line number Diff line number Diff line change
Expand Up @@ -6,9 +6,9 @@
#include <wdm.h>
#include <wsk.h>
// clang-format on
#include "netevent_types.h"
#include "netevent_npi_client.h"
#include "netevent_npi_provider.h"
#include "netevent_types.h"

#include <guiddef.h>
#include <ntstrsafe.h>
Expand Down Expand Up @@ -86,7 +86,6 @@ timer_dpc_routine(

// Send the payload to the attached NMR client (if any)
if (_netevent_provider_binding_context.client_dispatch != NULL) {

// Acquire the rundown protection
if (!ExAcquireRundownProtection(&_rundown_ref)) {
// The driver is unloading, so return without doing anything
Expand All @@ -95,19 +94,24 @@ timer_dpc_routine(

// Create a test event
LONG counter = InterlockedIncrement(&_event_counter);
netevent_type_drop_t demo_drop_event = {
.header = {.event_type = NOTIFY_EVENT_TYPE_NETEVENT},
netevent_message_t demo_event = {
.header = {.event_type = NOTIFY_EVENT_TYPE_NETEVENT_LOG},
.source_ip = {192, 168, 1, 1},
.destination_ip = {10, 11, 12, 1},
.source_port = 12345,
.destination_port = 80,
.reason = DROP_REASON_SECURITY_POLICY,
.reason = DROP_REASON_NONE,
.event_counter = counter};

if (_netevent_provider_binding_context.client_dispatch->capture_type == NetevenCapture_Drop) {
demo_event.header.event_type = NOTIFY_EVENT_TYPE_NETEVENT_DROP;
demo_event.reason = DROP_REASON_SECURITY_POLICY;
}

// Create the event payload
netevent_event_info_t event_payload = {
.event_data_start = (unsigned char*)&demo_drop_event,
.event_data_end = (unsigned char*)&demo_drop_event + sizeof(demo_drop_event)};
.event_data_start = (unsigned char*)&demo_event,
.event_data_end = (unsigned char*)&demo_event + sizeof(demo_event)};

// Invoke the NPI client's push_event_helper routine
netevent_push_event push_event_helper =
Expand Down
7 changes: 4 additions & 3 deletions tests/neteventebpfext/netevent_sim/netevent_types.h
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,8 @@
//

// The event type we want to process
#define NOTIFY_EVENT_TYPE_NETEVENT 100
#define NOTIFY_EVENT_TYPE_NETEVENT_DROP 100
#define NOTIFY_EVENT_TYPE_NETEVENT_LOG 101

#pragma pack(push, 1) // Set packing to 1 byte boundary

Expand Down Expand Up @@ -37,7 +38,7 @@ typedef enum _drop_reason
DROP_REASON_BANDWIDTH_LIMIT = 3,
DROP_REASON_INACTIVE_TIMEOUT = 4,
} drop_reason;
typedef struct _netevent_type_drop
typedef struct _netevent_message
{
event_header_t header;

Expand All @@ -49,6 +50,6 @@ typedef struct _netevent_type_drop

// Event counter, for testing purposes
unsigned long event_counter;
} netevent_type_drop_t;
} netevent_message_t;

#pragma pack(pop) // Restore default packing
135 changes: 122 additions & 13 deletions tests/neteventebpfext/neteventebpfext_unit/netevent_ebpfext_unit.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -7,12 +7,15 @@
#include "cxplat_fault_injection.h"
#include "cxplat_passed_test_log.h"
#include "ebpf_netevent_hooks.h"
#include "ebpf_netevent_program_attach_type_guids.h"
#include "netevent_ebpf_ext_helper.h"
#include "netevent_ebpf_ext_program_info.h"
#include "utils.h"
#include "watchdog.h"

#include <bpf/bpf.h>
#include <bpf/libbpf.h>
#include <ebpf_api.h>
#include <iostream>
#include <string>
#include <thread>
Expand All @@ -30,23 +33,25 @@ CATCH_REGISTER_LISTENER(cxplat_passed_test_log)
struct bpf_map* netevent_event_map;
struct bpf_map* command_map;
static volatile uint32_t event_count = 0;
static volatile uint32_t log_event_count = 0;
static volatile uint32_t drop_event_count = 0;

void
_dump_event(uint8_t event_type, const char* event_descr, void* data, size_t size)
{
if (event_type == NOTIFY_EVENT_TYPE_NETEVENT && size == sizeof(netevent_type_drop_t)) {
if ((event_type == NOTIFY_EVENT_TYPE_NETEVENT_DROP || event_type == NOTIFY_EVENT_TYPE_NETEVENT_LOG) &&
size == sizeof(netevent_message_t)) {

// Cast the event and print its details
netevent_type_drop_t* demo_drop_event = reinterpret_cast<netevent_type_drop_t*>(data);
std::cout << "\rNetwork drop event [" << demo_drop_event->event_counter << "]: {"
<< "src: " << (int)demo_drop_event->source_ip.octet1 << "." << (int)demo_drop_event->source_ip.octet2
<< "." << (int)demo_drop_event->source_ip.octet3 << "." << (int)demo_drop_event->source_ip.octet4
<< ":" << demo_drop_event->source_port << ", "
<< "dst: " << (int)demo_drop_event->destination_ip.octet1 << "."
<< (int)demo_drop_event->destination_ip.octet2 << "." << (int)demo_drop_event->destination_ip.octet3
<< "." << (int)demo_drop_event->destination_ip.octet4 << ":" << demo_drop_event->destination_port
<< ", "
<< "reason: " << (int)demo_drop_event->reason;
netevent_message_t* demo_event = reinterpret_cast<netevent_message_t*>(data);
std::cout << "\rNetwork event [" << demo_event->event_counter << "]: {"
<< "src: " << (int)demo_event->source_ip.octet1 << "." << (int)demo_event->source_ip.octet2 << "."
<< (int)demo_event->source_ip.octet3 << "." << (int)demo_event->source_ip.octet4 << ":"
<< demo_event->source_port << ", "
<< "dst: " << (int)demo_event->destination_ip.octet1 << "." << (int)demo_event->destination_ip.octet2
<< "." << (int)demo_event->destination_ip.octet3 << "." << (int)demo_event->destination_ip.octet4
<< ":" << demo_event->destination_port << ", "
<< "reason: " << (int)demo_event->reason;
std::cout << "}" << std::flush;
} else {
// Simply dump the event data as hex bytes.
Expand All @@ -72,7 +77,12 @@ netevent_monitor_event_callback(void* ctx, void* data, size_t size)

// Check if this event is actually a netevent event (i.e. first byte is NOTIFY_EVENT_TYPE_NETEVENT).
uint8_t event_type = static_cast<uint8_t>(*reinterpret_cast<const std::byte*>(data));
if (event_type != NOTIFY_EVENT_TYPE_NETEVENT) {
std::cout << "event type fired" << (int)event_type << std::flush;
if (event_type == NOTIFY_EVENT_TYPE_NETEVENT_LOG) {
log_event_count++;
} else if (event_type == NOTIFY_EVENT_TYPE_NETEVENT_DROP) {
drop_event_count++;
} else {
return 0;
}
event_count++;
Expand Down Expand Up @@ -150,6 +160,105 @@ TEST_CASE("netevent_event_simulation", "[neteventebpfext]")
REQUIRE(neteventebpfext_driver.unload() == true);
}

TEST_CASE("netevent_attach_opt_simulation", "[neteventebpfext]")
{
// Free the BPF object will take some time to unload from the previous test
// Once this issue is fixed, the sleep can be removed: https://github.com/microsoft/ebpf-for-windows/issues/2667
std::this_thread::sleep_for(std::chrono::seconds(10));

// First, load the netevent simulator driver (NPI provider).
driver_service netevent_sim_driver;
REQUIRE(
netevent_sim_driver.create(L"netevent_sim", driver_service::get_driver_path("netevent_sim.sys").c_str()) ==
true);
REQUIRE(netevent_sim_driver.start() == true);

// Load and start neteventebpfext extension driver.
driver_service neteventebpfext_driver;
REQUIRE(
neteventebpfext_driver.create(
L"neteventebpfext", driver_service::get_driver_path("neteventebpfext.sys").c_str()) == true);
REQUIRE(neteventebpfext_driver.start() == true);

// Load the NetEventMonitor native BPF program.
struct bpf_object* object = bpf_object__open("netevent_monitor.sys");
REQUIRE(object != nullptr);

int res = bpf_object__load(object);
REQUIRE(res == 0);

// Find and attach to the netevent_monitor BPF program with attach opts.
auto netevent_monitor = bpf_object__find_program_by_name(object, "NetEventMonitor");
REQUIRE(netevent_monitor != nullptr);

// Attach to the eBPF ring buffer event map.
bpf_map* netevent_events_map = bpf_object__find_map_by_name(object, "netevent_events_map");
REQUIRE(netevent_events_map != nullptr);
auto ring = ring_buffer__new(bpf_map__fd(netevent_events_map), netevent_monitor_event_callback, nullptr, nullptr);
REQUIRE(ring != nullptr);

// Test attach with invalid capture type
bpf_link* netevent_monitor_link = nullptr;
netevent_attach_opts_t attach_opts = {};
attach_opts.capture_type = (netevent_capture_type_t)0;
ebpf_program_attach(
netevent_monitor, &EBPF_ATTACH_TYPE_NETEVENT, &attach_opts, sizeof(attach_opts), &netevent_monitor_link);
REQUIRE(netevent_monitor_link == nullptr);

// Test attach with capture valid capture type
uint32_t event_count_before = event_count;
uint32_t log_event_count_before = log_event_count;
uint32_t drop_event_count_before = drop_event_count;

attach_opts.capture_type = NeteventCapture_All;
ebpf_program_attach(
netevent_monitor, &EBPF_ATTACH_TYPE_NETEVENT, &attach_opts, sizeof(attach_opts), &netevent_monitor_link);
REQUIRE(netevent_monitor_link != nullptr);
std::this_thread::sleep_for(std::chrono::seconds(5));

// Detach the program (link) from the attach point.
int link_fd = bpf_link__fd(netevent_monitor_link);
bpf_link_detach(link_fd);
bpf_link__destroy(netevent_monitor_link);

// Test that only expected event counts have increased
std::this_thread::sleep_for(std::chrono::seconds(5));
REQUIRE(log_event_count_before < log_event_count);
REQUIRE((event_count - event_count_before) == (log_event_count - log_event_count_before));

// Test reattach with different capture type
event_count_before = event_count;
attach_opts.capture_type = NetevenCapture_Drop;
ebpf_program_attach(
netevent_monitor, &EBPF_ATTACH_TYPE_NETEVENT, &attach_opts, sizeof(attach_opts), &netevent_monitor_link);
REQUIRE(netevent_monitor_link != nullptr);
std::this_thread::sleep_for(std::chrono::seconds(5));

// Detach the program (link) from the attach point.
link_fd = bpf_link__fd(netevent_monitor_link);
bpf_link_detach(link_fd);
bpf_link__destroy(netevent_monitor_link);

// Test that only expected event counts have increased
std::this_thread::sleep_for(std::chrono::seconds(5));
REQUIRE(drop_event_count_before < drop_event_count);
REQUIRE((event_count - event_count_before) == (drop_event_count - drop_event_count_before));

// Close ring buffer.
ring_buffer__free(ring);

// Free the BPF object.
bpf_object__close(object);

// First, stop and unload the netevent simulator driver (NPI provider).
REQUIRE(netevent_sim_driver.stop() == true);
REQUIRE(netevent_sim_driver.unload() == true);

// Stop and unload the neteventebpfext extension driver (NPI client).
REQUIRE(neteventebpfext_driver.stop() == true);
REQUIRE(neteventebpfext_driver.unload() == true);
}

TEST_CASE("netevent_drivers_load_unload_stress", "[neteventebpfext]")
{
// Free the BPF object will take some time to unload from the previous test
Expand Down Expand Up @@ -299,7 +408,7 @@ TEST_CASE("netevent_bpf_prog_run_test", "[neteventebpfext]")
bpf_test_run_opts bpf_opts = {0};
netevent_event_md_t netevent_ctx_in = {0};
netevent_event_md_t netevent_ctx_out = {0};
unsigned char dummy_data_in[] = {NOTIFY_EVENT_TYPE_NETEVENT, 'a', 'b'};
unsigned char dummy_data_in[] = {NOTIFY_EVENT_TYPE_NETEVENT_DROP, 'a', 'b'};
const size_t dummy_data_size = sizeof(dummy_data_in);
unsigned char data_out[MAX_PACKET_SIZE] = {0};
uint32_t event_count_before = event_count;
Expand Down
Loading