Skip to content

Commit

Permalink
Reallocate packet (#3344)
Browse files Browse the repository at this point in the history
* reallocate_packet.

* PR Feedback.
  • Loading branch information
shankarseal committed Apr 4, 2024
1 parent 37b7cc5 commit 73647f8
Show file tree
Hide file tree
Showing 15 changed files with 148 additions and 14 deletions.
19 changes: 19 additions & 0 deletions docs/eBpfExtensions.md
Original file line number Diff line number Diff line change
Expand Up @@ -141,13 +141,32 @@ in the context descriptor.
#### `ebpf_helper_function_prototype_t` Struct
This structure is used to describe the prototypes of the various helper functions implemented by the extension.
```c
typedef struct _ebpf_helper_function_prototype_flags
{
bool reallocate_packet : 1;
} ebpf_helper_function_prototype_flags_t;
typedef struct _ebpf_helper_function_prototype
{
ebpf_extension_header_t header;
uint32_t helper_id;
const char* name;
ebpf_return_type_t return_type;
ebpf_argument_type_t arguments[5];
ebpf_helper_function_prototype_flags_t flags;
} ebpf_helper_function_prototype_t;
```
* `header`: Version and size.
* `helper_id`: Integer signifying the helper function ID. (See section 2.6).
Helper function IDs for different program types need not be unique.
* `name`: Helper function name.
* `return_type`: Set the appropriate value for the `ebpf_return_type_t` enum that represents the return type of the
helper function.
* `arguments`: Array of (at most) five helper function arguments of type `ebpf_argument_type_t`.
* `flags`: Bit field of flags.
* `reallocate_packet`: Flag indicating if this helper function performs packet reallocation.


#### `ebpf_argument_type_t` Enum
This enum describes the various argument types that can be passed to an eBPF helper function. This is defined in the
Expand Down
6 changes: 6 additions & 0 deletions include/ebpf_program_types.h
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,11 @@ typedef struct _ebpf_program_type_descriptor
char is_privileged;
} ebpf_program_type_descriptor_t;

typedef struct _ebpf_helper_function_prototype_flags
{
bool reallocate_packet : 1;
} ebpf_helper_function_prototype_flags_t;

// This is the type definition for the eBPF helper function prototype
// when version is EBPF_HELPER_FUNCTION_PROTOTYPE_CURRENT_VERSION.
typedef struct _ebpf_helper_function_prototype
Expand All @@ -30,6 +35,7 @@ typedef struct _ebpf_helper_function_prototype
const char* name;
ebpf_return_type_t return_type;
ebpf_argument_type_t arguments[5];
ebpf_helper_function_prototype_flags_t flags;
} ebpf_helper_function_prototype_t;

// This is the type definition for the eBPF program information
Expand Down
4 changes: 3 additions & 1 deletion include/ebpf_windows.h
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ typedef unsigned short wchar_t;
typedef unsigned int uint32_t;
typedef unsigned long long uint64_t;
typedef unsigned long long size_t;
#define bool _Bool
#endif

// This file contains eBPF definitions needed by eBPF programs as well as
Expand Down Expand Up @@ -52,6 +53,7 @@ typedef unsigned long long size_t;
#define EBPF_PROGRAM_DATA_HELPER_COUNT L"HelperCount"

#define EBPF_HELPER_DATA_PROTOTYPE L"Prototype"
#define EBPF_HELPER_DATA_REALLOCATE_PACKET L"ReallocatePacket"

#define EBPF_DATA_BPF_PROG_TYPE L"BpfProgType"
#define EBPF_DATA_BPF_ATTACH_TYPE L"BpfAttachType"
Expand Down Expand Up @@ -81,7 +83,7 @@ typedef enum _ebpf_helper_function

#define EBPF_HELPER_FUNCTION_PROTOTYPE_CURRENT_VERSION 1
#define EBPF_HELPER_FUNCTION_PROTOTYPE_CURRENT_VERSION_SIZE \
EBPF_SIZE_INCLUDING_FIELD(ebpf_helper_function_prototype_t, arguments)
EBPF_SIZE_INCLUDING_FIELD(ebpf_helper_function_prototype_t, flags)

