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