diff --git a/include/ebpf_nethooks.h b/include/ebpf_nethooks.h
index d6a8d17727..a1bea96257 100644
--- a/include/ebpf_nethooks.h
+++ b/include/ebpf_nethooks.h
@@ -36,6 +36,27 @@ typedef enum _xdp_action
typedef xdp_action_t
xdp_hook_t(xdp_md_t* context);
+// XDP helper functions.
+#define XDP_EXT_HELPER_FN_BASE 0xFFFF
+
+#ifndef __doxygen
+#define EBPF_HELPER(return_type, name, args) typedef return_type(*name##_t) args
+#endif
+
+/**
+ * @brief Adjust XDP context data pointer.
+ *
+ * @param[in] ctx XDP context.
+ * @param[in] delta Number of bytes to move the data pointer by.
+ *
+ * @retval 0 The operation was successful.
+ * @retval <0 A failure occured.
+ */
+EBPF_HELPER(int, bpf_xdp_adjust_head, (xdp_md_t * ctx, int delta));
+#ifndef __doxygen
+#define bpf_xdp_adjust_head ((bpf_xdp_adjust_head_t)XDP_EXT_HELPER_FN_BASE + 1)
+#endif
+
// BIND hook
typedef enum _bind_operation
diff --git a/libs/platform/unit/platform_unit_test.cpp b/libs/platform/unit/platform_unit_test.cpp
index a86458a203..8ef2c896a2 100644
--- a/libs/platform/unit/platform_unit_test.cpp
+++ b/libs/platform/unit/platform_unit_test.cpp
@@ -21,6 +21,7 @@
#include "ebpf_xdp_program_data.h"
#include "ebpf_state.h"
#include "encode_program_info.h"
+#include "net_ebpf_ext_program_info.h"
class _test_helper
{
@@ -418,7 +419,9 @@ TEST_CASE("program_type_info_stored", "[platform]")
ebpf_program_info_decode(
&xdp_program_info, _ebpf_encoded_xdp_program_info_data, sizeof(_ebpf_encoded_xdp_program_info_data)) ==
EBPF_SUCCESS);
- REQUIRE(xdp_program_info->count_of_helpers == ebpf_core_helper_functions_count);
+ REQUIRE(
+ xdp_program_info->count_of_helpers ==
+ ebpf_core_helper_functions_count + EBPF_COUNT_OF(_xdp_ebpf_extension_helper_function_prototype));
REQUIRE(strcmp(xdp_program_info->program_type_descriptor.name, "xdp") == 0);
ebpf_free(xdp_program_info);
diff --git a/libs/platform/user/ebpf_extension_user.c b/libs/platform/user/ebpf_extension_user.c
index c6db47b378..76cadc1d3e 100644
--- a/libs/platform/user/ebpf_extension_user.c
+++ b/libs/platform/user/ebpf_extension_user.c
@@ -47,8 +47,6 @@ ebpf_extension_load(
ebpf_extension_provider_t** hash_table_find_result = NULL;
ebpf_extension_client_t* local_extension_client = NULL;
- UNREFERENCED_PARAMETER(extension_changed);
-
state = ebpf_lock_lock(&_ebpf_provider_table_lock);
if (provider_binding_context == NULL) {
@@ -113,6 +111,10 @@ ebpf_extension_load(
if (provider_dispatch_table != NULL)
*provider_dispatch_table = local_extension_provider->provider_dispatch_table;
+ // Invoke extension changed on client.
+ if ((extension_changed != NULL) && (provider_data != NULL))
+ extension_changed(extension_client_context, provider_binding_context, *provider_data);
+
Done:
if (local_extension_provider && local_extension_client) {
ebpf_hash_table_delete(
diff --git a/netebpfext/net_ebpf_ext.h b/netebpfext/net_ebpf_ext.h
index 987c197c6f..5ab87532f5 100644
--- a/netebpfext/net_ebpf_ext.h
+++ b/netebpfext/net_ebpf_ext.h
@@ -37,6 +37,8 @@
#include "ebpf_program_types.h"
#include "ebpf_windows.h"
+#include "net_ebpf_ext_program_info.h"
+
#define NET_EBPF_EXTENSION_NPI_PROVIDER_VERSION 0
//
diff --git a/netebpfext/net_ebpf_ext_program_info.h b/netebpfext/net_ebpf_ext_program_info.h
new file mode 100644
index 0000000000..52e0e6640d
--- /dev/null
+++ b/netebpfext/net_ebpf_ext_program_info.h
@@ -0,0 +1,26 @@
+// Copyright (c) Microsoft Corporation
+// SPDX-License-Identifier: MIT
+
+#include "ebpf_program_types.h"
+#include "ebpf_platform.h"
+#include "ebpf_nethooks.h"
+
+#define XDP_EXT_HELPER_FUNCTION_START EBPF_MAX_GENERAL_HELPER_FUNCTION
+
+// XDP Extension Helper function prototype descriptors.
+static ebpf_helper_function_prototype_t _xdp_ebpf_extension_helper_function_prototype[] = {
+ {XDP_EXT_HELPER_FUNCTION_START + 1,
+ "bpf_xdp_adjust_head",
+ EBPF_RETURN_TYPE_INTEGER,
+ {EBPF_ARGUMENT_TYPE_PTR_TO_CTX, EBPF_ARGUMENT_TYPE_ANYTHING}}};
+
+// XDP Extension program information.
+static ebpf_context_descriptor_t _ebpf_xdp_context_descriptor = {
+ sizeof(xdp_md_t),
+ EBPF_OFFSET_OF(xdp_md_t, data),
+ EBPF_OFFSET_OF(xdp_md_t, data_end),
+ EBPF_OFFSET_OF(xdp_md_t, data_meta)};
+static ebpf_program_info_t _ebpf_xdp_program_info = {
+ {"xdp", &_ebpf_xdp_context_descriptor, {0}},
+ EBPF_COUNT_OF(_xdp_ebpf_extension_helper_function_prototype),
+ _xdp_ebpf_extension_helper_function_prototype};
diff --git a/netebpfext/net_ebpf_ext_xdp.c b/netebpfext/net_ebpf_ext_xdp.c
index 381044fd68..d1bc7fcd7f 100644
--- a/netebpfext/net_ebpf_ext_xdp.c
+++ b/netebpfext/net_ebpf_ext_xdp.c
@@ -21,14 +21,20 @@ HANDLE _net_ebpf_ext_l2_injection_handle = NULL;
static ebpf_ext_attach_hook_provider_registration_t* _ebpf_xdp_hook_provider_registration = NULL;
static ebpf_extension_provider_t* _ebpf_xdp_program_info_provider = NULL;
-static ebpf_context_descriptor_t _ebpf_xdp_context_descriptor = {
- sizeof(xdp_md_t),
- EBPF_OFFSET_OF(xdp_md_t, data),
- EBPF_OFFSET_OF(xdp_md_t, data_end),
- EBPF_OFFSET_OF(xdp_md_t, data_meta)};
-static ebpf_program_info_t _ebpf_xdp_program_info = {{"xdp", &_ebpf_xdp_context_descriptor, {0}}, 0, NULL};
-
-static ebpf_program_data_t _ebpf_xdp_program_data = {&_ebpf_xdp_program_info, NULL};
+static int
+_net_ebpf_xdp_adjust_head(xdp_md_t* ctx, int delta)
+{
+ UNREFERENCED_PARAMETER(ctx);
+ UNREFERENCED_PARAMETER(delta);
+ return -1;
+}
+
+static const void* _ebpf_xdp_helper_functions[] = {(void*)&_net_ebpf_xdp_adjust_head};
+
+static ebpf_helper_function_addresses_t _ebpf_xdp_helper_function_address_table = {
+ EBPF_COUNT_OF(_ebpf_xdp_helper_functions), (uint64_t*)_ebpf_xdp_helper_functions};
+
+static ebpf_program_data_t _ebpf_xdp_program_data = {&_ebpf_xdp_program_info, &_ebpf_xdp_helper_function_address_table};
static ebpf_extension_data_t _ebpf_xdp_program_info_provider_data = {
NET_EBPF_EXTENSION_NPI_PROVIDER_VERSION, sizeof(_ebpf_xdp_program_data), &_ebpf_xdp_program_data};
diff --git a/netebpfext/netebpfext.vcxproj b/netebpfext/netebpfext.vcxproj
index ffb27da3d7..991969b606 100644
--- a/netebpfext/netebpfext.vcxproj
+++ b/netebpfext/netebpfext.vcxproj
@@ -138,6 +138,7 @@
+
diff --git a/netebpfext/netebpfext.vcxproj.filters b/netebpfext/netebpfext.vcxproj.filters
index 3f113497c9..0ee91e5d4f 100644
--- a/netebpfext/netebpfext.vcxproj.filters
+++ b/netebpfext/netebpfext.vcxproj.filters
@@ -52,6 +52,9 @@
Header Files
+
+ Header Files
+
Header Files
diff --git a/tests/end_to_end/end_to_end.cpp b/tests/end_to_end/end_to_end.cpp
index 1789ebf06e..66ccf57629 100644
--- a/tests/end_to_end/end_to_end.cpp
+++ b/tests/end_to_end/end_to_end.cpp
@@ -118,6 +118,12 @@ typedef class _udp_packet
return _packet.size();
}
+ std::vector&
+ packet()
+ {
+ return _packet;
+ }
+
static const ebpf::UDP_HEADER*
_get_udp_header(_In_ const uint8_t* packet_buffer, ADDRESS_FAMILY address_family)
{
@@ -1026,7 +1032,60 @@ _xdp_reflect_packet_test(ebpf_execution_type_t execution_type, ADDRESS_FAMILY ad
}
}
-TEST_CASE("xdp-reflect-v4-jit", "[xdp_tests]") { _xdp_reflect_packet_test(EBPF_EXECUTION_INTERPRET, AF_INET); }
-TEST_CASE("xdp-reflect-v6-jit", "[xdp_tests]") { _xdp_reflect_packet_test(EBPF_EXECUTION_INTERPRET, AF_INET6); }
+static void
+_xdp_encap_reflect_packet_test(ebpf_execution_type_t execution_type, ADDRESS_FAMILY address_family)
+{
+ _test_helper_end_to_end test_helper;
+ single_instance_hook_t hook(EBPF_PROGRAM_TYPE_XDP, EBPF_ATTACH_TYPE_XDP);
+ program_info_provider_t xdp_program_info(EBPF_PROGRAM_TYPE_XDP);
+ program_load_attach_helper_t program_helper(
+ SAMPLE_PATH "reflect_packet.o", EBPF_PROGRAM_TYPE_XDP, "encap_reflect_packet", execution_type, hook);
+
+ // Dummy UDP datagram with fake IP and MAC addresses.
+ udp_packet_t packet(address_family);
+ packet.set_destination_port(ntohs(REFLECTION_TEST_PORT));
+
+ // Dummy context (not used by the eBPF program).
+ xdp_md_helper_t ctx(packet.packet());
+ uint8_t* ip_header = packet.packet().data() + sizeof(ebpf::ETHERNET_HEADER);
+ std::vector original_ip_datagram(ip_header, packet.packet().data() + packet.packet().size());
+
+ int hook_result;
+ REQUIRE(hook.fire(&ctx, &hook_result) == EBPF_SUCCESS);
+ REQUIRE(hook_result == 3);
+
+ ebpf::ETHERNET_HEADER* ethernet_header = reinterpret_cast(ctx.data);
+ REQUIRE(memcmp(ethernet_header->Destination, _test_source_mac.data(), sizeof(ethernet_header->Destination)) == 0);
+ REQUIRE(memcmp(ethernet_header->Source, _test_destination_mac.data(), sizeof(ethernet_header->Source)) == 0);
+
+ if (address_family == AF_INET) {
+ ebpf::IPV4_HEADER* ipv4 = reinterpret_cast(ethernet_header + 1);
+ REQUIRE(ipv4->SourceAddress == _test_destination_ipv4.s_addr);
+ REQUIRE(ipv4->DestinationAddress == _test_source_ipv4.s_addr);
+ REQUIRE(ipv4->Protocol == IPPROTO_IPV4);
+ uint8_t* inner_ip_header = (uint8_t*)(ipv4 + 1);
+ REQUIRE(memcmp(inner_ip_header, original_ip_datagram.data(), original_ip_datagram.size()) == 0);
+ } else {
+ ebpf::IPV6_HEADER* ipv6 = reinterpret_cast(ethernet_header + 1);
+ REQUIRE(memcmp(ipv6->SourceAddress, &_test_destination_ipv6, sizeof(ebpf::ipv6_address_t)) == 0);
+ REQUIRE(memcmp(ipv6->DestinationAddress, &_test_source_ipv6, sizeof(ebpf::ipv6_address_t)) == 0);
+ REQUIRE(ipv6->NextHeader == IPPROTO_IPV6);
+ uint8_t* inner_ip_header = (uint8_t*)(ipv6 + 1);
+ REQUIRE(memcmp(inner_ip_header, original_ip_datagram.data(), original_ip_datagram.size()) == 0);
+ }
+}
+
+TEST_CASE("xdp-reflect-v4-jit", "[xdp_tests]") { _xdp_reflect_packet_test(EBPF_EXECUTION_JIT, AF_INET); }
+TEST_CASE("xdp-reflect-v6-jit", "[xdp_tests]") { _xdp_reflect_packet_test(EBPF_EXECUTION_JIT, AF_INET6); }
TEST_CASE("xdp-reflect-v4-interpret", "[xdp_tests]") { _xdp_reflect_packet_test(EBPF_EXECUTION_INTERPRET, AF_INET); }
-TEST_CASE("xdp-reflect-v6-interpret", "[xdp_tests]") { _xdp_reflect_packet_test(EBPF_EXECUTION_INTERPRET, AF_INET6); }
\ No newline at end of file
+TEST_CASE("xdp-reflect-v6-interpret", "[xdp_tests]") { _xdp_reflect_packet_test(EBPF_EXECUTION_INTERPRET, AF_INET6); }
+TEST_CASE("xdp-encap-reflect-v4-jit", "[xdp_tests]") { _xdp_encap_reflect_packet_test(EBPF_EXECUTION_JIT, AF_INET); }
+TEST_CASE("xdp-encap-reflect-v6-jit", "[xdp_tests]") { _xdp_encap_reflect_packet_test(EBPF_EXECUTION_JIT, AF_INET6); }
+TEST_CASE("xdp-encap-reflect-v4-interpret", "[xdp_tests]")
+{
+ _xdp_encap_reflect_packet_test(EBPF_EXECUTION_INTERPRET, AF_INET);
+}
+TEST_CASE("xdp-encap-reflect-v6-interpret", "[xdp_tests]")
+{
+ _xdp_encap_reflect_packet_test(EBPF_EXECUTION_INTERPRET, AF_INET6);
+}
\ No newline at end of file
diff --git a/tests/end_to_end/helpers.h b/tests/end_to_end/helpers.h
index aca94dd5c7..22494f4565 100644
--- a/tests/end_to_end/helpers.h
+++ b/tests/end_to_end/helpers.h
@@ -8,6 +8,7 @@
#include "ebpf_nethooks.h"
#include "ebpf_platform.h"
#include "ebpf_program_types.h"
+#include "net_ebpf_ext_program_info.h"
#include "sample_ext_program_info.h"
typedef struct _ebpf_free_memory
@@ -172,14 +173,64 @@ typedef class _single_instance_hook : public _hook_helper
#define TEST_NET_EBPF_EXTENSION_NPI_PROVIDER_VERSION 0
-static ebpf_context_descriptor_t _ebpf_xdp_context_descriptor = {
- sizeof(xdp_md_t),
- EBPF_OFFSET_OF(xdp_md_t, data),
- EBPF_OFFSET_OF(xdp_md_t, data_end),
- EBPF_OFFSET_OF(xdp_md_t, data_meta)};
-static ebpf_program_info_t _ebpf_xdp_program_info = {{"xdp", &_ebpf_xdp_context_descriptor, {0}}, 0, NULL};
+typedef class xdp_md_helper : public xdp_md_t
+{
+ public:
+ xdp_md_helper(std::vector& packet)
+ : xdp_md_t{packet.data(), packet.data() + packet.size()}, _packet(&packet), _begin(0), _end(packet.size()){};
+ int
+ adjust_head(int delta)
+ {
+ int return_value = 0;
+ if (delta == 0)
+ // Nothing changes.
+ goto Done;
+
+ if (delta > 0) {
+ if (_begin + delta > _end) {
+ return_value = -1;
+ goto Done;
+ }
+ _begin += delta;
+ } else {
+ int abs_delta = -delta;
+ if (_begin >= abs_delta)
+ _begin -= abs_delta;
+ else {
+ size_t additional_space_needed = abs_delta - _begin;
+ // Prepend _packet with additional_space_needed count of 0.
+ _packet->insert(_packet->begin(), additional_space_needed, 0);
+ _begin = 0;
+ _end += additional_space_needed;
+ }
+ // Adjust xdp_md data pointers.
+ data = _packet->data();
+ data_end = _packet->data() + _packet->size();
+ }
+ Done:
+ return return_value;
+ }
+
+ private:
+ std::vector* _packet;
+ size_t _begin;
+ size_t _end;
+} xdp_md_helper_t;
+
+static int
+_test_xdp_adjust_head(xdp_md_t* ctx, int delta)
+{
+ ((xdp_md_helper_t*)ctx)->adjust_head(delta);
+ return 0;
+}
+
+static const void* _test_ebpf_xdp_helper_functions[] = {(void*)&_test_xdp_adjust_head};
+
+static ebpf_helper_function_addresses_t _test_ebpf_xdp_helper_function_address_table = {
+ EBPF_COUNT_OF(_test_ebpf_xdp_helper_functions), (uint64_t*)_test_ebpf_xdp_helper_functions};
-static ebpf_program_data_t _ebpf_xdp_program_data = {&_ebpf_xdp_program_info, NULL};
+static ebpf_program_data_t _ebpf_xdp_program_data = {
+ &_ebpf_xdp_program_info, &_test_ebpf_xdp_helper_function_address_table};
static ebpf_extension_data_t _ebpf_xdp_program_info_provider_data = {
TEST_NET_EBPF_EXTENSION_NPI_PROVIDER_VERSION, sizeof(_ebpf_xdp_program_data), &_ebpf_xdp_program_data};
diff --git a/tests/end_to_end/mock.cpp b/tests/end_to_end/mock.cpp
index 1e00696566..8ab6c29201 100644
--- a/tests/end_to_end/mock.cpp
+++ b/tests/end_to_end/mock.cpp
@@ -72,6 +72,9 @@ clean_up_rpc_binding() { return RPC_S_OK; }
ebpf_result_t
ebpf_rpc_load_program(ebpf_program_load_info* info, const char** logs, uint32_t* logs_size)
{
+ // Set the handle of program being verified in thread-local storage.
+ set_program_under_verification(info->program_handle);
+
// Short circuit rpc call to service lib.
ebpf_result_t result = ebpf_verify_and_load_program(
&info->program_type,
diff --git a/tests/libs/util/test_util.vcxproj b/tests/libs/util/test_util.vcxproj
index 4465118151..97d8d59ae0 100644
--- a/tests/libs/util/test_util.vcxproj
+++ b/tests/libs/util/test_util.vcxproj
@@ -130,7 +130,7 @@
true
_DEBUG;_LIB;%(PreprocessorDefinitions)
true
- $(SolutionDir)libs\api_common;$(SolutionDir)libs\execution_context;$(SolutionDir)include;$(SolutionDir)libs\platform;$(SolutionDir)libs\platform\user;$(SolutionDir)tests\end_to_end;$(SolutionDir)tests\sample\ext\inc;$(OutDir);%(AdditionalIncludeDirectories)
+ $(SolutionDir)libs\api_common;$(SolutionDir)libs\execution_context;$(SolutionDir)include;$(SolutionDir)libs\platform;$(SolutionDir)libs\platform\user;$(SolutionDir)tests\end_to_end;$(SolutionDir)tests\sample\ext\inc;$(SolutionDir)\netebpfext;$(OutDir);%(AdditionalIncludeDirectories)
true
ProgramDatabase
MultiThreadedDebug
@@ -149,7 +149,7 @@
true
NDEBUG;_LIB;%(PreprocessorDefinitions)
true
- $(SolutionDir)libs\api_common;$(SolutionDir)libs\execution_context;$(SolutionDir)include;$(SolutionDir)libs\platform;$(SolutionDir)libs\platform\user;$(SolutionDir)tests\end_to_end;$(SolutionDir)tests\sample\ext\inc;$(OutDir);%(AdditionalIncludeDirectories)
+ $(SolutionDir)libs\api_common;$(SolutionDir)libs\execution_context;$(SolutionDir)include;$(SolutionDir)libs\platform;$(SolutionDir)libs\platform\user;$(SolutionDir)tests\end_to_end;$(SolutionDir)tests\sample\ext\inc;$(SolutionDir)\netebpfext;$(OutDir);%(AdditionalIncludeDirectories)
true
ProgramDatabase
MultiThreadedDLL
diff --git a/tests/performance/performance.vcxproj b/tests/performance/performance.vcxproj
index eff364a162..e562efa6cf 100644
--- a/tests/performance/performance.vcxproj
+++ b/tests/performance/performance.vcxproj
@@ -125,7 +125,7 @@
true
_DEBUG;_CONSOLE;%(PreprocessorDefinitions)
true
- $(SolutionDir)libs\api_common;$(SolutionDir)include;$(SolutionDir)libs\api;$(SolutionDir)libs\ebpfnetsh;$(SolutionDir)tests\libs\util;$(SolutionDir)tests\libs\common;$(OutDir);$(SolutionDir)external\ebpf-verifier\src;$(SolutionDir)libs\service;$(SolutionDir)rpc_interface;$(SolutionDir)libs\platform;$(SolutionDir)libs\platform\user;$(SolutionDir)libs\execution_context;$(SolutionDir)tests\end_to_end;$(SolutionDir)tests\sample\ext\inc;$(SolutionDir)external\ubpf\vm;$(SolutionDir)external\ubpf\vm\inc;%(AdditionalIncludeDirectories)
+ $(SolutionDir)libs\api_common;$(SolutionDir)include;$(SolutionDir)libs\api;$(SolutionDir)libs\ebpfnetsh;$(SolutionDir)tests\libs\util;$(SolutionDir)tests\libs\common;$(OutDir);$(SolutionDir)external\ebpf-verifier\src;$(SolutionDir)libs\service;$(SolutionDir)rpc_interface;$(SolutionDir)libs\platform;$(SolutionDir)libs\platform\user;$(SolutionDir)libs\execution_context;$(SolutionDir)tests\end_to_end;$(SolutionDir)tests\sample\ext\inc;$(SolutionDir)\netebpfext;$(SolutionDir)external\ubpf\vm;$(SolutionDir)external\ubpf\vm\inc;%(AdditionalIncludeDirectories)
true
stdcpp17
MultiThreadedDebug
@@ -144,7 +144,7 @@
true
NDEBUG;_CONSOLE;%(PreprocessorDefinitions)
true
- $(SolutionDir)libs\api_common;$(SolutionDir)include;$(SolutionDir)libs\api;$(SolutionDir)libs\ebpfnetsh;$(SolutionDir)tests\libs\util;$(SolutionDir)tests\libs\common;$(OutDir);$(SolutionDir)external\ebpf-verifier\src;$(SolutionDir)libs\service;$(SolutionDir)rpc_interface;$(SolutionDir)libs\platform;$(SolutionDir)libs\platform\user;$(SolutionDir)libs\execution_context;$(SolutionDir)tests\end_to_end;$(SolutionDir)tests\sample\ext\inc;$(SolutionDir)external\ubpf\vm;$(SolutionDir)external\ubpf\vm\inc;%(AdditionalIncludeDirectories)
+ $(SolutionDir)libs\api_common;$(SolutionDir)include;$(SolutionDir)libs\api;$(SolutionDir)libs\ebpfnetsh;$(SolutionDir)tests\libs\util;$(SolutionDir)tests\libs\common;$(OutDir);$(SolutionDir)external\ebpf-verifier\src;$(SolutionDir)libs\service;$(SolutionDir)rpc_interface;$(SolutionDir)libs\platform;$(SolutionDir)libs\platform\user;$(SolutionDir)libs\execution_context;$(SolutionDir)tests\end_to_end;$(SolutionDir)tests\sample\ext\inc;;$(SolutionDir)\netebpfext;$(SolutionDir)external\ubpf\vm;$(SolutionDir)external\ubpf\vm\inc;%(AdditionalIncludeDirectories)
true
stdcpp17
diff --git a/tests/sample/ebpf.h b/tests/sample/ebpf.h
index 1f9ba45c4f..ad598f898b 100644
--- a/tests/sample/ebpf.h
+++ b/tests/sample/ebpf.h
@@ -17,6 +17,8 @@ ntohs(uint16_t us)
return us << 8 | us >> 8;
}
+#define htons(x) ntohs(x)
+
typedef uint8_t mac_address_t[6];
#define ETHERNET_TYPE_IPV4 0x0800
@@ -34,6 +36,8 @@ typedef struct _ETHERNET_HEADER
} ETHERNET_HEADER, *PETHERNET_HEADER;
#define IPPROTO_UDP 17
+#define IPPROTO_IPV4 4
+#define IPPROTO_IPV6 41
typedef struct _IPV4_HEADER
{
diff --git a/tests/sample/ext/app/sample_ext_app.vcxproj b/tests/sample/ext/app/sample_ext_app.vcxproj
index b2a5455682..5d2492ef52 100644
--- a/tests/sample/ext/app/sample_ext_app.vcxproj
+++ b/tests/sample/ext/app/sample_ext_app.vcxproj
@@ -125,7 +125,7 @@
true
MultiThreadedDebug
ProgramDatabase
- $(SolutionDir)include;$(SolutionDir)libs\api;$(SolutionDir)libs\execution_context;$(SolutionDir)libs\platform;$(SolutionDir)libs\platform\user;$(SolutionDir)external\ebpf-verifier\src;$(SolutionDir)tests\end_to_end;$(SolutionDir)tests\libs\util;$(SolutionDir)tests\libs\common;$(SolutionDir)tests\sample;$(SolutionDir)tests\sample\ext\inc;$(OutDir);%(AdditionalIncludeDirectories)
+ $(SolutionDir)include;$(SolutionDir)libs\api;$(SolutionDir)libs\execution_context;$(SolutionDir)libs\platform;$(SolutionDir)libs\platform\user;$(SolutionDir)external\ebpf-verifier\src;$(SolutionDir)tests\end_to_end;$(SolutionDir)tests\libs\util;$(SolutionDir)tests\libs\common;$(SolutionDir)tests\sample;$(SolutionDir)tests\sample\ext\inc;$(SolutionDir)\netebpfext;$(OutDir);%(AdditionalIncludeDirectories)
stdcpplatest
true
@@ -142,7 +142,7 @@
true
NDEBUG;_CONSOLE;%(PreprocessorDefinitions)
true
- $(SolutionDir)include;$(SolutionDir)libs\api;$(SolutionDir)libs\execution_context;$(SolutionDir)libs\platform;$(SolutionDir)libs\platform\user;$(SolutionDir)external\ebpf-verifier\src;$(SolutionDir)tests\end_to_end;$(SolutionDir)tests\libs\util;$(SolutionDir)tests\libs\common;$(SolutionDir)tests\sample;$(SolutionDir)tests\sample\ext\inc;$(OutDir);%(AdditionalIncludeDirectories)
+ $(SolutionDir)include;$(SolutionDir)libs\api;$(SolutionDir)libs\execution_context;$(SolutionDir)libs\platform;$(SolutionDir)libs\platform\user;$(SolutionDir)external\ebpf-verifier\src;$(SolutionDir)tests\end_to_end;$(SolutionDir)tests\libs\util;$(SolutionDir)tests\libs\common;$(SolutionDir)tests\sample;$(SolutionDir)tests\sample\ext\inc;$(SolutionDir)\netebpfext;$(OutDir);%(AdditionalIncludeDirectories)
stdcpplatest
true
MultiThreadedDLL
diff --git a/tests/sample/reflect_packet.c b/tests/sample/reflect_packet.c
index d5a6aecc0f..457f5f0998 100644
--- a/tests/sample/reflect_packet.c
+++ b/tests/sample/reflect_packet.c
@@ -5,20 +5,13 @@
#include "ebpf.h"
#include "xdp_tests_common.h"
-void
-copy_mac_address(mac_address_t destination, mac_address_t source)
-{
- for (int i = 0; i < sizeof(mac_address_t); i++)
- destination[i] = source[i];
-}
-
void
swap_mac_addresses(ETHERNET_HEADER* ethernet_header)
{
mac_address_t mac = {0};
- copy_mac_address(mac, ethernet_header->Destination);
- copy_mac_address(ethernet_header->Destination, ethernet_header->Source);
- copy_mac_address(ethernet_header->Source, mac);
+ memcpy(mac, ethernet_header->Destination, sizeof(mac_address_t));
+ memcpy(ethernet_header->Destination, ethernet_header->Source, sizeof(mac_address_t));
+ memcpy(ethernet_header->Source, mac, sizeof(mac_address_t));
}
void
@@ -29,20 +22,13 @@ swap_ipv4_addresses(IPV4_HEADER* ipv4_header)
ipv4_header->SourceAddress = address;
}
-void
-copy_ipv6_address(ipv6_address_t destination, ipv6_address_t source)
-{
- for (int i = 0; i < sizeof(ipv6_address_t); i++)
- destination[i] = source[i];
-}
-
void
swap_ipv6_addresses(IPV6_HEADER* ipv6_header)
{
ipv6_address_t address = {0};
- copy_ipv6_address(address, ipv6_header->DestinationAddress);
- copy_ipv6_address(ipv6_header->DestinationAddress, ipv6_header->SourceAddress);
- copy_ipv6_address(ipv6_header->SourceAddress, address);
+ memcpy(address, ipv6_header->DestinationAddress, sizeof(ipv6_address_t));
+ memcpy(ipv6_header->DestinationAddress, ipv6_header->SourceAddress, sizeof(ipv6_address_t));
+ memcpy(ipv6_header->SourceAddress, address, sizeof(ipv6_address_t));
}
//
@@ -50,7 +36,7 @@ swap_ipv6_addresses(IPV6_HEADER* ipv6_header)
// by swapping the MAC and IP addresses. The program will only work for packets where UDP is the next header
// for IP header. For instance this will not work for AH packets.
//
-SEC("xdp")
+SEC("xdp/reflect")
int
reflect_packet(xdp_md_t* ctx)
{
@@ -100,6 +86,166 @@ reflect_packet(xdp_md_t* ctx)
}
}
+Done:
+ return rc;
+}
+
+int
+encapsulate_ipv4_reflect_packet(xdp_md_t* ctx)
+{
+ int rc = XDP_DROP;
+
+ // Adjust XDP context to allocate space for outer IPv4 header.
+ if (bpf_xdp_adjust_head(ctx, -sizeof(IPV4_HEADER)) < 0)
+ goto Done;
+
+ // The new ethernet header will be at the beginning of the expanded buffer.
+ char* next_header = (char*)ctx->data;
+ if ((char*)next_header + sizeof(ETHERNET_HEADER) > (char*)ctx->data_end)
+ goto Done;
+ ETHERNET_HEADER* new_ethernet_header = (ETHERNET_HEADER*)next_header;
+
+ // The old ethernet header is at an offset sizeof(IPV4_HEADER) from the start of the XDP buffer.
+ next_header = (char*)ctx->data;
+ if ((char*)next_header + sizeof(IPV4_HEADER) > (char*)ctx->data_end)
+ goto Done;
+ next_header = (char*)ctx->data + sizeof(IPV4_HEADER);
+ ETHERNET_HEADER* old_ethernet_header = (ETHERNET_HEADER*)next_header;
+
+ // The outer IPv4 header will be after the new ethernet header.
+ next_header = (char*)(new_ethernet_header + 1);
+ if ((char*)next_header + sizeof(IPV4_HEADER) > (char*)ctx->data_end)
+ goto Done;
+ IPV4_HEADER* outer_ipv4_header = (IPV4_HEADER*)next_header;
+
+ // The inner IPv4 header will be after the old ethernet header.
+ next_header = (char*)(old_ethernet_header + 1);
+ if ((char*)next_header + sizeof(IPV4_HEADER) > (char*)ctx->data_end)
+ goto Done;
+ IPV4_HEADER* inner_ipv4_header = (IPV4_HEADER*)next_header;
+
+ // Copy over the old ethernet header to the new one.
+ memcpy(new_ethernet_header, old_ethernet_header, sizeof(ETHERNET_HEADER));
+ // Swap the MAC addresses.
+ swap_mac_addresses(new_ethernet_header);
+
+ // Copy over the inner IP header to the encap IP header.
+ memcpy(outer_ipv4_header, inner_ipv4_header, sizeof(IPV4_HEADER));
+ // Swap the IP addresses.
+ swap_ipv4_addresses(outer_ipv4_header);
+ // Adjust header fields.
+ outer_ipv4_header->Protocol = IPPROTO_IPV4;
+ outer_ipv4_header->HeaderLength = sizeof(IPV4_HEADER) / sizeof(uint32_t);
+ outer_ipv4_header->TotalLength = htons((ntohs(inner_ipv4_header->TotalLength) + sizeof(IPV4_HEADER)));
+ outer_ipv4_header->HeaderChecksum = 0;
+
+ rc = XDP_TX;
+
+Done:
+ return rc;
+}
+
+int
+encapsulate_ipv6_reflect_packet(xdp_md_t* ctx)
+{
+ int rc = XDP_DROP;
+
+ // Adjust XDP context to allocate space for outer IPv6 header.
+ if (bpf_xdp_adjust_head(ctx, -sizeof(IPV6_HEADER)) < 0)
+ goto Done;
+
+ // The new ethernet header will be at the beginning of the expanded buffer.
+ char* next_header = (char*)ctx->data;
+ if ((char*)next_header + sizeof(ETHERNET_HEADER) > (char*)ctx->data_end)
+ goto Done;
+ ETHERNET_HEADER* new_ethernet_header = (ETHERNET_HEADER*)next_header;
+
+ // The old ethernet header is at an offset sizeof(IPV6_HEADER) from the start of the XDP buffer.
+ next_header = (char*)ctx->data;
+ if ((char*)next_header + sizeof(IPV6_HEADER) > (char*)ctx->data_end)
+ goto Done;
+ next_header = (char*)ctx->data + sizeof(IPV6_HEADER);
+ ETHERNET_HEADER* old_ethernet_header = (ETHERNET_HEADER*)next_header;
+
+ // The outer IPv6 header will be after the new ethernet header.
+ next_header = (char*)(new_ethernet_header + 1);
+ if ((char*)next_header + sizeof(IPV6_HEADER) > (char*)ctx->data_end)
+ goto Done;
+ IPV6_HEADER* outer_ipv6_header = (IPV6_HEADER*)next_header;
+
+ // The inner IPv6 header will be after the old ethernet header.
+ next_header = (char*)(old_ethernet_header + 1);
+ if ((char*)next_header + sizeof(IPV6_HEADER) > (char*)ctx->data_end)
+ goto Done;
+ IPV6_HEADER* inner_ipv6_header = (IPV6_HEADER*)next_header;
+
+ // Copy over the old ethernet header to the new one.
+ memcpy(new_ethernet_header, old_ethernet_header, sizeof(ETHERNET_HEADER));
+ // Swap the MAC addresses.
+ swap_mac_addresses(new_ethernet_header);
+
+ // Copy over the inner IP header to the encap IP header.
+ memcpy(outer_ipv6_header, inner_ipv6_header, sizeof(IPV6_HEADER));
+ // Swap the IP addresses.
+ swap_ipv6_addresses(outer_ipv6_header);
+ // Adjust header fields.
+ outer_ipv6_header->NextHeader = IPPROTO_IPV6;
+ outer_ipv6_header->PayloadLength = htons((ntohs(inner_ipv6_header->PayloadLength) + sizeof(IPV6_HEADER)));
+
+ rc = XDP_TX;
+
+Done:
+ return rc;
+}
+
+//
+// Same as the reflect_packet function, except the reflected packet is encapsulated in a new IP header.
+// The addresses on the outer IP header are the reverse of those on the inner IP header.
+// This program uses the bpf_xdp_adjust_head helper function.
+// (This program can only perform v4 in v4 and v6 in v6 encapsulation.)
+//
+SEC("xdp/encap_reflect")
+int
+encap_reflect_packet(xdp_md_t* ctx)
+{
+ int rc = XDP_PASS;
+
+ ETHERNET_HEADER* ethernet_header = NULL;
+ char* next_header = (char*)ctx->data;
+ if ((char*)next_header + sizeof(ETHERNET_HEADER) > (char*)ctx->data_end)
+ goto Done;
+ ethernet_header = (ETHERNET_HEADER*)next_header;
+ next_header = (char*)(ethernet_header + 1);
+ if (ethernet_header->Type == ntohs(ETHERNET_TYPE_IPV4)) {
+ if ((char*)next_header + sizeof(IPV4_HEADER) > (char*)ctx->data_end)
+ goto Done;
+ // IPv4.
+ IPV4_HEADER* ipv4_header = (IPV4_HEADER*)next_header;
+ next_header = (char*)ipv4_header + sizeof(uint32_t) * ipv4_header->HeaderLength;
+ if (ipv4_header->Protocol == IPPROTO_UDP) {
+ if ((char*)next_header + sizeof(UDP_HEADER) > (char*)ctx->data_end)
+ goto Done;
+ // UDP.
+ UDP_HEADER* udp_header = (UDP_HEADER*)next_header;
+ if (udp_header->destPort == ntohs(REFLECTION_TEST_PORT))
+ rc = encapsulate_ipv4_reflect_packet(ctx);
+ }
+ } else if (ethernet_header->Type == ntohs(ETHERNET_TYPE_IPV6)) {
+ if ((char*)next_header + sizeof(IPV6_HEADER) > (char*)ctx->data_end)
+ goto Done;
+ // IPv6.
+ IPV6_HEADER* ipv6_header = (IPV6_HEADER*)next_header;
+ next_header = (char*)(ipv6_header + 1);
+ if (ipv6_header->NextHeader == IPPROTO_UDP) {
+ if ((char*)next_header + sizeof(UDP_HEADER) > (char*)ctx->data_end)
+ goto Done;
+ // UDP.
+ UDP_HEADER* udp_header = (UDP_HEADER*)next_header;
+ if (udp_header->destPort == ntohs(REFLECTION_TEST_PORT))
+ rc = encapsulate_ipv6_reflect_packet(ctx);
+ }
+ }
+
Done:
return rc;
}
\ No newline at end of file
diff --git a/tests/unit/test.vcxproj b/tests/unit/test.vcxproj
index 47460329e4..40679bab88 100644
--- a/tests/unit/test.vcxproj
+++ b/tests/unit/test.vcxproj
@@ -126,7 +126,7 @@
true
_DEBUG;_CONSOLE;%(PreprocessorDefinitions)
true
- $(SolutionDir)libs\api_common;$(SolutionDir)include;$(SolutionDir)libs\api;$(SolutionDir)libs\ebpfnetsh;$(SolutionDir)tests\libs\util;$(SolutionDir)tests\libs\common;$(OutDir);$(SolutionDir)external\ebpf-verifier\src;$(SolutionDir)libs\service;$(SolutionDir)rpc_interface;$(SolutionDir)libs\platform;$(SolutionDir)libs\platform\user;$(SolutionDir)libs\execution_context;$(SolutionDir)tests\end_to_end;$(SolutionDir)tests\sample;$(SolutionDir)tests\sample\ext\inc;$(SolutionDir)\tests\xdp;$(SolutionDir)tools\encode_program_info;%(AdditionalIncludeDirectories)
+ $(SolutionDir)libs\api_common;$(SolutionDir)include;$(SolutionDir)libs\api;$(SolutionDir)libs\ebpfnetsh;$(SolutionDir)tests\libs\util;$(SolutionDir)tests\libs\common;$(OutDir);$(SolutionDir)external\ebpf-verifier\src;$(SolutionDir)libs\service;$(SolutionDir)rpc_interface;$(SolutionDir)libs\platform;$(SolutionDir)libs\platform\user;$(SolutionDir)libs\execution_context;$(SolutionDir)tests\end_to_end;$(SolutionDir)tests\sample;$(SolutionDir)tests\sample\ext\inc;$(SolutionDir)\netebpfext;$(SolutionDir)\tests\xdp;$(SolutionDir)tools\encode_program_info;%(AdditionalIncludeDirectories)
true
stdcpp17
MultiThreadedDebug
@@ -146,7 +146,7 @@
true
NDEBUG;_CONSOLE;%(PreprocessorDefinitions)
true
- $(SolutionDir)libs\api_common;$(SolutionDir)include;$(SolutionDir)libs\api;$(SolutionDir)libs\ebpfnetsh;$(SolutionDir)tests\libs\util;$(SolutionDir)tests\libs\common;$(OutDir);$(SolutionDir)external\ebpf-verifier\src;$(SolutionDir)libs\service;$(SolutionDir)rpc_interface;$(SolutionDir)libs\platform;$(SolutionDir)libs\platform\user;$(SolutionDir)libs\execution_context;$(SolutionDir)tests\end_to_end;$(SolutionDir)tests\sample;$(SolutionDir)tests\sample\ext\inc;$(SolutionDir)\tests\xdp;$(SolutionDir)tools\encode_program_info;%(AdditionalIncludeDirectories)
+ $(SolutionDir)libs\api_common;$(SolutionDir)include;$(SolutionDir)libs\api;$(SolutionDir)libs\ebpfnetsh;$(SolutionDir)tests\libs\util;$(SolutionDir)tests\libs\common;$(OutDir);$(SolutionDir)external\ebpf-verifier\src;$(SolutionDir)libs\service;$(SolutionDir)rpc_interface;$(SolutionDir)libs\platform;$(SolutionDir)libs\platform\user;$(SolutionDir)libs\execution_context;$(SolutionDir)tests\end_to_end;$(SolutionDir)tests\sample;$(SolutionDir)tests\sample\ext\inc;$(SolutionDir)\netebpfext;$(SolutionDir)\tests\xdp;$(SolutionDir)tools\encode_program_info;%(AdditionalIncludeDirectories)
true
stdcpp17
diff --git a/tools/encode_program_info/encode_program_info.cpp b/tools/encode_program_info/encode_program_info.cpp
index 7f9b3a2fd4..8860793d35 100644
--- a/tools/encode_program_info/encode_program_info.cpp
+++ b/tools/encode_program_info/encode_program_info.cpp
@@ -1,10 +1,12 @@
// Copyright (c) Microsoft Corporation
// SPDX-License-Identifier: MIT
-
#include
+#include
+
#include "ebpf_api.h"
#include "ebpf_nethooks.h"
#include "encode_program_info.h"
+#include "net_ebpf_ext_program_info.h"
static ebpf_result_t
_emit_program_info_file(const char* file_name, const char* symbol_name, uint8_t* buffer, unsigned long buffer_size)
@@ -72,8 +74,16 @@ _encode_xdp()
EBPF_OFFSET_OF(xdp_md_t, data_meta)};
ebpf_program_type_descriptor_t xdp_program_type = {"xdp", &xdp_context_descriptor, EBPF_PROGRAM_TYPE_XDP};
ebpf_program_info_t xdp_program_info = {xdp_program_type, 0, NULL};
- xdp_program_info.count_of_helpers = ebpf_core_helper_functions_count;
- xdp_program_info.helper_prototype = ebpf_core_helper_function_prototype;
+ xdp_program_info.count_of_helpers =
+ ebpf_core_helper_functions_count + EBPF_COUNT_OF(_xdp_ebpf_extension_helper_function_prototype);
+ std::vector _helper_function_prototypes;
+ _helper_function_prototypes.assign(
+ ebpf_core_helper_function_prototype, ebpf_core_helper_function_prototype + ebpf_core_helper_functions_count);
+ _helper_function_prototypes.insert(
+ _helper_function_prototypes.end(),
+ _xdp_ebpf_extension_helper_function_prototype,
+ _xdp_ebpf_extension_helper_function_prototype + EBPF_COUNT_OF(_xdp_ebpf_extension_helper_function_prototype));
+ xdp_program_info.helper_prototype = _helper_function_prototypes.data();
return_value = ebpf_program_info_encode(&xdp_program_info, &buffer, &buffer_size);
if (return_value != EBPF_SUCCESS)
goto Done;
diff --git a/tools/encode_program_info/encode_program_info.vcxproj b/tools/encode_program_info/encode_program_info.vcxproj
index 0a4692ee58..750b1492a8 100644
--- a/tools/encode_program_info/encode_program_info.vcxproj
+++ b/tools/encode_program_info/encode_program_info.vcxproj
@@ -126,7 +126,7 @@
true
_DEBUG;_CONSOLE;%(PreprocessorDefinitions)
true
- $(SolutionDir)include;$(SolutionDir)libs\platform;$(SolutionDir)libs\platform\user;$(SolutionDir)libs\api
+ $(SolutionDir)include;$(SolutionDir)libs\platform;$(SolutionDir)libs\platform\user;$(SolutionDir)libs\api;$(SolutionDir)\netebpfext
MultiThreadedDebug
@@ -153,7 +153,7 @@ $(OutputPath)encode_program_info.exe
true
NDEBUG;_CONSOLE;%(PreprocessorDefinitions)
true
- $(SolutionDir)include;$(SolutionDir)libs\platform;$(SolutionDir)libs\platform\user;$(SolutionDir)libs\api
+ $(SolutionDir)include;$(SolutionDir)libs\platform;$(SolutionDir)libs\platform\user;$(SolutionDir)libs\api;$(SolutionDir)\netebpfext
Console