#define EBPF_PROGRAM_INFORMATION_CURRENT_VERSION 1
#define EBPF_PROGRAM_INFORMATION_CURRENT_VERSION_SIZE \
Expand Down
4 changes: 4 additions & 0 deletions libs/api_common/store_helper_internal.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -63,6 +63,7 @@ _ebpf_store_load_helper_prototype(
char serialized_data[sizeof(ebpf_helper_function_prototype_t)] = {0};
size_t expected_size = sizeof(helper_prototype->helper_id) + sizeof(helper_prototype->return_type) +
sizeof(helper_prototype->arguments);
uint32_t reallocate_packet_value = 0;

EBPF_LOG_ENTRY();

Expand Down Expand Up @@ -95,6 +96,9 @@ _ebpf_store_load_helper_prototype(
memcpy(&helper_prototype->arguments, serialized_data + offset, sizeof(helper_prototype->arguments));
offset += sizeof(helper_prototype->arguments);

(void)ebpf_read_registry_value_dword(helper_info_key, EBPF_HELPER_DATA_REALLOCATE_PACKET, &reallocate_packet_value);
helper_prototype->flags.reallocate_packet = !!reallocate_packet_value;

try {
helper_prototype->name =
cxplat_duplicate_string(ebpf_down_cast_from_wstring(std::wstring(helper_name)).c_str());
Expand Down
2 changes: 2 additions & 0 deletions libs/api_common/windows_helpers.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -64,5 +64,7 @@ get_helper_prototype_windows(int32_t n)
verifier_prototype.argument_type[i] = raw_prototype->arguments[i];
}

verifier_prototype.reallocate_packet = raw_prototype->flags.reallocate_packet == TRUE;

return verifier_prototype;
}
9 changes: 9 additions & 0 deletions libs/execution_context/ebpf_program.c
Original file line number Diff line number Diff line change
Expand Up @@ -2090,6 +2090,7 @@ _IRQL_requires_max_(PASSIVE_LEVEL) static ebpf_result_t _ebpf_program_compute_pr
// b. Helper name.
// c. Helper return type.
// d. Helper argument types.
// e. reallocate_packet flag (if set).

// Note:
// Order and fields being hashed is important. The order and fields being hashed must match the order and fields
Expand Down Expand Up @@ -2155,6 +2156,14 @@ _IRQL_requires_max_(PASSIVE_LEVEL) static ebpf_result_t _ebpf_program_compute_pr
goto Exit;
}
}

