Skip to content
Draft
Show file tree
Hide file tree
Changes from all 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
2 changes: 1 addition & 1 deletion external/usersim
4 changes: 3 additions & 1 deletion include/ebpf_nethooks.h
Original file line number Diff line number Diff line change
Expand Up @@ -149,7 +149,9 @@ typedef enum _bpf_sock_op_type
/** @brief Indicates when a passive (inbound) connection is established. **/
BPF_SOCK_OPS_PASSIVE_ESTABLISHED_CB,
/** @brief Indicates when a connection is deleted. **/
BPF_SOCK_OPS_CONNECTION_DELETED_CB
BPF_SOCK_OPS_CONNECTION_DELETED_CB,
/** @brief Indicates when a connection transitions to the listening state. **/
BPF_SOCK_OPS_LISTEN_CB,
Copy link
Collaborator

Choose a reason for hiding this comment

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

What's the difference between BPF_SOCK_OPS_LISTEN_CB vs BPF_SOCK_OPS_TCP_LISTEN_CB?
Linux headers say the latter is "Called on listen(2), right after socket transition to LISTEN state"

} bpf_sock_op_type_t;

typedef struct _bpf_sock_ops
Expand Down
32 changes: 30 additions & 2 deletions netebpfext/net_ebpf_ext.c
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@
#include "net_ebpf_ext_sock_ops.h"
#include "net_ebpf_ext_xdp.h"

#define SECONDSTO100NS(x) ((x)*10000000)
#define SECONDSTO100NS(x) ((x) * 10000000)
#define SUBLAYER_WEIGHT_MAXIMUM 0xFFFF

// Globals.
Expand Down Expand Up @@ -223,9 +223,31 @@ static net_ebpf_ext_wfp_callout_state_t _net_ebpf_ext_wfp_callout_states[] = {
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 v6",
L"ALE Flow Established callout for eBPF",
FWP_ACTION_CALLOUT_TERMINATING,
},
// EBPF_HOOK_ALE_AUTH_LISTEN_V4
{
&EBPF_HOOK_ALE_AUTH_LISTEN_V4_CALLOUT,
&FWPM_LAYER_ALE_AUTH_LISTEN_V4,
net_ebpf_extension_sock_ops_listen_classify,
net_ebpf_ext_filter_change_notify,
NULL, // No flow delete callback for listen
L"ALE Auth Listen Callout v4",
L"ALE Auth Listen callout for eBPF",
FWP_ACTION_CALLOUT_TERMINATING,
},
// EBPF_HOOK_ALE_AUTH_LISTEN_V6
{
&EBPF_HOOK_ALE_AUTH_LISTEN_V6_CALLOUT,
&FWPM_LAYER_ALE_AUTH_LISTEN_V6,
net_ebpf_extension_sock_ops_listen_classify,
net_ebpf_ext_filter_change_notify,
NULL, // No flow delete callback for listen
L"ALE Auth Listen Callout v6",
L"ALE Auth Listen callout for eBPF",
FWP_ACTION_CALLOUT_TERMINATING,
}};

// WFP globals
Expand Down Expand Up @@ -365,6 +387,12 @@ net_ebpf_extension_get_hook_id_from_wfp_layer_id(uint16_t wfp_layer_id)
case FWPS_LAYER_ALE_FLOW_ESTABLISHED_V6:
hook_id = EBPF_HOOK_ALE_FLOW_ESTABLISHED_V6;
break;
case FWPS_LAYER_ALE_AUTH_LISTEN_V4:
hook_id = EBPF_HOOK_ALE_AUTH_LISTEN_V4;
break;
case FWPS_LAYER_ALE_AUTH_LISTEN_V6:
hook_id = EBPF_HOOK_ALE_AUTH_LISTEN_V6;
break;
case FWPS_LAYER_ALE_CONNECT_REDIRECT_V4:
hook_id = EBPF_HOOK_ALE_CONNECT_REDIRECT_V4;
break;
Expand Down
4 changes: 3 additions & 1 deletion netebpfext/net_ebpf_ext.h
Original file line number Diff line number Diff line change
Expand Up @@ -249,7 +249,9 @@ typedef enum _net_ebpf_extension_hook_id
EBPF_HOOK_ALE_AUTH_RECV_ACCEPT_V4, // 10
EBPF_HOOK_ALE_AUTH_RECV_ACCEPT_V6,
EBPF_HOOK_ALE_FLOW_ESTABLISHED_V4,
EBPF_HOOK_ALE_FLOW_ESTABLISHED_V6
EBPF_HOOK_ALE_FLOW_ESTABLISHED_V6,
EBPF_HOOK_ALE_AUTH_LISTEN_V4,
EBPF_HOOK_ALE_AUTH_LISTEN_V6
} net_ebpf_extension_hook_id_t;

