// Copyright (c) Microsoft Corporation // SPDX-License-Identifier: MIT /*++ Abstract: This file implements the classifyFn, notifyFn, and flowDeleteFn callouts functions for: Layer 2 network receive Resource Acquire Resource Release Environment: Kernel mode --*/ #include "net_ebpf_ext.h" #include "net_ebpf_ext_bind.h" #include "net_ebpf_ext_sock_addr.h" #include "net_ebpf_ext_sock_ops.h" #include "net_ebpf_ext_xdp.h" #define SUBLAYER_WEIGHT_MAXIMUM 0xFFFF // Globals. NDIS_HANDLE _net_ebpf_ext_ndis_handle = NULL; NDIS_HANDLE _net_ebpf_ext_nbl_pool_handle = NULL; HANDLE _net_ebpf_ext_l2_injection_handle = NULL; static bool _net_ebpf_xdp_providers_registered = false; static bool _net_ebpf_bind_providers_registered = false; static bool _net_ebpf_sock_addr_providers_registered = false; static bool _net_ebpf_sock_ops_providers_registered = false; static net_ebpf_ext_sublayer_info_t _net_ebpf_ext_sublayers[] = { {&EBPF_DEFAULT_SUBLAYER, L"EBPF Sub-Layer", L"Sub-Layer for use by eBPF callouts", 0, SUBLAYER_WEIGHT_MAXIMUM}, {&EBPF_HOOK_CGROUP_CONNECT_V4_SUBLAYER, L"EBPF CGroup Connect V4 Sub-Layer", L"Sub-Layer for use by eBPF connect redirect callouts", 0, SUBLAYER_WEIGHT_MAXIMUM}, {&EBPF_HOOK_CGROUP_CONNECT_V6_SUBLAYER, L"EBPF CGroup Connect V6 Sub-Layer", L"Sub-Layer for use by eBPF connect redirect callouts", 0, SUBLAYER_WEIGHT_MAXIMUM}}; static void _net_ebpf_ext_flow_delete(uint16_t layer_id, uint32_t callout_id, uint64_t flow_context); NTSTATUS net_ebpf_ext_filter_change_notify( FWPS_CALLOUT_NOTIFY_TYPE callout_notification_type, _In_ const GUID* filter_key, _Inout_ FWPS_FILTER* filter); typedef struct _net_ebpf_ext_wfp_callout_state { const GUID* callout_guid; const GUID* layer_guid; FWPS_CALLOUT_CLASSIFY_FN classify_fn; FWPS_CALLOUT_NOTIFY_FN notify_fn; FWPS_CALLOUT_FLOW_DELETE_NOTIFY_FN delete_fn; wchar_t* name; wchar_t* description; FWP_ACTION_TYPE filter_action_type; uint32_t assigned_callout_id; } net_ebpf_ext_wfp_callout_state_t; static net_ebpf_ext_wfp_callout_state_t _net_ebpf_ext_wfp_callout_states[] = { // EBPF_HOOK_OUTBOUND_L2 { &EBPF_HOOK_OUTBOUND_L2_CALLOUT, &FWPM_LAYER_OUTBOUND_MAC_FRAME_NATIVE, net_ebpf_ext_layer_2_classify, net_ebpf_ext_filter_change_notify, _net_ebpf_ext_flow_delete, L"L2 Outbound", L"L2 Outbound Callout for eBPF", FWP_ACTION_CALLOUT_TERMINATING, }, // EBPF_HOOK_INBOUND_L2 { &EBPF_HOOK_INBOUND_L2_CALLOUT, &FWPM_LAYER_INBOUND_MAC_FRAME_NATIVE, net_ebpf_ext_layer_2_classify, net_ebpf_ext_filter_change_notify, _net_ebpf_ext_flow_delete, L"L2 Inbound", L"L2 Inbound Callout for eBPF", FWP_ACTION_CALLOUT_TERMINATING, }, // EBPF_HOOK_ALE_RESOURCE_ALLOC_V4 { &EBPF_HOOK_ALE_RESOURCE_ALLOC_V4_CALLOUT, &FWPM_LAYER_ALE_RESOURCE_ASSIGNMENT_V4, net_ebpf_ext_resource_allocation_classify, net_ebpf_ext_filter_change_notify, _net_ebpf_ext_flow_delete, L"Resource Allocation v4", L"Resource Allocation v4 callout for eBPF", FWP_ACTION_CALLOUT_TERMINATING, }, // EBPF_HOOK_ALE_RESOURCE_RELEASE_V4 { &EBPF_HOOK_ALE_RESOURCE_RELEASE_V4_CALLOUT, &FWPM_LAYER_ALE_RESOURCE_RELEASE_V4, net_ebpf_ext_resource_release_classify, net_ebpf_ext_filter_change_notify, _net_ebpf_ext_flow_delete, L"Resource Release v4", L"Resource Release v4 callout for eBPF", FWP_ACTION_CALLOUT_TERMINATING, }, // EBPF_HOOK_ALE_RESOURCE_ALLOC_V6 { &EBPF_HOOK_ALE_RESOURCE_ALLOC_V6_CALLOUT, &FWPM_LAYER_ALE_RESOURCE_ASSIGNMENT_V6, net_ebpf_ext_resource_allocation_classify, net_ebpf_ext_filter_change_notify, _net_ebpf_ext_flow_delete, L"Resource Allocation v6", L"Resource Allocation v6 callout for eBPF", FWP_ACTION_CALLOUT_TERMINATING, }, // EBPF_HOOK_ALE_RESOURCE_RELEASE_V6 { &EBPF_HOOK_ALE_RESOURCE_RELEASE_V6_CALLOUT, &FWPM_LAYER_ALE_RESOURCE_RELEASE_V6, net_ebpf_ext_resource_release_classify, net_ebpf_ext_filter_change_notify, _net_ebpf_ext_flow_delete, L"Resource Release eBPF Callout v6", L"Resource Release callout for eBPF", FWP_ACTION_CALLOUT_TERMINATING, }, // EBPF_HOOK_ALE_AUTH_CONNECT_V4 { &EBPF_HOOK_ALE_AUTH_CONNECT_V4_CALLOUT, &FWPM_LAYER_ALE_AUTH_CONNECT_V4, net_ebpf_extension_sock_addr_authorize_connection_classify, net_ebpf_ext_filter_change_notify, _net_ebpf_ext_flow_delete, L"ALE Authorize Connect eBPF Callout v4", L"ALE Authorize Connect callout for eBPF", FWP_ACTION_CALLOUT_TERMINATING, }, // EBPF_HOOK_ALE_AUTH_CONNECT_V6 { &EBPF_HOOK_ALE_AUTH_CONNECT_V6_CALLOUT, &FWPM_LAYER_ALE_AUTH_CONNECT_V6, net_ebpf_extension_sock_addr_authorize_connection_classify, net_ebpf_ext_filter_change_notify, _net_ebpf_ext_flow_delete, L"ALE Authorize Connect eBPF Callout v6", L"ALE Authorize Connect callout for eBPF", FWP_ACTION_CALLOUT_TERMINATING, }, // EBPF_HOOK_ALE_AUTH_RECV_ACCEPT_V4 { &EBPF_HOOK_ALE_AUTH_RECV_ACCEPT_V4_CALLOUT, &FWPM_LAYER_ALE_AUTH_RECV_ACCEPT_V4, net_ebpf_extension_sock_addr_authorize_recv_accept_classify, net_ebpf_ext_filter_change_notify, _net_ebpf_ext_flow_delete, L"ALE Authorize Receive or Accept eBPF Callout v4", L"ALE Authorize Receive or Accept callout for eBPF", FWP_ACTION_CALLOUT_TERMINATING, }, // EBPF_HOOK_ALE_AUTH_RECV_ACCEPT_V6 { &EBPF_HOOK_ALE_AUTH_RECV_ACCEPT_V6_CALLOUT, &FWPM_LAYER_ALE_AUTH_RECV_ACCEPT_V6, net_ebpf_extension_sock_addr_authorize_recv_accept_classify, net_ebpf_ext_filter_change_notify, _net_ebpf_ext_flow_delete, L"ALE Authorize Receive or Accept eBPF Callout v6", L"ALE Authorize Receive or Accept callout for eBPF", FWP_ACTION_CALLOUT_TERMINATING, }, // EBPF_HOOK_ALE_CONNECT_REDIRECT_V4 { &EBPF_HOOK_ALE_CONNECT_REDIRECT_V4_CALLOUT, &FWPM_LAYER_ALE_CONNECT_REDIRECT_V4, net_ebpf_extension_sock_addr_redirect_connection_classify, net_ebpf_ext_filter_change_notify, _net_ebpf_ext_flow_delete, L"ALE Connect Redirect eBPF Callout v4", L"ALE Connect Redirect callout for eBPF", FWP_ACTION_CALLOUT_TERMINATING, }, // EBPF_HOOK_ALE_CONNECT_REDIRECT_V6 { &EBPF_HOOK_ALE_CONNECT_REDIRECT_V6_CALLOUT, &FWPM_LAYER_ALE_CONNECT_REDIRECT_V6, net_ebpf_extension_sock_addr_redirect_connection_classify, net_ebpf_ext_filter_change_notify, _net_ebpf_ext_flow_delete, L"ALE Connect Redirect eBPF Callout v6", L"ALE Connect Redirect callout for eBPF", FWP_ACTION_CALLOUT_TERMINATING, }, // EBPF_HOOK_ALE_FLOW_ESTABLISHED_V4 { &EBPF_HOOK_ALE_FLOW_ESTABLISHED_V4_CALLOUT, &FWPM_LAYER_ALE_FLOW_ESTABLISHED_V4, net_ebpf_extension_sock_ops_flow_established_classify, net_ebpf_ext_filter_change_notify, net_ebpf_extension_sock_ops_flow_delete, L"ALE Flow Established Callout v4", L"ALE Flow Established callout for eBPF", FWP_ACTION_CALLOUT_TERMINATING, }, // EBPF_HOOK_ALE_FLOW_ESTABLISHED_V6 { &EBPF_HOOK_ALE_FLOW_ESTABLISHED_V6_CALLOUT, &FWPM_LAYER_ALE_FLOW_ESTABLISHED_V6, net_ebpf_extension_sock_ops_flow_established_classify, net_ebpf_ext_filter_change_notify, net_ebpf_extension_sock_ops_flow_delete, L"ALE Flow Established Callout v4", L"ALE Flow Established callout for eBPF", FWP_ACTION_CALLOUT_TERMINATING, }}; // WFP globals static HANDLE _fwp_engine_handle; // // WFP component management related utility functions. // _Must_inspect_result_ ebpf_result_t net_ebpf_extension_wfp_filter_context_create( size_t filter_context_size, _In_ const net_ebpf_extension_hook_client_t* client_context, _Outptr_ net_ebpf_extension_wfp_filter_context_t** filter_context) { ebpf_result_t result = EBPF_SUCCESS; net_ebpf_extension_wfp_filter_context_t* local_filter_context = NULL; NET_EBPF_EXT_LOG_ENTRY(); *filter_context = NULL; // Allocate buffer for WFP filter context. local_filter_context = (net_ebpf_extension_wfp_filter_context_t*)ExAllocatePoolUninitialized( NonPagedPoolNx, filter_context_size, NET_EBPF_EXTENSION_POOL_TAG); NET_EBPF_EXT_BAIL_ON_ALLOC_FAILURE_RESULT( NET_EBPF_EXT_TRACELOG_KEYWORD_EXTENSION, local_filter_context, "local_filter_context", result); memset(local_filter_context, 0, filter_context_size); local_filter_context->reference_count = 1; // Initial reference. local_filter_context->client_context = client_context; if (!net_ebpf_extension_hook_client_enter_rundown( (net_ebpf_extension_hook_client_t*)local_filter_context->client_context)) { // We're setting up the filter context here and as this is the very first (and exclusive) attempt to acquire // rundown, it cannot fail. If it does, this is indicative of a fatal system level error. __fastfail(FAST_FAIL_INVALID_ARG); } *filter_context = local_filter_context; local_filter_context = NULL; Exit: if (local_filter_context != NULL) { ExFreePool(local_filter_context); } NET_EBPF_EXT_RETURN_RESULT(result); } void net_ebpf_extension_wfp_filter_context_cleanup(_Frees_ptr_ net_ebpf_extension_wfp_filter_context_t* filter_context) { // Since the hook client is detaching, the eBPF program should not be invoked any further. // The client_detached field in filter_context is set to false for this reason. This way any // lingering WFP classify callbacks will exit as it would not find any hook client associated // with the filter context. This is best effort & no locks are held. filter_context->client_detached = TRUE; DEREFERENCE_FILTER_CONTEXT(filter_context); } net_ebpf_extension_hook_id_t net_ebpf_extension_get_hook_id_from_wfp_layer_id(uint16_t wfp_layer_id) { net_ebpf_extension_hook_id_t hook_id = (net_ebpf_extension_hook_id_t)0; switch (wfp_layer_id) { case FWPS_LAYER_OUTBOUND_MAC_FRAME_NATIVE: hook_id = EBPF_HOOK_OUTBOUND_L2; break; case FWPS_LAYER_INBOUND_MAC_FRAME_NATIVE: hook_id = EBPF_HOOK_INBOUND_L2; break; case FWPS_LAYER_ALE_RESOURCE_ASSIGNMENT_V4: hook_id = EBPF_HOOK_ALE_RESOURCE_ALLOC_V4; break; case FWPS_LAYER_ALE_RESOURCE_ASSIGNMENT_V6: hook_id = EBPF_HOOK_ALE_RESOURCE_ALLOC_V6; break; case FWPS_LAYER_ALE_RESOURCE_RELEASE_V4: hook_id = EBPF_HOOK_ALE_RESOURCE_RELEASE_V4; break; case FWPS_LAYER_ALE_RESOURCE_RELEASE_V6: hook_id = EBPF_HOOK_ALE_RESOURCE_RELEASE_V6; break; case FWPS_LAYER_ALE_AUTH_CONNECT_V4: hook_id = EBPF_HOOK_ALE_AUTH_CONNECT_V4; break; case FWPS_LAYER_ALE_AUTH_RECV_ACCEPT_V4: hook_id = EBPF_HOOK_ALE_AUTH_RECV_ACCEPT_V4; break; case FWPS_LAYER_ALE_AUTH_CONNECT_V6: hook_id = EBPF_HOOK_ALE_AUTH_CONNECT_V6; break; case FWPS_LAYER_ALE_AUTH_RECV_ACCEPT_V6: hook_id = EBPF_HOOK_ALE_AUTH_RECV_ACCEPT_V6; break; case FWPS_LAYER_ALE_FLOW_ESTABLISHED_V4: hook_id = EBPF_HOOK_ALE_FLOW_ESTABLISHED_V4; break; case FWPS_LAYER_ALE_FLOW_ESTABLISHED_V6: hook_id = EBPF_HOOK_ALE_FLOW_ESTABLISHED_V6; break; case FWPS_LAYER_ALE_CONNECT_REDIRECT_V4: hook_id = EBPF_HOOK_ALE_CONNECT_REDIRECT_V4; break; case FWPS_LAYER_ALE_CONNECT_REDIRECT_V6: hook_id = EBPF_HOOK_ALE_CONNECT_REDIRECT_V6; break; default: ASSERT(FALSE); break; } return hook_id; } uint32_t net_ebpf_extension_get_callout_id_for_hook(net_ebpf_extension_hook_id_t hook_id) { uint32_t callout_id = 0; if (hook_id < EBPF_COUNT_OF(_net_ebpf_ext_wfp_callout_states)) { callout_id = _net_ebpf_ext_wfp_callout_states[hook_id].assigned_callout_id; } return callout_id; } void net_ebpf_extension_delete_wfp_filters( uint32_t filter_count, _Frees_ptr_ _In_count_(filter_count) net_ebpf_ext_wfp_filter_id_t* filter_ids) { NET_EBPF_EXT_LOG_ENTRY(); NTSTATUS status = STATUS_SUCCESS; for (uint32_t index = 0; index < filter_count; index++) { status = FwpmFilterDeleteById(_fwp_engine_handle, filter_ids[index].id); if (!NT_SUCCESS(status)) { NET_EBPF_EXT_LOG_NTSTATUS_API_FAILURE( NET_EBPF_EXT_TRACELOG_KEYWORD_EXTENSION, "FwpmFilterDeleteById", status); } else { NET_EBPF_EXT_LOG_MESSAGE_UINT64_UINT64( NET_EBPF_EXT_TRACELOG_LEVEL_VERBOSE, NET_EBPF_EXT_TRACELOG_KEYWORD_EXTENSION, "Marked WFP filter for deletion: ", index, filter_ids[index].id); } } NET_EBPF_EXT_LOG_EXIT(); } _Must_inspect_result_ ebpf_result_t net_ebpf_extension_add_wfp_filters( uint32_t filter_count, _In_count_(filter_count) const net_ebpf_extension_wfp_filter_parameters_t* parameters, uint32_t condition_count, _In_opt_count_(condition_count) const FWPM_FILTER_CONDITION* conditions, _Inout_ net_ebpf_extension_wfp_filter_context_t* filter_context, _Outptr_result_buffer_maybenull_(filter_count) net_ebpf_ext_wfp_filter_id_t** filter_ids) { NTSTATUS status = STATUS_SUCCESS; ebpf_result_t result = EBPF_SUCCESS; bool is_in_transaction = FALSE; net_ebpf_ext_wfp_filter_id_t* local_filter_ids = NULL; *filter_ids = NULL; NET_EBPF_EXT_LOG_ENTRY(); if (filter_count == 0) { NET_EBPF_EXT_LOG_MESSAGE( NET_EBPF_EXT_TRACELOG_LEVEL_ERROR, NET_EBPF_EXT_TRACELOG_KEYWORD_EXTENSION, "Filter count is 0"); result = EBPF_INVALID_ARGUMENT; goto Exit; } local_filter_ids = (net_ebpf_ext_wfp_filter_id_t*)ExAllocatePoolUninitialized( NonPagedPoolNx, (sizeof(net_ebpf_ext_wfp_filter_id_t) * filter_count), NET_EBPF_EXTENSION_POOL_TAG); NET_EBPF_EXT_BAIL_ON_ALLOC_FAILURE_RESULT( NET_EBPF_EXT_TRACELOG_KEYWORD_EXTENSION, local_filter_ids, "local_filter_ids", result); memset(local_filter_ids, 0, (sizeof(net_ebpf_ext_wfp_filter_id_t) * filter_count)); status = FwpmTransactionBegin(_fwp_engine_handle, 0); if (!NT_SUCCESS(status)) { NET_EBPF_EXT_LOG_NTSTATUS_API_FAILURE(NET_EBPF_EXT_TRACELOG_KEYWORD_EXTENSION, "FwpmTransactionBegin", status); result = EBPF_INVALID_ARGUMENT; goto Exit; } is_in_transaction = TRUE; for (uint32_t index = 0; index < filter_count; index++) { FWPM_FILTER filter = {0}; uint64_t local_filter_id = 0; const net_ebpf_extension_wfp_filter_parameters_t* filter_parameter = ¶meters[index]; filter.layerKey = *filter_parameter->layer_guid; filter.displayData.name = (wchar_t*)filter_parameter->name; filter.displayData.description = (wchar_t*)filter_parameter->description; filter.providerKey = (GUID*)&EBPF_WFP_PROVIDER; filter.action.type = FWP_ACTION_CALLOUT_TERMINATING; filter.action.calloutKey = *filter_parameter->callout_guid; filter.filterCondition = (FWPM_FILTER_CONDITION*)conditions; filter.numFilterConditions = condition_count; if (filter_parameter->sublayer_guid != NULL) { filter.subLayerKey = *(filter_parameter->sublayer_guid); } else { filter.subLayerKey = EBPF_DEFAULT_SUBLAYER; } filter.weight.type = FWP_EMPTY; // auto-weight. REFERENCE_FILTER_CONTEXT(filter_context); filter.rawContext = (uint64_t)(uintptr_t)filter_context; status = FwpmFilterAdd(_fwp_engine_handle, &filter, NULL, &local_filter_id); if (!NT_SUCCESS(status)) { NET_EBPF_EXT_LOG_NTSTATUS_API_FAILURE_MESSAGE_STRING( NET_EBPF_EXT_TRACELOG_KEYWORD_EXTENSION, "FwpmFilterAdd", status, "Failed to add filter", (char*)filter_parameter->name); result = EBPF_INVALID_ARGUMENT; goto Exit; } else { local_filter_ids[index].id = local_filter_id; local_filter_ids[index].name = (wchar_t*)filter_parameter->name; local_filter_ids[index].state = NET_EBPF_EXT_WFP_FILTER_ADDED; NET_EBPF_EXT_LOG_MESSAGE_UINT64_UINT64( NET_EBPF_EXT_TRACELOG_LEVEL_VERBOSE, NET_EBPF_EXT_TRACELOG_KEYWORD_EXTENSION, "Added WFP filter: ", index, local_filter_id); } } status = FwpmTransactionCommit(_fwp_engine_handle); if (!NT_SUCCESS(status)) { NET_EBPF_EXT_LOG_NTSTATUS_API_FAILURE(NET_EBPF_EXT_TRACELOG_KEYWORD_EXTENSION, "FwpmTransactionCommit", status); result = EBPF_INVALID_ARGUMENT; goto Exit; } is_in_transaction = FALSE; *filter_ids = local_filter_ids; Exit: if (!NT_SUCCESS(status)) { if (local_filter_ids != NULL) { ExFreePool(local_filter_ids); } if (is_in_transaction) { status = FwpmTransactionAbort(_fwp_engine_handle); if (!NT_SUCCESS(status)) { NET_EBPF_EXT_LOG_NTSTATUS_API_FAILURE( NET_EBPF_EXT_TRACELOG_KEYWORD_EXTENSION, "FwpmTransactionAbort", status); } } } NET_EBPF_EXT_RETURN_RESULT(result); } static NTSTATUS _net_ebpf_ext_register_wfp_callout(_Inout_ net_ebpf_ext_wfp_callout_state_t* callout_state, _Inout_ void* device_object) /* ++ This function registers callouts and filters. -- */ { NTSTATUS status = STATUS_SUCCESS; NET_EBPF_EXT_LOG_ENTRY(); FWPS_CALLOUT callout_register_state = {0}; FWPM_CALLOUT callout_add_state = {0}; FWPM_DISPLAY_DATA display_data = {0}; BOOLEAN was_callout_registered = FALSE; callout_register_state.calloutKey = *callout_state->callout_guid; callout_register_state.classifyFn = callout_state->classify_fn; callout_register_state.notifyFn = callout_state->notify_fn; callout_register_state.flowDeleteFn = callout_state->delete_fn; callout_register_state.flags = 0; status = FwpsCalloutRegister(device_object, &callout_register_state, &callout_state->assigned_callout_id); if (!NT_SUCCESS(status)) { NET_EBPF_EXT_LOG_NTSTATUS_API_FAILURE_MESSAGE_STRING( NET_EBPF_EXT_TRACELOG_KEYWORD_EXTENSION, "FwpsCalloutRegister", status, "Failed to register callout", (char*)callout_state->name); goto Exit; } was_callout_registered = TRUE; display_data.name = callout_state->name; display_data.description = callout_state->description; callout_add_state.calloutKey = *callout_state->callout_guid; callout_add_state.displayData = display_data; callout_add_state.providerKey = (GUID*)&EBPF_WFP_PROVIDER; callout_add_state.applicableLayer = *callout_state->layer_guid; status = FwpmCalloutAdd(_fwp_engine_handle, &callout_add_state, NULL, NULL); if (!NT_SUCCESS(status)) { NET_EBPF_EXT_LOG_NTSTATUS_API_FAILURE_MESSAGE_STRING( NET_EBPF_EXT_TRACELOG_KEYWORD_EXTENSION, "FwpmCalloutAdd", status, "Failed to add callout", (char*)callout_state->name); goto Exit; } Exit: if (!NT_SUCCESS(status)) { if (was_callout_registered) { status = FwpsCalloutUnregisterById(callout_state->assigned_callout_id); if (!NT_SUCCESS(status)) { NET_EBPF_EXT_LOG_NTSTATUS_API_FAILURE( NET_EBPF_EXT_TRACELOG_KEYWORD_EXTENSION, "FwpsCalloutUnregisterById", status); } else { callout_state->assigned_callout_id = 0; } } } NET_EBPF_EXT_RETURN_NTSTATUS(status); } NTSTATUS net_ebpf_ext_initialize_ndis_handles(_In_ const DRIVER_OBJECT* driver_object) { NTSTATUS status = STATUS_SUCCESS; NET_BUFFER_LIST_POOL_PARAMETERS nbl_pool_parameters = {0}; NDIS_HANDLE local_net_ebpf_ext_ndis_handle = NULL; NDIS_HANDLE local_net_ebpf_ext_nbl_pool_handle = NULL; NET_EBPF_EXT_LOG_ENTRY(); local_net_ebpf_ext_ndis_handle = NdisAllocateGenericObject((DRIVER_OBJECT*)driver_object, NET_EBPF_EXTENSION_POOL_TAG, 0); if (local_net_ebpf_ext_ndis_handle == NULL) { status = STATUS_INSUFFICIENT_RESOURCES; NET_EBPF_EXT_LOG_NTSTATUS_API_FAILURE( NET_EBPF_EXT_TRACELOG_KEYWORD_EXTENSION, "NdisAllocateGenericObject", status); goto Exit; } nbl_pool_parameters.Header.Type = NDIS_OBJECT_TYPE_DEFAULT; nbl_pool_parameters.Header.Revision = NET_BUFFER_LIST_POOL_PARAMETERS_REVISION_1; nbl_pool_parameters.Header.Size = NDIS_SIZEOF_NET_BUFFER_LIST_POOL_PARAMETERS_REVISION_1; nbl_pool_parameters.ProtocolId = NDIS_PROTOCOL_ID_DEFAULT; nbl_pool_parameters.fAllocateNetBuffer = TRUE; nbl_pool_parameters.DataSize = 0; nbl_pool_parameters.PoolTag = NET_EBPF_EXTENSION_POOL_TAG; local_net_ebpf_ext_nbl_pool_handle = NdisAllocateNetBufferListPool(local_net_ebpf_ext_ndis_handle, &nbl_pool_parameters); if (local_net_ebpf_ext_nbl_pool_handle == NULL) { status = STATUS_INSUFFICIENT_RESOURCES; NET_EBPF_EXT_LOG_NTSTATUS_API_FAILURE( NET_EBPF_EXT_TRACELOG_KEYWORD_EXTENSION, "NdisAllocateNetBufferListPool", status); NdisFreeGenericObject((PNDIS_GENERIC_OBJECT)local_net_ebpf_ext_ndis_handle); goto Exit; } _net_ebpf_ext_ndis_handle = local_net_ebpf_ext_ndis_handle; _net_ebpf_ext_nbl_pool_handle = local_net_ebpf_ext_nbl_pool_handle; Exit: NET_EBPF_EXT_RETURN_NTSTATUS(status); } void net_ebpf_ext_uninitialize_ndis_handles() { if (_net_ebpf_ext_nbl_pool_handle != NULL) { NdisFreeNetBufferListPool(_net_ebpf_ext_nbl_pool_handle); } if (_net_ebpf_ext_ndis_handle != NULL) { NdisFreeGenericObject((NDIS_GENERIC_OBJECT*)_net_ebpf_ext_ndis_handle); } } NTSTATUS net_ebpf_extension_initialize_wfp_components(_Inout_ void* device_object) /* ++ This function initializes various WFP related components. -- */ { NTSTATUS status = STATUS_SUCCESS; FWPM_PROVIDER ebpf_wfp_provider = {0}; FWPM_SUBLAYER ebpf_hook_sub_layer; UNREFERENCED_PARAMETER(device_object); BOOLEAN is_engine_opened = FALSE; BOOLEAN is_in_transaction = FALSE; FWPM_SESSION session = {0}; size_t index; NET_EBPF_EXT_LOG_ENTRY(); if (_fwp_engine_handle != NULL) { // already registered goto Exit; } session.flags = FWPM_SESSION_FLAG_DYNAMIC; status = FwpmEngineOpen(NULL, RPC_C_AUTHN_WINNT, NULL, &session, &_fwp_engine_handle); NET_EBPF_EXT_BAIL_ON_API_FAILURE_STATUS(NET_EBPF_EXT_TRACELOG_KEYWORD_EXTENSION, "FwpmEngineOpen", status); is_engine_opened = TRUE; status = FwpmTransactionBegin(_fwp_engine_handle, 0); NET_EBPF_EXT_BAIL_ON_API_FAILURE_STATUS(NET_EBPF_EXT_TRACELOG_KEYWORD_EXTENSION, "FwpmTransactionBegin", status); is_in_transaction = TRUE; // Create the WFP provider. ebpf_wfp_provider.displayData.name = L"Microsoft Corporation"; ebpf_wfp_provider.displayData.description = L"Windows Networking eBPF Extension"; ebpf_wfp_provider.providerKey = EBPF_WFP_PROVIDER; status = FwpmProviderAdd(_fwp_engine_handle, &ebpf_wfp_provider, NULL); NET_EBPF_EXT_BAIL_ON_API_FAILURE_STATUS(NET_EBPF_EXT_TRACELOG_KEYWORD_EXTENSION, "FwpmProviderAdd", status); // Add all the sub layers. for (index = 0; index < EBPF_COUNT_OF(_net_ebpf_ext_sublayers); index++) { RtlZeroMemory(&ebpf_hook_sub_layer, sizeof(FWPM_SUBLAYER)); ebpf_hook_sub_layer.subLayerKey = *(_net_ebpf_ext_sublayers[index].sublayer_guid); ebpf_hook_sub_layer.displayData.name = (wchar_t*)_net_ebpf_ext_sublayers[index].name; ebpf_hook_sub_layer.displayData.description = (wchar_t*)_net_ebpf_ext_sublayers[index].description; ebpf_hook_sub_layer.providerKey = (GUID*)&EBPF_WFP_PROVIDER; ebpf_hook_sub_layer.flags = _net_ebpf_ext_sublayers[index].flags; ebpf_hook_sub_layer.weight = _net_ebpf_ext_sublayers[index].weight; status = FwpmSubLayerAdd(_fwp_engine_handle, &ebpf_hook_sub_layer, NULL); NET_EBPF_EXT_BAIL_ON_API_FAILURE_STATUS(NET_EBPF_EXT_TRACELOG_KEYWORD_EXTENSION, "FwpmSubLayerAdd", status); } for (index = 0; index < EBPF_COUNT_OF(_net_ebpf_ext_wfp_callout_states); index++) { status = _net_ebpf_ext_register_wfp_callout(&_net_ebpf_ext_wfp_callout_states[index], device_object); if (!NT_SUCCESS(status)) { NET_EBPF_EXT_LOG_MESSAGE_STRING( NET_EBPF_EXT_TRACELOG_LEVEL_ERROR, NET_EBPF_EXT_TRACELOG_KEYWORD_EXTENSION, "_net_ebpf_ext_register_wfp_callout() failed to register callout", (char*)_net_ebpf_ext_wfp_callout_states[index].name); goto Exit; } } status = FwpmTransactionCommit(_fwp_engine_handle); NET_EBPF_EXT_BAIL_ON_API_FAILURE_STATUS(NET_EBPF_EXT_TRACELOG_KEYWORD_EXTENSION, "FwpmTransactionCommit", status); is_in_transaction = FALSE; // Create L2 injection handle. status = FwpsInjectionHandleCreate(AF_LINK, FWPS_INJECTION_TYPE_L2, &_net_ebpf_ext_l2_injection_handle); NET_EBPF_EXT_BAIL_ON_API_FAILURE_STATUS( NET_EBPF_EXT_TRACELOG_KEYWORD_EXTENSION, "FwpsInjectionHandleCreate", status); Exit: if (!NT_SUCCESS(status)) { if (is_in_transaction) { NTSTATUS abort_status = FwpmTransactionAbort(_fwp_engine_handle); if (!NT_SUCCESS(abort_status)) { NET_EBPF_EXT_LOG_NTSTATUS_API_FAILURE( NET_EBPF_EXT_TRACELOG_KEYWORD_EXTENSION, "FwpmTransactionAbort", abort_status); } } if (is_engine_opened) { net_ebpf_extension_uninitialize_wfp_components(); } } NET_EBPF_EXT_RETURN_NTSTATUS(status); } void net_ebpf_extension_uninitialize_wfp_components(void) { size_t index; NTSTATUS status; if (_fwp_engine_handle != NULL) { status = FwpmEngineClose(_fwp_engine_handle); if (!NT_SUCCESS(status)) { NET_EBPF_EXT_LOG_NTSTATUS_API_FAILURE(NET_EBPF_EXT_TRACELOG_KEYWORD_EXTENSION, "FwpmEngineClose", status); } _fwp_engine_handle = NULL; for (index = 0; index < EBPF_COUNT_OF(_net_ebpf_ext_wfp_callout_states); index++) { status = FwpsCalloutUnregisterById(_net_ebpf_ext_wfp_callout_states[index].assigned_callout_id); if (!NT_SUCCESS(status)) { NET_EBPF_EXT_LOG_NTSTATUS_API_FAILURE( NET_EBPF_EXT_TRACELOG_KEYWORD_EXTENSION, "FwpsCalloutUnregisterById", status); } } } // FwpsInjectionHandleCreate can fail. So, check for NULL. if (_net_ebpf_ext_l2_injection_handle != NULL) { status = FwpsInjectionHandleDestroy(_net_ebpf_ext_l2_injection_handle); if (!NT_SUCCESS(status)) { NET_EBPF_EXT_LOG_NTSTATUS_API_FAILURE( NET_EBPF_EXT_TRACELOG_KEYWORD_EXTENSION, "FwpsInjectionHandleDestroy", status); } } } NTSTATUS net_ebpf_ext_filter_change_notify( FWPS_CALLOUT_NOTIFY_TYPE callout_notification_type, _In_ const GUID* filter_key, _Inout_ FWPS_FILTER* filter) { NET_EBPF_EXT_LOG_ENTRY(); UNREFERENCED_PARAMETER(filter_key); if (callout_notification_type == FWPS_CALLOUT_NOTIFY_DELETE_FILTER) { net_ebpf_extension_wfp_filter_context_t* filter_context = (net_ebpf_extension_wfp_filter_context_t*)(uintptr_t)filter->context; for (uint32_t index = 0; index < filter_context->filter_ids_count; index++) { net_ebpf_ext_wfp_filter_id_t* cur_filter_id = &filter_context->filter_ids[index]; if (cur_filter_id->id == filter->filterId) { cur_filter_id->state = NET_EBPF_EXT_WFP_FILTER_DELETED; NET_EBPF_EXT_LOG_MESSAGE_UINT64_UINT64( NET_EBPF_EXT_TRACELOG_LEVEL_VERBOSE, NET_EBPF_EXT_TRACELOG_KEYWORD_EXTENSION, "Deleted WFP filter: ", index, cur_filter_id->id); break; } } DEREFERENCE_FILTER_CONTEXT((filter_context)); } NET_EBPF_EXT_RETURN_NTSTATUS(STATUS_SUCCESS); } static void _net_ebpf_ext_flow_delete(uint16_t layer_id, uint32_t callout_id, uint64_t flow_context) /* ++ This is the flowDeleteFn function of the L2 callout. -- */ { UNREFERENCED_PARAMETER(layer_id); UNREFERENCED_PARAMETER(callout_id); UNREFERENCED_PARAMETER(flow_context); return; } NTSTATUS net_ebpf_ext_register_providers() { NTSTATUS status = STATUS_SUCCESS; NET_EBPF_EXT_LOG_ENTRY(); status = net_ebpf_ext_xdp_register_providers(); if (!NT_SUCCESS(status)) { NET_EBPF_EXT_LOG_MESSAGE_NTSTATUS( NET_EBPF_EXT_TRACELOG_LEVEL_ERROR, NET_EBPF_EXT_TRACELOG_KEYWORD_EXTENSION, "net_ebpf_ext_xdp_register_providers failed.", status); goto Exit; } _net_ebpf_xdp_providers_registered = true; status = net_ebpf_ext_bind_register_providers(); if (!NT_SUCCESS(status)) { NET_EBPF_EXT_LOG_MESSAGE_NTSTATUS( NET_EBPF_EXT_TRACELOG_LEVEL_ERROR, NET_EBPF_EXT_TRACELOG_KEYWORD_EXTENSION, "net_ebpf_ext_bind_register_providers failed.", status); goto Exit; } _net_ebpf_bind_providers_registered = true; status = net_ebpf_ext_sock_addr_register_providers(); if (!NT_SUCCESS(status)) { NET_EBPF_EXT_LOG_MESSAGE_NTSTATUS( NET_EBPF_EXT_TRACELOG_LEVEL_ERROR, NET_EBPF_EXT_TRACELOG_KEYWORD_EXTENSION, "net_ebpf_ext_bind_register_providers failed.", status); goto Exit; } _net_ebpf_sock_addr_providers_registered = true; status = net_ebpf_ext_sock_ops_register_providers(); if (!NT_SUCCESS(status)) { NET_EBPF_EXT_LOG_MESSAGE_NTSTATUS( NET_EBPF_EXT_TRACELOG_LEVEL_ERROR, NET_EBPF_EXT_TRACELOG_KEYWORD_EXTENSION, "net_ebpf_ext_sock_ops_register_providers failed.", status); goto Exit; } _net_ebpf_sock_ops_providers_registered = true; Exit: if (!NT_SUCCESS(status)) { net_ebpf_ext_unregister_providers(); } NET_EBPF_EXT_RETURN_NTSTATUS(status); } void net_ebpf_ext_unregister_providers() { if (_net_ebpf_xdp_providers_registered) { net_ebpf_ext_xdp_unregister_providers(); _net_ebpf_xdp_providers_registered = false; } if (_net_ebpf_bind_providers_registered) { net_ebpf_ext_bind_unregister_providers(); _net_ebpf_bind_providers_registered = false; } if (_net_ebpf_sock_addr_providers_registered) { net_ebpf_ext_sock_addr_unregister_providers(); _net_ebpf_sock_addr_providers_registered = false; } if (_net_ebpf_sock_ops_providers_registered) { net_ebpf_ext_sock_ops_unregister_providers(); _net_ebpf_sock_ops_providers_registered = false; } }