// This check for flags is temporary, until https://github.com/microsoft/ebpf-for-windows/issues/3429 is fixed.
if (helper_function_prototype->flags.reallocate_packet != 0) {
result = EBPF_CRYPTOGRAPHIC_HASH_APPEND_VALUE(cryptographic_hash, helper_function_prototype->flags);
if (result != EBPF_SUCCESS) {
goto Exit;
}
}
}
*hash_length = 0;
result = ebpf_cryptographic_hash_get_hash_length(cryptographic_hash, hash_length);
Expand Down
3 changes: 3 additions & 0 deletions libs/shared/ebpf_serialize.c
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@ typedef struct _ebpf_serialized_helper_function_prototype
uint32_t helper_id;
ebpf_return_type_t return_type;
ebpf_argument_type_t arguments[5];
ebpf_helper_function_prototype_flags_t flags;
size_t name_length;
uint8_t name[1];
} ebpf_serialized_helper_function_prototype_t;
Expand Down Expand Up @@ -477,6 +478,7 @@ ebpf_serialize_program_info(
for (uint16_t index = 0; index < EBPF_COUNT_OF(helper_prototype->arguments); index++) {
serialized_helper_prototype->arguments[index] = helper_prototype->arguments[index];
}
serialized_helper_prototype->flags = helper_prototype->flags;
serialized_helper_prototype->name_length = helper_function_name_length;
// Copy the program type descriptor name buffer.
memcpy(serialized_helper_prototype->name, helper_prototype->name, helper_function_name_length);
Expand Down Expand Up @@ -657,6 +659,7 @@ ebpf_deserialize_program_info(
for (int i = 0; i < EBPF_COUNT_OF(helper_prototype->arguments); i++) {
helper_prototype->arguments[i] = serialized_helper_prototype->arguments[i];
}
helper_prototype->flags = serialized_helper_prototype->flags;

// Adjust remaining buffer length.
result = ebpf_safe_size_t_subtract(
Expand Down
12 changes: 6 additions & 6 deletions libs/shared/shared_common.c
Original file line number Diff line number Diff line change
Expand Up @@ -34,12 +34,12 @@ size_t _ebpf_attach_provider_data_supported_size[] = {EBPF_ATTACH_PROVIDER_DATA_
#define EBPF_PROGRAM_TYPE_DESCRIPTOR_SIZE_0 EBPF_OFFSET_OF(ebpf_program_type_descriptor_t, is_privileged) + sizeof(char)
size_t _ebpf_program_type_descriptor_supported_size[] = {EBPF_PROGRAM_TYPE_DESCRIPTOR_SIZE_0};

#define EBPF_HELPER_FUNCTION_PROTOTYPE_SIZE_0 \
EBPF_OFFSET_OF(ebpf_helper_function_prototype_t, arguments) + 5 * sizeof(ebpf_argument_type_t)
size_t _ebpf_helper_function_prototype_supported_size[] = {EBPF_HELPER_FUNCTION_PROTOTYPE_SIZE_0};
#define EBPF_HELPER_FUNCTION_PROTOTYPE_SIZE_0 EBPF_SIZE_INCLUDING_FIELD(ebpf_helper_function_prototype_t, arguments)
#define EBPF_HELPER_FUNCTION_PROTOTYPE_SIZE_1 EBPF_SIZE_INCLUDING_FIELD(ebpf_helper_function_prototype_t, flags)
size_t _ebpf_helper_function_prototype_supported_size[] = {
EBPF_HELPER_FUNCTION_PROTOTYPE_SIZE_0, EBPF_HELPER_FUNCTION_PROTOTYPE_SIZE_1};

#define EBPF_PROGRAM_INFO_SIZE_0 \
EBPF_OFFSET_OF(ebpf_program_info_t, global_helper_prototype) + sizeof(ebpf_helper_function_prototype_t*)
#define EBPF_PROGRAM_INFO_SIZE_0 EBPF_SIZE_INCLUDING_FIELD(ebpf_program_info_t, global_helper_prototype)
size_t _ebpf_program_info_supported_size[] = {EBPF_PROGRAM_INFO_SIZE_0};

#define EBPF_HELPER_FUNCTION_ADDRESSES_SIZE_0 \
Expand All @@ -49,7 +49,7 @@ size_t _ebpf_helper_function_addresses_supported_size[] = {EBPF_HELPER_FUNCTION_
#define EBPF_PROGRAM_DATA_SIZE_0 EBPF_OFFSET_OF(ebpf_program_data_t, required_irql) + sizeof(uint8_t)
size_t _ebpf_program_data_supported_size[] = {EBPF_PROGRAM_DATA_SIZE_0};

#define EBPF_PROGRAM_SECTION_SIZE_0 EBPF_OFFSET_OF(ebpf_program_section_info_t, bpf_attach_type) + sizeof(uint32_t)
#define EBPF_PROGRAM_SECTION_SIZE_0 EBPF_SIZE_INCLUDING_FIELD(ebpf_program_section_info_t, bpf_attach_type)
size_t _ebpf_program_section_supported_size[] = {EBPF_PROGRAM_SECTION_SIZE_0};

struct _ebpf_extension_data_structure_supported_sizes
Expand Down
10 changes: 10 additions & 0 deletions libs/store_helper/ebpf_store_helper.c
Original file line number Diff line number Diff line change
Expand Up @@ -88,6 +88,16 @@ _ebpf_store_update_helper_prototype(
goto Exit;
}

if (helper_info->header.size >= EBPF_SIZE_INCLUDING_FIELD(ebpf_helper_function_prototype_t, flags)) {
// Save the reallocate_packet flag.
uint32_t reallocate_packet_value = helper_info->flags.reallocate_packet ? 1 : 0;
result = ebpf_write_registry_value_dword(
helper_function_key, EBPF_HELPER_DATA_REALLOCATE_PACKET, reallocate_packet_value);
if (!IS_SUCCESS(result)) {
goto Exit;
}
}

Exit:
ebpf_free_wstring(wide_helper_name);
ebpf_close_registry_key(helper_function_key);
Expand Down
6 changes: 5 additions & 1 deletion netebpfext/net_ebpf_ext_program_info.h
Original file line number Diff line number Diff line change
Expand Up @@ -9,13 +9,17 @@

#define XDP_EXT_HELPER_FUNCTION_START EBPF_MAX_GENERAL_HELPER_FUNCTION

#define HELPER_FUNCTION_REALLOCATE_PACKET TRUE

// XDP_TEST helper function prototype descriptors.
static const ebpf_helper_function_prototype_t _xdp_test_ebpf_extension_helper_function_prototype[] = {
{{EBPF_HELPER_FUNCTION_PROTOTYPE_CURRENT_VERSION, EBPF_HELPER_FUNCTION_PROTOTYPE_CURRENT_VERSION_SIZE},
XDP_EXT_HELPER_FUNCTION_START + 1,
"bpf_xdp_adjust_head",
EBPF_RETURN_TYPE_INTEGER,
{EBPF_ARGUMENT_TYPE_PTR_TO_CTX, EBPF_ARGUMENT_TYPE_ANYTHING}}};
{EBPF_ARGUMENT_TYPE_PTR_TO_CTX, EBPF_ARGUMENT_TYPE_ANYTHING},
// Flags.
{HELPER_FUNCTION_REALLOCATE_PACKET}}};

// XDP_TEST program information.
static const ebpf_context_descriptor_t _ebpf_xdp_test_context_descriptor = {
Expand Down
2 changes: 1 addition & 1 deletion scripts/config_test_vm.psm1
Original file line number Diff line number Diff line change
Expand Up @@ -600,7 +600,7 @@ function Get-RegressionTestArtifacts

# Download regression test artifacts for each version.
$DownloadPath = "$RegressionTestArtifactsPath"
$ArtifactName = "Release-v$ArtifactVersion/Build-x64.$Configuration.zip"
$ArtifactName = "Release-v$ArtifactVersion/Build-x64-$Configuration.zip"
$ArtifactUrl = "https://github.com/microsoft/ebpf-for-windows/releases/download/" + $ArtifactName

Write-Log "Downloading regression test artifacts for version $ArtifactVersion" -ForegroundColor Green
Expand Down
23 changes: 23 additions & 0 deletions tests/end_to_end/netsh_test.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -355,6 +355,29 @@ TEST_CASE("show verification droppacket.o", "[netsh][verification]")
"Program terminates within 0 loop iterations\n");
}

TEST_CASE("show verification xdp_adjust_head_unsafe.o", "[netsh][verification]")
{
_test_helper_netsh test_helper;
test_helper.initialize();

int result;
std::string output =
_run_netsh_command(handle_ebpf_show_verification, L"xdp_adjust_head_unsafe.o", L"xdp", nullptr, &result);
REQUIRE(result == ERROR_SUPPRESS_OUTPUT);
output = strip_paths(output);
REQUIRE(
output == "Verification failed\n"
"\n"
"Verification report:\n"
"\n"
"; ./tests/sample/unsafe/xdp_adjust_head_unsafe.c:42\n"
"; ethernet_header->Type = 0x0800;\n"
"17: Upper bound must be at most packet_size (valid_access(r1.offset+12, width=2) for write)\n"
"\n"
"1 errors\n"
"\n");
}

TEST_CASE("show verification droppacket_unsafe.o", "[netsh][verification]")
{
_test_helper_netsh test_helper;
Expand Down
46 changes: 46 additions & 0 deletions tests/sample/unsafe/xdp_adjust_head_unsafe.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
// Copyright (c) Microsoft Corporation
// SPDX-License-Identifier: MIT

// clang -O2 -Werror -c xdp_adjust_head_unsafe.c -o xdp_adjust_head_unsafe_jit.o
//
// For bpf code: clang -target bpf -O2 -Werror -c xdp_adjust_head_unsafe.c -o xdp_adjust_head_unsafe.o
//

#include "bpf_endian.h"
#include "bpf_helpers.h"
#include "net/if_ether.h"
#include "net/ip.h"
#include "net/udp.h"

SEC("xdp")
int
xdp_adjust_head_unsafe(xdp_md_t* ctx)
{
int rc = XDP_PASS;

ETHERNET_HEADER* ethernet_header = NULL;
char* next_header = (char*)ctx->data;

// Access the Ethernet header fields after checking for safety.
// This will pass verifier test.
if (next_header + sizeof(ETHERNET_HEADER) > (char*)ctx->data_end) {
rc = XDP_DROP;
goto Done;
}
ethernet_header = (ETHERNET_HEADER*)next_header;
ethernet_header->Type = 0x0800;

// Adjust the head of the packet by removing the Ethernet header.
if (bpf_xdp_adjust_head(ctx, sizeof(ETHERNET_HEADER)) < 0) {
rc = XDP_DROP;
goto Done;
}

// Access the packet without checking for safety.
// This will fail verifier test.
ethernet_header = (ETHERNET_HEADER*)ctx->data;
ethernet_header->Type = 0x0800;

Done:
return rc;
}
6 changes: 6 additions & 0 deletions tools/bpf2c/bpf2c.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -116,6 +116,12 @@ get_program_info_type_hash(const std::vector<int32_t>& actual_helper_ids, const
hash_t::append_byte_range(
byte_range, program_info->program_type_specific_helper_prototype[index].arguments[argument]);
}
// This check for flags is temporary, until https://github.com/microsoft/ebpf-for-windows/issues/3429 is
// fixed.
if (program_info->program_type_specific_helper_prototype[index].flags.reallocate_packet != 0) {
hash_t::append_byte_range(
byte_range, program_info->program_type_specific_helper_prototype[index].flags);
}
}
}
hash_t hash(algorithm);
Expand Down
10 changes: 5 additions & 5 deletions tools/bpf2c/bpf2c.vcxproj
Original file line number Diff line number Diff line change
Expand Up @@ -108,7 +108,7 @@
</ClCompile>
<Link>
<SubSystem>Console</SubSystem>
<GenerateDebugInformation>true</GenerateDebugInformation>
<GenerateDebugInformation>DebugFull</GenerateDebugInformation>
<AdditionalDependencies>$(FuzzerLibs);%(AdditionalDependencies)</AdditionalDependencies>
</Link>
<PreBuildEvent>
Expand All @@ -126,7 +126,7 @@
</ClCompile>
<Link>
<SubSystem>Console</SubSystem>
<GenerateDebugInformation>true</GenerateDebugInformation>
<GenerateDebugInformation>DebugFull</GenerateDebugInformation>
<AdditionalDependencies>$(FuzzerLibs);%(AdditionalDependencies)</AdditionalDependencies>
</Link>
<PreBuildEvent>
Expand All @@ -141,7 +141,7 @@
</ClCompile>
<Link>
<SubSystem>Console</SubSystem>
<GenerateDebugInformation>true</GenerateDebugInformation>
<GenerateDebugInformation>DebugFull</GenerateDebugInformation>
<AdditionalDependencies>$(FuzzerLibs);%(AdditionalDependencies)</AdditionalDependencies>
</Link>
<PreBuildEvent>
Expand All @@ -159,7 +159,7 @@
<SubSystem>Console</SubSystem>
<EnableCOMDATFolding>true</EnableCOMDATFolding>
<OptimizeReferences>true</OptimizeReferences>
<GenerateDebugInformation>true</GenerateDebugInformation>
<GenerateDebugInformation>DebugFull</GenerateDebugInformation>
<AdditionalDependencies>$(FuzzerLibs);%(AdditionalDependencies)</AdditionalDependencies>
</Link>
<PreBuildEvent>
Expand All @@ -177,7 +177,7 @@
<SubSystem>Console</SubSystem>
<EnableCOMDATFolding>true</EnableCOMDATFolding>
<OptimizeReferences>true</OptimizeReferences>
<GenerateDebugInformation>true</GenerateDebugInformation>
<GenerateDebugInformation>DebugFull</GenerateDebugInformation>
<AdditionalDependencies>$(FuzzerLibs);%(AdditionalDependencies)</AdditionalDependencies>
</Link>
<PreBuildEvent>
Expand Down

0 comments on commit 73647f8

Please sign in to comment.