/**
Expand Down
174 changes: 173 additions & 1 deletion netebpfext/net_ebpf_ext_sock_ops.c
Original file line number Diff line number Diff line change
Expand Up @@ -52,7 +52,17 @@ const net_ebpf_extension_wfp_filter_parameters_t _net_ebpf_extension_sock_ops_wf
NULL, // Default sublayer.
&EBPF_HOOK_ALE_FLOW_ESTABLISHED_V6_CALLOUT,
L"net eBPF sock_ops hook",
L"net eBPF sock_ops hook WFP filter"}};
L"net eBPF sock_ops hook WFP filter"},
{&FWPM_LAYER_ALE_AUTH_LISTEN_V4,
NULL, // Default sublayer.
&EBPF_HOOK_ALE_AUTH_LISTEN_V4_CALLOUT,
L"net eBPF sock_ops listen hook",
L"net eBPF sock_ops listen hook WFP filter"},
{&FWPM_LAYER_ALE_AUTH_LISTEN_V6,
NULL, // Default sublayer.
&EBPF_HOOK_ALE_AUTH_LISTEN_V6_CALLOUT,
L"net eBPF sock_ops listen hook",
L"net eBPF sock_ops listen hook WFP filter"}};

#define NET_EBPF_SOCK_OPS_FILTER_COUNT EBPF_COUNT_OF(_net_ebpf_extension_sock_ops_wfp_filter_parameters)

Expand Down Expand Up @@ -386,6 +396,27 @@ wfp_ale_layer_fields_t wfp_flow_established_fields[] = {
FWPS_FIELD_ALE_FLOW_ESTABLISHED_V6_COMPARTMENT_ID,
FWPS_FIELD_ALE_FLOW_ESTABLISHED_V6_IP_LOCAL_INTERFACE}};

// WFP layer fields for listen hooks
wfp_ale_layer_fields_t wfp_auth_listen_fields[] = {
// EBPF_HOOK_ALE_AUTH_LISTEN_V4
{FWPS_FIELD_ALE_AUTH_LISTEN_V4_IP_LOCAL_ADDRESS,
FWPS_FIELD_ALE_AUTH_LISTEN_V4_IP_LOCAL_PORT,
0, // No remote address for listen
0, // No remote port for listen
0, // No protocol field for listen layer
0, // No direction field for listen (always inbound)
FWPS_FIELD_ALE_AUTH_LISTEN_V4_COMPARTMENT_ID,
FWPS_FIELD_ALE_AUTH_LISTEN_V4_IP_LOCAL_INTERFACE},
// EBPF_HOOK_ALE_AUTH_LISTEN_V6
{FWPS_FIELD_ALE_AUTH_LISTEN_V6_IP_LOCAL_ADDRESS,
FWPS_FIELD_ALE_AUTH_LISTEN_V6_IP_LOCAL_PORT,
0, // No remote address for listen
0, // No remote port for listen
0, // No protocol field for listen layer
0, // No direction field for listen (always inbound)
FWPS_FIELD_ALE_AUTH_LISTEN_V6_COMPARTMENT_ID,
FWPS_FIELD_ALE_AUTH_LISTEN_V6_IP_LOCAL_INTERFACE}};

static void
_net_ebpf_extension_sock_ops_copy_wfp_connection_fields(
_In_ const FWPS_INCOMING_VALUES* incoming_fixed_values,
Expand Down Expand Up @@ -437,6 +468,53 @@ _net_ebpf_extension_sock_ops_copy_wfp_connection_fields(
}
}

static void
_net_ebpf_extension_sock_ops_copy_wfp_listen_fields(
_In_ const FWPS_INCOMING_VALUES* incoming_fixed_values,
_In_ const FWPS_INCOMING_METADATA_VALUES* incoming_metadata_values,
_Out_ net_ebpf_sock_ops_t* sock_ops_context)
{
uint16_t wfp_layer_id = incoming_fixed_values->layerId;
net_ebpf_extension_hook_id_t hook_id = net_ebpf_extension_get_hook_id_from_wfp_layer_id(wfp_layer_id);
wfp_ale_layer_fields_t* fields = &wfp_auth_listen_fields[hook_id - EBPF_HOOK_ALE_AUTH_LISTEN_V4];
bpf_sock_ops_t* sock_ops = &sock_ops_context->context;

FWPS_INCOMING_VALUE0* incoming_values = incoming_fixed_values->incomingValue;

// Listen operations are always of type BPF_SOCK_OPS_LISTEN_CB
sock_ops->op = BPF_SOCK_OPS_LISTEN_CB;

// Copy IP address fields.
if (hook_id == EBPF_HOOK_ALE_AUTH_LISTEN_V4) {
sock_ops->family = AF_INET;
sock_ops->local_ip4 = htonl(incoming_values[fields->local_ip_address_field].value.uint32);
sock_ops->remote_ip4 = 0; // No remote address for listen
} else {
sock_ops->family = AF_INET6;
RtlCopyMemory(
sock_ops->local_ip6,
incoming_values[fields->local_ip_address_field].value.byteArray16,
sizeof(FWP_BYTE_ARRAY16));
memset(sock_ops->remote_ip6, 0, sizeof(sock_ops->remote_ip6)); // No remote address for listen
}
sock_ops->local_port = htons(incoming_values[fields->local_port_field].value.uint16);
sock_ops->remote_port = 0; // No remote port for listen
sock_ops->protocol = IPPROTO_TCP; // Listen operations are typically TCP
sock_ops->compartment_id = incoming_values[fields->compartment_id_field].value.uint32;
sock_ops->interface_luid = *incoming_values[fields->interface_luid_field].value.uint64;
if (incoming_metadata_values->currentMetadataValues & FWPS_METADATA_FIELD_PROCESS_ID) {
sock_ops_context->process_id = incoming_metadata_values->processId;
} else {
NET_EBPF_EXT_LOG_MESSAGE_UINT64(
NET_EBPF_EXT_TRACELOG_LEVEL_VERBOSE,
NET_EBPF_EXT_TRACELOG_KEYWORD_SOCK_OPS,
"FWPS_METADATA_FIELD_PROCESS_ID not present",
hook_id);

sock_ops_context->process_id = 0;
}
}

//
// WFP callout callback function.
//
Expand Down Expand Up @@ -624,6 +702,100 @@ net_ebpf_extension_sock_ops_flow_delete(uint16_t layer_id, uint32_t callout_id,
}
}

//
// WFP classifyFn callback function for listen operations.
//
void
net_ebpf_extension_sock_ops_listen_classify(
_In_ const FWPS_INCOMING_VALUES* incoming_fixed_values,
_In_ const FWPS_INCOMING_METADATA_VALUES* incoming_metadata_values,
_Inout_opt_ void* layer_data,
_In_opt_ const void* classify_context,
_In_ const FWPS_FILTER* filter,
uint64_t flow_context,
_Inout_ FWPS_CLASSIFY_OUT* classify_output)
{
uint32_t result;
net_ebpf_extension_sock_ops_wfp_filter_context_t* filter_context = NULL;
net_ebpf_sock_ops_t sock_ops_context = {0};
uint32_t client_compartment_id = UNSPECIFIED_COMPARTMENT_ID;
ebpf_result_t program_result;

UNREFERENCED_PARAMETER(layer_data);
UNREFERENCED_PARAMETER(classify_context);
UNREFERENCED_PARAMETER(flow_context);

NET_EBPF_EXT_LOG_ENTRY();

classify_output->actionType = FWP_ACTION_PERMIT;

filter_context = (net_ebpf_extension_sock_ops_wfp_filter_context_t*)filter->context;
ASSERT(filter_context != NULL);
if (filter_context == NULL) {
goto Exit;
}

if (filter_context->base.context_deleting) {
NET_EBPF_EXT_LOG_MESSAGE_NTSTATUS(
NET_EBPF_EXT_TRACELOG_LEVEL_VERBOSE,
NET_EBPF_EXT_TRACELOG_KEYWORD_SOCK_OPS,
"net_ebpf_extension_sock_ops_listen_classify - Client detach detected.",
STATUS_INVALID_PARAMETER);
goto Exit;
}

// Copy WFP fields for listen operation
_net_ebpf_extension_sock_ops_copy_wfp_listen_fields(
incoming_fixed_values, incoming_metadata_values, &sock_ops_context);

client_compartment_id = filter_context->compartment_id;
ASSERT(
(client_compartment_id == UNSPECIFIED_COMPARTMENT_ID) ||
(client_compartment_id == sock_ops_context.context.compartment_id));
if (client_compartment_id != UNSPECIFIED_COMPARTMENT_ID &&
client_compartment_id != sock_ops_context.context.compartment_id) {
// The client is not interested in this compartment Id.
NET_EBPF_EXT_LOG_MESSAGE_UINT32(
NET_EBPF_EXT_TRACELOG_LEVEL_VERBOSE,
NET_EBPF_EXT_TRACELOG_KEYWORD_SOCK_OPS,
"The sock_ops eBPF program is not interested in this compartmentId",
sock_ops_context.context.compartment_id);
goto Exit;
}

program_result = net_ebpf_extension_hook_invoke_programs(&sock_ops_context.context, &filter_context->base, &result);
if (program_result == EBPF_OBJECT_NOT_FOUND) {
// No program is attached to this hook.
NET_EBPF_EXT_LOG_MESSAGE(
NET_EBPF_EXT_TRACELOG_LEVEL_WARNING,
NET_EBPF_EXT_TRACELOG_KEYWORD_SOCK_OPS,
"net_ebpf_extension_sock_ops_listen_classify - No attached client.");
goto Exit;
} else if (program_result != EBPF_SUCCESS) {
NET_EBPF_EXT_LOG_MESSAGE_UINT32(
NET_EBPF_EXT_TRACELOG_LEVEL_ERROR,
NET_EBPF_EXT_TRACELOG_KEYWORD_SOCK_OPS,
"net_ebpf_extension_sock_ops_listen_classify - Program invocation failed.",
program_result);
goto Exit;
}

// For listen operations, the result determines whether to allow or block the listen operation
classify_output->actionType = (result == 0) ? FWP_ACTION_PERMIT : FWP_ACTION_BLOCK;
if (classify_output->actionType == FWP_ACTION_BLOCK) {
classify_output->rights &= ~FWPS_RIGHT_ACTION_WRITE;
}

NET_EBPF_EXT_LOG_MESSAGE_UINT32(
NET_EBPF_EXT_TRACELOG_LEVEL_VERBOSE,
NET_EBPF_EXT_TRACELOG_KEYWORD_SOCK_OPS,
"Listen operation processed, port:",
sock_ops_context.context.local_port);

Exit:
NET_EBPF_EXT_LOG_EXIT();
}

static ebpf_result_t
_ebpf_sock_ops_context_create(
_In_reads_bytes_opt_(data_size_in) const uint8_t* data_in,
Expand Down
45 changes: 45 additions & 0 deletions netebpfext/net_ebpf_ext_sock_ops.h
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@

// Callout GUIDs

// clang-format off
// f53b4577-bc47-11ec-9a30-18602489beee
DEFINE_GUID(
EBPF_HOOK_ALE_FLOW_ESTABLISHED_V4_CALLOUT,
Expand Down Expand Up @@ -37,6 +38,37 @@ DEFINE_GUID(
0xbe,
0xee);

// f53b4579-bc47-11ec-9a30-18602489beee
DEFINE_GUID(
EBPF_HOOK_ALE_AUTH_LISTEN_V4_CALLOUT,
0xf53b4579,
0xbc47,
0x11ec,
0x9a,
0x30,
0x18,
0x60,
0x24,
0x89,
0xbe,
0xee);

// f53b457a-bc47-11ec-9a30-18602489beee
DEFINE_GUID(
EBPF_HOOK_ALE_AUTH_LISTEN_V6_CALLOUT,
0xf53b457a,
0xbc47,
0x11ec,
0x9a,
0x30,
0x18,
0x60,
0x24,
0x89,
0xbe,
0xee);
// clang-format on

/**
* @brief WFP classifyFn callback for EBPF_HOOK_ALE_FLOW_ESTABLISHED_V4/6_CALLOUT.
*/
Expand All @@ -56,6 +88,19 @@ net_ebpf_extension_sock_ops_flow_established_classify(
void
net_ebpf_extension_sock_ops_flow_delete(uint16_t layer_id, uint32_t callout_id, uint64_t flow_context);

/**
* @brief WFP classifyFn callback for EBPF_HOOK_ALE_AUTH_LISTEN_V4/6.
*/
void
net_ebpf_extension_sock_ops_listen_classify(
_In_ const FWPS_INCOMING_VALUES* incoming_fixed_values,
_In_ const FWPS_INCOMING_METADATA_VALUES* incoming_metadata_values,
_Inout_opt_ void* layer_data,
_In_opt_ const void* classify_context,
_In_ const FWPS_FILTER* filter,
uint64_t flow_context,
_Inout_ FWPS_CLASSIFY_OUT* classify_output);

/**
* @brief Unregister SOCK_OPS NPI providers.
*
Expand Down
12 changes: 12 additions & 0 deletions tests/netebpfext_unit/netebpf_ext_helper.h
Original file line number Diff line number Diff line change
Expand Up @@ -98,6 +98,18 @@ typedef class _netebpf_ext_helper
usersim_fwp_sock_ops_v4_remove_flow_context(flow_id);
}

FWP_ACTION_TYPE
test_sock_ops_listen_v4(_In_ fwp_classify_parameters_t* parameters)
{
return usersim_fwp_sock_ops_listen_v4(parameters);
}

FWP_ACTION_TYPE
test_sock_ops_listen_v6(_In_ fwp_classify_parameters_t* parameters)
{
return usersim_fwp_sock_ops_listen_v6(parameters);
}

private:
bool trace_initiated = false;
bool ndis_handle_initialized = false;
Expand Down
Loading
Loading