* remove duplicate windows_helpers.cpp, refactor

* remove commented code

* move program load to service

* cleanup commented code

* remove commented code

* cr comments

* make device handle init optional

* cr comments

* change return type of some functions

* fix

* cr comments

* cr comments, cover more error conditions in windows_error_to_ebpf_result

* cr comments

Co-authored-by: Dave Thaler <dthaler@microsoft.com>
Co-authored-by: Alan Jowett <alanjo@microsoft.com>
This commit is contained in:
saxena-anurag 2021-06-07 14:22:04 -07:00 коммит произвёл GitHub
Родитель 7e033f0200
Коммит cf2ef87325
Не найден ключ, соответствующий данной подписи
Идентификатор ключа GPG: 4AEE18F83AFDEB23
39 изменённых файлов: 1028 добавлений и 534 удалений

Просмотреть файл

@ -32,6 +32,7 @@ This will build the following binaries:
loading eBPF programs.
* ebpfnetsh.dll: A plugin for the Windows netsh.exe command line tool that provides eBPF command line
utility functionality.
* ebpfsvc.exe: A user-mode service that verifies and loads an eBPF program in the execution context.
* end_to_end.exe: A collection of tests using the Catch framework. These tests are also run as part
of the Github CI/CD so should always pass.
@ -60,14 +61,17 @@ On the defender machine, do the following:
3. Install Debug VS 2019 VC redist from TBD (or switch everything to Multi-threaded Debug (/MTd) and rebuild)
4. Copy ebpfcore.sys to %windir%\system32\drivers
5. Copy netebpfext.sys to %windir%\system32\drivers
6. Copy ebpfapi.dll and ebpfnetsh.dll to %windir%\system32
7. Do `sc create EbpfCore type=kernel start=boot binpath=%windir%\system32\drivers\ebpfcore.sys`
8. Do `sc start EbpfCore`
9. Do `sc create NetEbpfExt type=kernel start=boot binpath=%windir%\system32\drivers\netebpfext.sys`
10. Do `sc start NetEbpfExt`
11. Do `netsh add helper %windir%\system32\ebpfnetsh.dll`
12. Install [clang](https://github.com/llvm/llvm-project/releases/download/llvmorg-11.0.0/LLVM-11.0.0-win64.exe)
13. Copy droppacket.c and ebpf.h to a folder (such as c:\test)
6. Copy ebpfsvc.exe to %windir%\system32
7. Copy ebpfapi.dll and ebpfnetsh.dll to %windir%\system32
8. Do `sc create EbpfCore type=kernel start=boot binpath=%windir%\system32\drivers\ebpfcore.sys`
9. Do `sc start EbpfCore`
10. Do `sc create NetEbpfExt type=kernel start=boot binpath=%windir%\system32\drivers\netebpfext.sys`
11. Do `sc start NetEbpfExt`
12. Do `sc create ebpfsvc start= auto binpath=%windir%\system32\ebpfsvc.exe type=own`
13. Do `sc start ebpfsvc`
14. Do `netsh add helper %windir%\system32\ebpfnetsh.dll`
15. Install [clang](https://github.com/llvm/llvm-project/releases/download/llvmorg-11.0.0/LLVM-11.0.0-win64.exe)
16. Copy droppacket.c and ebpf.h to a folder (such as c:\test)
On the attacker machine, do the following:
1. Copy DnsFlood.exe to attacker machine

Просмотреть файл

@ -46,6 +46,7 @@ EndProject
Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "EbpfApi", "ebpfapi\ebpfapi.vcxproj", "{75FE223A-3E45-4B0E-A2E8-04285E52E440}"
ProjectSection(ProjectDependencies) = postProject
{231EE32B-EBA4-4FE5-A55B-DB18F539D403} = {231EE32B-EBA4-4FE5-A55B-DB18F539D403}
{1423245D-0249-40FC-A077-FF7780ACFE3F} = {1423245D-0249-40FC-A077-FF7780ACFE3F}
EndProjectSection
EndProject
Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "ebpfnetsh", "tools\netsh\ebpfnetsh.vcxproj", "{74803F80-A8BD-4A03-862B-FA96648A9BF6}"

Просмотреть файл

@ -74,7 +74,7 @@
<ConformanceMode>true</ConformanceMode>
<PrecompiledHeaderFile>pch.h</PrecompiledHeaderFile>
<RuntimeLibrary>MultiThreadedDebugDLL</RuntimeLibrary>
<AdditionalIncludeDirectories>$(SolutionDir)include;$(SolutionDir)libs\platform;$(SolutionDir)libs\platform\user;$(SolutionDir)libs\execution_context</AdditionalIncludeDirectories>
<AdditionalIncludeDirectories>$(SolutionDir)libs\api;$(SolutionDir)rpc_interface;$(SolutionDir)include;$(SolutionDir)libs\platform;$(SolutionDir)libs\platform\user;$(SolutionDir)libs\execution_context</AdditionalIncludeDirectories>
<TreatWarningAsError>true</TreatWarningAsError>
<LanguageStandard>stdcpp17</LanguageStandard>
</ClCompile>
@ -96,7 +96,7 @@
<PreprocessorDefinitions>NDEBUG;EBPFAPI_EXPORTS;_WINDOWS;_USRDLL;%(PreprocessorDefinitions)</PreprocessorDefinitions>
<ConformanceMode>true</ConformanceMode>
<PrecompiledHeaderFile>pch.h</PrecompiledHeaderFile>
<AdditionalIncludeDirectories>$(SolutionDir)include;$(SolutionDir)libs\platform;$(SolutionDir)libs\platform\user;$(SolutionDir)libs\execution_context</AdditionalIncludeDirectories>
<AdditionalIncludeDirectories>$(SolutionDir)libs\api;$(SolutionDir)rpc_interface;$(SolutionDir)include;$(SolutionDir)libs\platform;$(SolutionDir)libs\platform\user;$(SolutionDir)libs\execution_context</AdditionalIncludeDirectories>
<RuntimeLibrary>MultiThreadedDLL</RuntimeLibrary>
<TreatWarningAsError>true</TreatWarningAsError>
<LanguageStandard>stdcpp17</LanguageStandard>
@ -120,6 +120,7 @@
<ItemGroup>
<ClCompile Include="dllmain.cpp" />
<ClCompile Include="platform.cpp" />
<ClCompile Include="rpc_client.cpp" />
</ItemGroup>
<ItemGroup>
<None Include="Source.def" />
@ -137,9 +138,6 @@
<ProjectReference Include="..\libs\platform\user\platform_user.vcxproj">
<Project>{c26cb6a9-158c-4a9e-a243-755ddd98e5fe}</Project>
</ProjectReference>
<ProjectReference Include="..\libs\service\service.vcxproj">
<Project>{af85c549-57cc-40a5-bdfc-dcf1998de80f}</Project>
</ProjectReference>
<ProjectReference Include="..\libs\ubpf\user\ubpf_user.vcxproj">
<Project>{245f0ec7-1ebc-4d68-8b1f-f758ea9196ae}</Project>
</ProjectReference>

Просмотреть файл

@ -33,6 +33,9 @@
<ClCompile Include="platform.cpp">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="rpc_client.cpp">
<Filter>Source Files</Filter>
</ClCompile>
</ItemGroup>
<ItemGroup>
<None Include="Source.def">

85
ebpfapi/rpc_client.cpp Normal file
Просмотреть файл

@ -0,0 +1,85 @@
// Copyright (c) Microsoft Corporation
// SPDX-License-Identifier: MIT
#include <ctype.h>
#include <iostream>
#include <stdio.h>
#include <stdlib.h>
#include <windows.h>
#include "ebpf_api.h"
#include "ebpf_windows.h"
#include "rpc_interface_c.c"
#pragma comment(lib, "Rpcrt4.lib")
static RPC_WSTR _string_binding = nullptr;
static const WCHAR* _protocol_sequence = L"ncalrpc";
static bool _binding_initialized = false;
ebpf_result_t
ebpf_rpc_verify_program(ebpf_program_verify_info* info, const char** logs, uint32_t* logs_size) noexcept
{
ebpf_result_t result;
RpcTryExcept { result = ebpf_client_verify_program(info, logs_size, const_cast<char**>(logs)); }
RpcExcept(RpcExceptionFilter(RpcExceptionCode()))
{
// TODO: (Issue# 247) Add tracing for the RpcExceptionCode() that is returned.
result = EBPF_RPC_EXCEPTION;
}
RpcEndExcept
return result;
}
ebpf_result_t
ebpf_rpc_load_program(ebpf_program_load_info* info, const char** logs, uint32_t* logs_size) noexcept
{
ebpf_result_t result;
RpcTryExcept { result = ebpf_client_verify_and_load_program(info, logs_size, const_cast<char**>(logs)); }
RpcExcept(RpcExceptionFilter(RpcExceptionCode()))
{
// TODO: (Issue# 247) Add tracing for the RpcExceptionCode() that is returned.
result = EBPF_RPC_EXCEPTION;
}
RpcEndExcept
return result;
}
RPC_STATUS
initialize_rpc_binding()
{
RPC_STATUS status =
RpcStringBindingCompose(nullptr, (RPC_WSTR)_protocol_sequence, nullptr, nullptr, nullptr, &_string_binding);
if (status != RPC_S_OK) {
return status;
}
status = RpcBindingFromStringBinding(_string_binding, &ebpf_service_interface_handle);
if (status == RPC_S_OK) {
_binding_initialized = true;
}
return status;
}
RPC_STATUS
clean_up_rpc_binding()
{
RPC_STATUS status = RpcStringFree(&_string_binding);
if (status != RPC_S_OK) {
printf("RpcStringFree failed with error %d\n", status);
}
if (_binding_initialized) {
status = RpcBindingFree(&ebpf_service_interface_handle);
if (status != RPC_S_OK) {
printf("RpcBindingFree failed with error %d\n", status);
}
}
return status;
}

Просмотреть файл

@ -134,7 +134,7 @@
<PrecompiledHeader>NotUsing</PrecompiledHeader>
<PrecompiledHeaderFile>pch.h</PrecompiledHeaderFile>
<RuntimeLibrary>MultiThreadedDebugDLL</RuntimeLibrary>
<AdditionalIncludeDirectories>$(SolutionDir)libs\api;$(SolutionDir)libs\platform;$(SolutionDir)libs\service;$(SolutionDir)include;$(SolutionDir)rpc_interface;$(SolutionDir)libs\platform\user;$(SolutionDir)external\ubpf\vm;$(SolutionDir)external\ubpf\vm\inc;$(SolutionDir)external\ebpf-verifier\src;$(SolutionDir)external\ebpf-verifier\external;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
<AdditionalIncludeDirectories>$(SolutionDir)libs\api_common;$(SolutionDir)libs\execution_context;$(SolutionDir)libs\api;$(SolutionDir)libs\platform;$(SolutionDir)libs\service;$(SolutionDir)include;$(SolutionDir)rpc_interface;$(SolutionDir)libs\platform\user;$(SolutionDir)external\ubpf\vm;$(SolutionDir)external\ubpf\vm\inc;$(SolutionDir)external\ebpf-verifier\src;$(SolutionDir)external\ebpf-verifier\external;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
<LanguageStandard>stdcpplatest</LanguageStandard>
</ClCompile>
<Link>

Просмотреть файл

@ -5,29 +5,36 @@
#include <stdexcept>
#include <stdio.h>
#include <vector>
#include "api_internal.h"
#include "api_service.h"
#include "ebpf_windows.h"
#include "rpc_interface_h.h"
#include "svc_common.h"
#include "Verifier.h"
// Critical section to serialize RPC calls.
// Currently ebpfsvc uses a global context to track verification
// and JIT compilation, hence all RPC calls should be serialized.
static std::mutex _mutex;
ebpf_result_t
ebpf_server_verify_and_jit_program(
ebpf_server_verify_and_load_program(
/* [ref][in] */ ebpf_program_load_info* info,
/* [ref][out] */ uint32_t* logs_size,
/* [ref][size_is][size_is][out] */ char** logs)
{
UNREFERENCED_PARAMETER(info);
UNREFERENCED_PARAMETER(logs_size);
UNREFERENCED_PARAMETER(logs);
ebpf_result_t result;
return EBPF_FAILED;
if (info->byte_code_size == 0) {
return EBPF_INVALID_ARGUMENT;
}
result = ebpf_verify_and_load_program(
&info->program_type,
info->program_handle,
info->execution_context,
info->execution_type,
info->map_count,
info->handle_map,
info->byte_code_size,
info->byte_code,
const_cast<const char**>(logs),
logs_size);
ebpf_clear_thread_local_storage();
return result;
}
ebpf_result_t
@ -36,17 +43,17 @@ ebpf_server_verify_program(
/* [out] */ uint32_t* logs_size,
/* [ref][size_is][size_is][out] */ char** logs)
{
ebpf_result_t result;
if (info->byte_code_size == 0) {
return EBPF_INVALID_ARGUMENT;
}
std::scoped_lock lock(_mutex);
// MIDL generates warnings if any [out] param uses 'const',
// since RPC marshaling will copy the data anyway. So we
// can safely cast the 'logs' param below.
return ebpf_verify_program(
result = ebpf_verify_program(
reinterpret_cast<const GUID*>(&info->program_type),
info->execution_context,
info->map_descriptors_count,
@ -55,4 +62,7 @@ ebpf_server_verify_program(
info->byte_code,
const_cast<const char**>(logs),
logs_size);
ebpf_clear_thread_local_storage();
return result;
}

Просмотреть файл

@ -7,38 +7,46 @@
#pragma comment(lib, "Rpcrt4.lib")
#define RPC_SERVER_ENDPOINT L"\\pipe\\ebpf_service"
#define ANNOTATION L"ebpfsvc rpc server"
#define EBPF_SERVICE_INTERFACE_HANDLE ebpf_server_ebpf_service_interface_v1_0_s_ifspec
static const WCHAR* _protocol_sequence = L"ncalrpc";
static bool _rpc_server_initialized = false;
DWORD
initialize_rpc_server()
{
RPC_STATUS status;
const WCHAR* protocol_sequence = L"ncacn_np";
unsigned char* security = nullptr;
const WCHAR* endpoint = RPC_SERVER_ENDPOINT;
unsigned int minimum_calls = 1;
unsigned int dont_wait = true;
bool registered = false;
RPC_BINDING_VECTOR* binding_vector = nullptr;
status = RpcServerUseProtseqEp(
(RPC_WSTR)protocol_sequence, RPC_C_LISTEN_MAX_CALLS_DEFAULT, (RPC_WSTR)endpoint, security);
status = RpcServerUseProtseq((RPC_WSTR)_protocol_sequence, RPC_C_PROTSEQ_MAX_REQS_DEFAULT, nullptr);
if (status != RPC_S_OK) {
goto Exit;
}
status = RpcServerRegisterIf(ebpf_server_ebpf_service_interface_v1_0_s_ifspec, nullptr, nullptr);
status = RpcServerRegisterIfEx(
EBPF_SERVICE_INTERFACE_HANDLE, nullptr, nullptr, RPC_IF_AUTOLISTEN, RPC_C_LISTEN_MAX_CALLS_DEFAULT, nullptr);
if (status != RPC_S_OK) {
goto Exit;
}
registered = true;
status = RpcServerListen(minimum_calls, RPC_C_LISTEN_MAX_CALLS_DEFAULT, dont_wait);
status = RpcServerInqBindings(&binding_vector);
if (status != RPC_S_OK) {
goto Exit;
}
status = RpcEpRegister(EBPF_SERVICE_INTERFACE_HANDLE, binding_vector, NULL, (RPC_WSTR)ANNOTATION);
if (status == RPC_S_OK) {
_rpc_server_initialized = true;
}
Exit:
if (binding_vector != nullptr) {
RpcBindingVectorFree(&binding_vector);
}
if (status != RPC_S_OK) {
if (registered) {
RpcServerUnregisterIf(nullptr, nullptr, true);
@ -50,22 +58,32 @@ Exit:
void
shutdown_rpc_server()
{
RPC_STATUS status;
RPC_BINDING_VECTOR* binding_vector = nullptr;
if (!_rpc_server_initialized) {
return;
}
RPC_STATUS status;
status = RpcMgmtStopServerListening(nullptr);
status = RpcServerInqBindings(&binding_vector);
if (status != RPC_S_OK) {
goto Exit;
}
status = RpcEpUnregister(EBPF_SERVICE_INTERFACE_HANDLE, binding_vector, nullptr);
if (status != RPC_S_OK) {
goto Exit;
}
status = RpcServerUnregisterIf(EBPF_SERVICE_INTERFACE_HANDLE, nullptr, true);
if (status != RPC_S_OK) {
// TODO: Add a trace that something happened.
return;
}
status = RpcServerUnregisterIf(nullptr, nullptr, true);
if (status != RPC_S_OK) {
// TODO: Add a trace that something happened.
return;
goto Exit;
}
Exit:
if (binding_vector != nullptr) {
RpcBindingVectorFree(&binding_vector);
}
return;
}

Просмотреть файл

@ -1,6 +1,7 @@
// Copyright (c) Microsoft Corporation
// SPDX-License-Identifier: MIT
#include "api_service.h"
#include "rpc_util.h"
#include "svc_common.h"
@ -198,7 +199,13 @@ Initialize()
{
DWORD status;
status = initialize_rpc_server();
if (status != ERROR_SUCCESS) {
goto Exit;
}
status = ebpf_service_initialize();
Exit:
return status;
}
@ -206,6 +213,7 @@ void
Cleanup()
{
shutdown_rpc_server();
ebpf_service_cleanup();
if (ebpf_service_stop_event_handle) {
CloseHandle(ebpf_service_stop_event_handle);
}

Просмотреть файл

@ -7,7 +7,8 @@
#include <stdbool.h>
#include <stdint.h>
#include "ebpf_execution_type.h"
#include "ebpf_result.h"
#include "ebpf_windows.h"
#ifdef __cplusplus
@ -33,12 +34,6 @@ extern "C"
const ebpf_handle_t ebpf_handle_invalid = (ebpf_handle_t)-1;
typedef struct _tlv_type_length_value tlv_type_length_value_t;
typedef enum _ebpf_execution_type
{
EBPF_EXECUTION_JIT,
EBPF_EXECUTION_INTERPRET
} ebpf_execution_type_t;
/**
* @brief Initialize the eBPF user mode library.
*/
@ -63,7 +58,7 @@ extern "C"
* @param[out] map_handles Array of map handles to be filled in.
* @param[out] error_message Error message describing what failed.
*/
uint32_t
ebpf_result_t
ebpf_api_load_program(
const char* file,
const char* section_name,
@ -148,7 +143,7 @@ extern "C"
* @param[out] value_size Size of values in the eBPF map.
* @param[out] max_entries Maximum number of entries in the map.
*/
uint32_t
ebpf_result_t
ebpf_api_map_query_definition(
ebpf_handle_t handle,
uint32_t* size,

Просмотреть файл

@ -0,0 +1,19 @@
// Copyright (c) Microsoft Corporation
// SPDX-License-Identifier: MIT
#pragma once
#ifdef __cplusplus
extern "C"
{
#endif
typedef enum _ebpf_execution_type
{
EBPF_EXECUTION_JIT,
EBPF_EXECUTION_INTERPRET
} ebpf_execution_type_t;
#ifdef __cplusplus
}
#endif

Просмотреть файл

@ -18,8 +18,11 @@ extern "C"
// Program verification failed.
EBPF_VALIDATION_FAILED,
// JIT compilation failed.
EBPF_JIT_COMPILATION_FAILED,
// Program load failed.
EBPF_LOAD_FAILED,
EBPF_PROGRAM_LOAD_FAILED,
// Invalid FD provided.
EBPF_INVALID_FD,
@ -48,6 +51,15 @@ extern "C"
// Low memory.
EBPF_NO_MEMORY,
// The program is too large.
EBPF_PROGRAM_TOO_LARGE,
// RPC exception.
EBPF_RPC_EXCEPTION,
// Handle is already initialized.
EBPF_ALREADY_INITIALIZED,
// Generic failure code for all other errors.
EBPF_FAILED,

Просмотреть файл

@ -51,6 +51,12 @@ load_byte_code(
// copy out the bytecode for the jitter
size_t ebpf_bytes = raw_prog.prog.size() * sizeof(ebpf_inst);
// Check if the raw program can fit in the supplied buffer.
if (ebpf_bytes > *byte_code_size) {
return ERROR_BUFFER_OVERFLOW;
}
int i = 0;
for (ebpf_inst inst : raw_prog.prog) {
char* buf = (char*)&inst;

Просмотреть файл

@ -131,7 +131,7 @@
<ConformanceMode>true</ConformanceMode>
<PrecompiledHeader>NotUsing</PrecompiledHeader>
<PrecompiledHeaderFile>pch.h</PrecompiledHeaderFile>
<AdditionalIncludeDirectories>$(SolutionDir)libs\service;$(SolutionDir)libs\api_common;$(SolutionDir)include;$(SolutionDir)libs\platform;$(SolutionDir)libs\platform\user;$(SolutionDir)libs\execution_context;$(SolutionDir)external\ubpf\vm;$(SolutionDir)external\ubpf\vm\inc;$(SolutionDir)external\ebpf-verifier\src;$(SolutionDir)external\ebpf-verifier\external;$(OutDir);%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
<AdditionalIncludeDirectories>$(SolutionDir)libs\api;$(SolutionDir)rpc_interface;$(SolutionDir)libs\service;$(SolutionDir)libs\api_common;$(SolutionDir)include;$(SolutionDir)libs\platform;$(SolutionDir)libs\platform\user;$(SolutionDir)libs\execution_context;$(SolutionDir)external\ubpf\vm;$(SolutionDir)external\ubpf\vm\inc;$(SolutionDir)external\ebpf-verifier\src;$(SolutionDir)external\ebpf-verifier\external;$(OutDir);%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
<LanguageStandard>stdcpplatest</LanguageStandard>
<RuntimeLibrary>MultiThreadedDebugDLL</RuntimeLibrary>
<TreatWarningAsError>true</TreatWarningAsError>
@ -152,7 +152,7 @@
<ConformanceMode>true</ConformanceMode>
<PrecompiledHeader>NotUsing</PrecompiledHeader>
<PrecompiledHeaderFile>pch.h</PrecompiledHeaderFile>
<AdditionalIncludeDirectories>$(SolutionDir)libs\service;$(SolutionDir)libs\api_common;$(SolutionDir)include;$(SolutionDir)libs\platform;$(SolutionDir)libs\platform\user;$(SolutionDir)libs\execution_context;$(SolutionDir)external\ubpf\vm;$(SolutionDir)external\ubpf\vm\inc;$(SolutionDir)external\ebpf-verifier\src;$(SolutionDir)external\ebpf-verifier\external;$(OutDir);%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
<AdditionalIncludeDirectories>$(SolutionDir)rpc_interface;$(SolutionDir)libs\service;$(SolutionDir)libs\api_common;$(SolutionDir)include;$(SolutionDir)libs\platform;$(SolutionDir)libs\platform\user;$(SolutionDir)libs\execution_context;$(SolutionDir)external\ubpf\vm;$(SolutionDir)external\ubpf\vm\inc;$(SolutionDir)external\ebpf-verifier\src;$(SolutionDir)external\ebpf-verifier\external;$(OutDir);%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
<LanguageStandard>stdcpplatest</LanguageStandard>
<TreatWarningAsError>true</TreatWarningAsError>
</ClCompile>
@ -176,6 +176,7 @@
<ClInclude Include="api_internal.h" />
<ClInclude Include="pch.h" />
<ClInclude Include="platform.h" />
<ClInclude Include="rpc_client.h" />
<ClInclude Include="tlv.h" />
<ClInclude Include="Verifier.h" />
<ClInclude Include="windows_platform.hpp" />

Просмотреть файл

@ -45,5 +45,8 @@
<ClInclude Include="api_internal.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="rpc_client.h">
<Filter>Header Files</Filter>
</ClInclude>
</ItemGroup>
</Project>

Просмотреть файл

@ -2,55 +2,44 @@
// SPDX-License-Identifier: MIT
#include "pch.h"
#include "ebpf_api.h"
#include <map>
#include <stdexcept>
#include "api_common.hpp"
#include "device_helper.hpp"
#include "ebpf_api.h"
#include "ebpf_protocol.h"
#include "ebpf_platform.h"
#include "map_descriptors.hpp"
#include "platform.h"
#include "rpc_client.h"
extern "C"
{
#include "ubpf.h"
}
#include "Verifier.h"
#include "verifier_service.h"
#include "windows_helpers.hpp"
#define MAX_CODE_SIZE (32 * 1024) // 32 KB
uint32_t
ebpf_api_initiate()
{
if (device_handle != INVALID_HANDLE_VALUE) {
return ERROR_ALREADY_INITIALIZED;
uint32_t result;
// This is best effort. If device handle does not initialize,
// it will be re-attempted before an IOCTL call is made.
initialize_device_handle();
result = initialize_rpc_binding();
if (result != ERROR_SUCCESS) {
clean_up_device_handle();
clean_up_rpc_binding();
}
device_handle = Platform::CreateFile(
EBPF_DEVICE_WIN32_NAME,
GENERIC_READ | GENERIC_WRITE,
0,
nullptr,
CREATE_ALWAYS,
FILE_ATTRIBUTE_NORMAL,
nullptr);
if (device_handle == INVALID_HANDLE_VALUE) {
return GetLastError();
}
return 0;
return result;
}
void
ebpf_api_terminate()
{
if (device_handle != INVALID_HANDLE_VALUE) {
Platform::CloseHandle(device_handle);
device_handle = INVALID_HANDLE_VALUE;
}
clean_up_device_handle();
clean_up_rpc_binding();
}
int
@ -64,7 +53,7 @@ create_map_function(
_ebpf_operation_create_map_reply reply{};
uint32_t retval = invoke_ioctl(device_handle, request, reply);
uint32_t retval = invoke_ioctl(request, reply);
if (retval != ERROR_SUCCESS) {
throw std::runtime_error(std::string("Error ") + std::to_string(retval) + " trying to create map");
}
@ -73,165 +62,10 @@ create_map_function(
throw std::runtime_error(std::string("reply.header.id != ebpf_operation_id_t::EBPF_OPERATION_CREATE_MAP"));
}
return cache_map_handle(reply.handle, type, key_size, value_size);
return cache_map_handle(reply.handle, type, key_size, value_size, max_entries);
}
static uint32_t
resolve_maps_in_byte_code(ebpf_handle_t program_handle, ebpf_code_buffer_t& byte_code)
{
// Maintain two maps.
// First map is instruction offset -> map handle.
// Second map is map handle -> map address.
std::map<size_t, uint64_t> instruction_offsets_to_map_handles;
std::map<uint64_t, uint64_t> map_handles_to_map_addresses;
ebpf_inst* instructions = reinterpret_cast<ebpf_inst*>(byte_code.data());
ebpf_inst* instruction_end = reinterpret_cast<ebpf_inst*>(byte_code.data() + byte_code.size());
for (size_t index = 0; index < byte_code.size() / sizeof(ebpf_inst); index++) {
ebpf_inst& first_instruction = instructions[index];
ebpf_inst& second_instruction = instructions[index + 1];
if (first_instruction.opcode != INST_OP_LDDW_IMM) {
continue;
}
if (&instructions[index + 1] >= instruction_end) {
return ERROR_INVALID_PARAMETER;
}
index++;
// Check for LD_MAP flag
if (first_instruction.src != 1) {
continue;
}
uint64_t imm =
static_cast<uint64_t>(first_instruction.imm) | (static_cast<uint64_t>(second_instruction.imm) << 32);
// Collect set of instructions to patch with the value to replace.
instruction_offsets_to_map_handles[index - 1] = imm;
// Collect set of map handles.
map_handles_to_map_addresses[imm] = 0;
}
if (instruction_offsets_to_map_handles.size() == 0) {
return ERROR_SUCCESS;
}
ebpf_protocol_buffer_t request_buffer(
offsetof(ebpf_operation_resolve_map_request_t, map_handle) +
sizeof(uint64_t) * map_handles_to_map_addresses.size());
ebpf_protocol_buffer_t reply_buffer(
offsetof(ebpf_operation_resolve_map_reply_t, address) + sizeof(uint64_t) * map_handles_to_map_addresses.size());
auto request = reinterpret_cast<ebpf_operation_resolve_map_request_t*>(request_buffer.data());
auto reply = reinterpret_cast<ebpf_operation_resolve_map_reply_t*>(reply_buffer.data());
request->header.id = ebpf_operation_id_t::EBPF_OPERATION_RESOLVE_MAP;
request->header.length = static_cast<uint16_t>(request_buffer.size());
request->program_handle = reinterpret_cast<uint64_t>(program_handle);
size_t index = 0;
for (auto& [map_handle, map_address] : map_handles_to_map_addresses) {
if (map_handle > get_map_descriptor_size()) {
return ERROR_INVALID_PARAMETER;
}
request->map_handle[index++] = get_map_handle_at_index(map_handle - 1);
}
uint32_t result = invoke_ioctl(device_handle, request_buffer, reply_buffer);
if (result != ERROR_SUCCESS) {
return result;
}
index = 0;
for (auto& [map_handle, map_address] : map_handles_to_map_addresses) {
map_address = reply->address[index++];
}
for (auto& [instruction_offset, map_handle] : instruction_offsets_to_map_handles) {
ebpf_inst& first_instruction = instructions[instruction_offset];
ebpf_inst& second_instruction = instructions[instruction_offset + 1];
// Clear LD_MAP flag
first_instruction.src = 0;
// Replace handle with address
uint64_t new_imm = map_handles_to_map_addresses[map_handle];
first_instruction.imm = static_cast<uint32_t>(new_imm);
second_instruction.imm = static_cast<uint32_t>(new_imm >> 32);
}
return ERROR_SUCCESS;
}
static uint32_t
build_helper_id_to_address_map(
ebpf_handle_t program_handle, ebpf_code_buffer_t& byte_code, std::map<uint32_t, uint64_t>& helper_id_to_adddress)
{
ebpf_inst* instructions = reinterpret_cast<ebpf_inst*>(byte_code.data());
for (size_t index = 0; index < byte_code.size() / sizeof(ebpf_inst); index++) {
ebpf_inst& instruction = instructions[index];
if (instruction.opcode != INST_OP_CALL) {
continue;
}
helper_id_to_adddress[instruction.imm] = 0;
}
if (helper_id_to_adddress.size() == 0)
return ERROR_SUCCESS;
ebpf_protocol_buffer_t request_buffer(
offsetof(ebpf_operation_resolve_helper_request_t, helper_id) + sizeof(uint32_t) * helper_id_to_adddress.size());
ebpf_protocol_buffer_t reply_buffer(
offsetof(ebpf_operation_resolve_helper_reply_t, address) + sizeof(uint64_t) * helper_id_to_adddress.size());
auto request = reinterpret_cast<ebpf_operation_resolve_helper_request_t*>(request_buffer.data());
auto reply = reinterpret_cast<ebpf_operation_resolve_helper_reply_t*>(reply_buffer.data());
request->header.id = ebpf_operation_id_t::EBPF_OPERATION_RESOLVE_HELPER;
request->header.length = static_cast<uint16_t>(request_buffer.size());
request->program_handle = reinterpret_cast<uint64_t>(program_handle);
size_t index = 0;
for (const auto& helper : helper_id_to_adddress) {
request->helper_id[index++] = helper.first;
}
uint32_t result = invoke_ioctl(device_handle, request_buffer, reply_buffer);
if (result != ERROR_SUCCESS) {
return result;
}
index = 0;
for (auto& helper : helper_id_to_adddress) {
helper.second = reply->address[index++];
}
return EBPF_SUCCESS;
}
static uint32_t
resolve_ec_function(ebpf_ec_function_t function, uint64_t* address)
{
ebpf_operation_get_ec_function_request_t request = {sizeof(request), EBPF_OPERATION_GET_EC_FUNCTION, function};
ebpf_operation_get_ec_function_reply_t reply;
uint32_t retval = invoke_ioctl(device_handle, request, reply);
if (retval != ERROR_SUCCESS) {
return retval;
}
if (reply.header.id != ebpf_operation_id_t::EBPF_OPERATION_GET_EC_FUNCTION) {
return ERROR_INVALID_PARAMETER;
}
*address = reply.address;
return retval;
}
static uint32_t
static ebpf_result_t
_create_program(
ebpf_program_type_t program_type,
const std::string& file_name,
@ -258,12 +92,14 @@ _create_program(
std::copy(section_name.begin(), section_name.end(), request_buffer.begin() + request->section_name_offset);
uint32_t retval = invoke_ioctl(device_handle, request_buffer, reply);
if (retval != ERROR_SUCCESS) {
return retval;
uint32_t error = invoke_ioctl(request_buffer, reply);
if (error != ERROR_SUCCESS) {
goto Exit;
}
*program_handle = reinterpret_cast<ebpf_handle_t>(reply.program_handle);
return retval;
Exit:
return windows_error_to_ebpf_result(error);
}
uint32_t
@ -328,7 +164,7 @@ Done:
return result;
}
uint32_t
ebpf_result_t
ebpf_api_load_program(
const char* file_name,
const char* section_name,
@ -343,18 +179,18 @@ ebpf_api_load_program(
ebpf_code_buffer_t byte_code(MAX_CODE_SIZE);
size_t byte_code_size = byte_code.size();
ebpf_protocol_buffer_t request_buffer;
struct ubpf_vm* vm = nullptr;
uint64_t log_function_address;
ebpf_operation_load_code_request_t* request = nullptr;
uint32_t error_message_size = 0;
std::vector<uintptr_t> handles;
uint32_t result;
ebpf_result_t result = EBPF_SUCCESS;
ebpf_program_load_info load_info = {0};
std::vector<fd_handle_map> handle_map;
*handle = 0;
*error_message = nullptr;
clear_map_descriptors();
try {
ebpf_verifier_options_t verifier_options{false, false, false, false, false};
if (load_byte_code(
file_name,
@ -364,27 +200,52 @@ ebpf_api_load_program(
&byte_code_size,
&program_type,
error_message) != 0) {
result = ERROR_INVALID_PARAMETER;
result = EBPF_INVALID_ARGUMENT;
goto Done;
}
byte_code.resize(byte_code_size);
// TODO: (issue #169): Should switch this to more idiomatic C++
// Note: This leaks the program handle on some errors.
result = _create_program(program_type, file_name, section_name, &program_handle);
if (result != ERROR_SUCCESS)
goto Done;
// Verify code.
if (verify_byte_code(&program_type, byte_code.data(), byte_code_size, error_message, &error_message_size) != 0) {
result = ERROR_INVALID_PARAMETER;
if (result != EBPF_SUCCESS) {
goto Done;
}
if (get_map_descriptor_size() > *count_of_map_handles) {
result = ERROR_INSUFFICIENT_BUFFER;
result = EBPF_ERROR_INSUFFICIENT_BUFFER;
goto Done;
}
// populate load_info.
load_info.file_name = const_cast<char*>(file_name);
load_info.section_name = const_cast<char*>(section_name);
load_info.program_name = nullptr;
load_info.program_type = program_type;
load_info.program_handle = program_handle;
load_info.execution_type = execution_type;
load_info.byte_code = byte_code.data();
load_info.byte_code_size = (uint32_t)byte_code_size;
load_info.execution_context = execution_context_kernel_mode;
load_info.map_count = (uint32_t)get_map_descriptor_size();
if (load_info.map_count > 0) {
auto descriptors = get_all_map_descriptors();
for (const auto& descriptor : descriptors) {
handle_map.emplace_back(
descriptor.ebpf_map_descriptor.original_fd, reinterpret_cast<file_handle_t>(descriptor.handle));
}
load_info.handle_map = handle_map.data();
}
result = ebpf_rpc_load_program(&load_info, error_message, &error_message_size);
if (result != EBPF_SUCCESS) {
goto Done;
}
// Program is verified and loaded.
*count_of_map_handles = 0;
handles = get_all_map_handles();
for (const auto& map_handle : handles) {
@ -392,74 +253,18 @@ ebpf_api_load_program(
(*count_of_map_handles)++;
}
byte_code.resize(byte_code_size);
result = resolve_maps_in_byte_code(program_handle, byte_code);
if (result != ERROR_SUCCESS) {
goto Done;
}
result = resolve_ec_function(EBPF_EC_FUNCTION_LOG, &log_function_address);
if (result != ERROR_SUCCESS)
goto Done;
if (execution_type == EBPF_EXECUTION_JIT) {
std::map<uint32_t, uint64_t> helper_id_to_adddress;
result = build_helper_id_to_address_map(program_handle, byte_code, helper_id_to_adddress);
if (result != ERROR_SUCCESS)
goto Done;
ebpf_code_buffer_t machine_code(MAX_CODE_SIZE);
size_t machine_code_size = machine_code.size();
// JIT code.
vm = ubpf_create();
if (vm == nullptr)
goto Done;
for (const auto& helper : helper_id_to_adddress) {
if (ubpf_register(vm, helper.first, nullptr, reinterpret_cast<void*>(helper.second)) < 0)
goto Done;
}
ubpf_set_error_print(
vm, reinterpret_cast<int (*)(FILE * stream, const char* format, ...)>(log_function_address));
if (ubpf_load(
vm, byte_code.data(), static_cast<uint32_t>(byte_code.size()), const_cast<char**>(error_message)) < 0) {
result = ERROR_INVALID_PARAMETER;
goto Done;
}
if (ubpf_translate(vm, machine_code.data(), &machine_code_size, const_cast<char**>(error_message))) {
result = ERROR_INVALID_PARAMETER;
goto Done;
}
machine_code.resize(machine_code_size);
byte_code = machine_code;
}
request_buffer.resize(offsetof(ebpf_operation_load_code_request_t, code) + byte_code.size());
request = reinterpret_cast<ebpf_operation_load_code_request_t*>(request_buffer.data());
request->header.id = ebpf_operation_id_t::EBPF_OPERATION_LOAD_CODE;
request->header.length = static_cast<uint16_t>(request_buffer.size());
request->program_handle = reinterpret_cast<uint64_t>(program_handle);
request->code_type = execution_type == EBPF_EXECUTION_JIT ? EBPF_CODE_NATIVE : EBPF_CODE_EBPF;
std::copy(
byte_code.begin(),
byte_code.end(),
request_buffer.begin() + offsetof(ebpf_operation_load_code_request_t, code));
result = invoke_ioctl(device_handle, request_buffer);
if (result != ERROR_SUCCESS)
goto Done;
*handle = program_handle;
program_handle = INVALID_HANDLE_VALUE;
} catch (const std::bad_alloc&) {
result = EBPF_NO_MEMORY;
goto Done;
} catch (...) {
result = EBPF_FAILED;
goto Done;
}
Done:
if (result != ERROR_SUCCESS) {
if (result != EBPF_SUCCESS) {
handles = get_all_map_handles();
for (const auto& map_handle : handles) {
ebpf_api_close_handle((ebpf_handle_t)map_handle);
@ -467,11 +272,9 @@ Done:
}
clear_map_descriptors();
if (vm)
ubpf_destroy(vm);
if (program_handle != INVALID_HANDLE_VALUE)
if (program_handle != INVALID_HANDLE_VALUE) {
ebpf_api_close_handle(program_handle);
}
return result;
}
@ -492,7 +295,7 @@ ebpf_api_pin_object(ebpf_handle_t handle, const uint8_t* name, uint32_t name_len
request->header.length = static_cast<uint16_t>(request_buffer.size());
request->handle = reinterpret_cast<uint64_t>(handle);
std::copy(name, name + name_length, request->name);
return invoke_ioctl(device_handle, request_buffer);
return invoke_ioctl(request_buffer);
}
uint32_t
@ -505,7 +308,7 @@ ebpf_api_unpin_object(const uint8_t* name, uint32_t name_length)
request->header.length = static_cast<uint16_t>(request_buffer.size());
request->handle = UINT64_MAX;
std::copy(name, name + name_length, request->name);
return invoke_ioctl(device_handle, request_buffer);
return invoke_ioctl(request_buffer);
}
uint32_t
@ -518,7 +321,7 @@ ebpf_api_get_pinned_map(const uint8_t* name, uint32_t name_length, ebpf_handle_t
request->header.id = EBPF_OPERATION_GET_PINNING;
request->header.length = static_cast<uint16_t>(request_buffer.size());
std::copy(name, name + name_length, request->name);
auto result = invoke_ioctl(device_handle, request_buffer, reply);
auto result = invoke_ioctl(request_buffer, reply);
if (result != ERROR_SUCCESS) {
return result;
}
@ -546,7 +349,7 @@ ebpf_api_map_find_element(
request->handle = reinterpret_cast<uint64_t>(handle);
std::copy(key, key + key_size, request->key);
auto retval = invoke_ioctl(device_handle, request_buffer, reply_buffer);
auto retval = invoke_ioctl(request_buffer, reply_buffer);
if (reply->header.id != ebpf_operation_id_t::EBPF_OPERATION_MAP_FIND_ELEMENT) {
return ERROR_INVALID_PARAMETER;
@ -572,7 +375,7 @@ ebpf_api_map_update_element(
std::copy(key, key + key_size, request->data);
std::copy(value, value + value_size, request->data + key_size);
return invoke_ioctl(device_handle, request_buffer);
return invoke_ioctl(request_buffer);
}
uint32_t
@ -586,7 +389,7 @@ ebpf_api_map_delete_element(ebpf_handle_t handle, uint32_t key_size, const uint8
request->handle = (uint64_t)handle;
std::copy(key, key + key_size, request->key);
return invoke_ioctl(device_handle, request_buffer);
return invoke_ioctl(request_buffer);
}
uint32_t
@ -606,7 +409,7 @@ ebpf_api_get_next_map_key(ebpf_handle_t handle, uint32_t key_size, const uint8_t
request->header.length = offsetof(ebpf_operation_map_get_next_key_request_t, previous_key);
}
auto retval = invoke_ioctl(device_handle, request_buffer, reply_buffer);
auto retval = invoke_ioctl(request_buffer, reply_buffer);
if (reply->header.id != ebpf_operation_id_t::EBPF_OPERATION_MAP_GET_NEXT_KEY) {
return ERROR_INVALID_PARAMETER;
@ -626,7 +429,7 @@ ebpf_api_get_next_map(ebpf_handle_t previous_handle, ebpf_handle_t* next_handle)
_ebpf_operation_get_next_map_reply reply;
uint32_t retval = invoke_ioctl(device_handle, request, reply);
uint32_t retval = invoke_ioctl(request, reply);
if (retval == ERROR_SUCCESS) {
*next_handle = reinterpret_cast<ebpf_handle_t>(reply.next_handle);
}
@ -636,21 +439,20 @@ ebpf_api_get_next_map(ebpf_handle_t previous_handle, ebpf_handle_t* next_handle)
uint32_t
ebpf_api_get_next_program(ebpf_handle_t previous_handle, ebpf_handle_t* next_handle)
{
_ebpf_operation_get_next_program_request request{
sizeof(request),
_ebpf_operation_get_next_program_request request{sizeof(request),
ebpf_operation_id_t::EBPF_OPERATION_GET_NEXT_PROGRAM,
reinterpret_cast<uint64_t>(previous_handle)};
_ebpf_operation_get_next_program_reply reply;
uint32_t retval = invoke_ioctl(device_handle, request, reply);
uint32_t retval = invoke_ioctl(request, reply);
if (retval == ERROR_SUCCESS) {
*next_handle = reinterpret_cast<ebpf_handle_t>(reply.next_handle);
}
return retval;
}
uint32_t
ebpf_result_t
ebpf_api_map_query_definition(
ebpf_handle_t handle,
uint32_t* size,
@ -659,20 +461,7 @@ ebpf_api_map_query_definition(
uint32_t* value_size,
uint32_t* max_entries)
{
_ebpf_operation_query_map_definition_request request{
sizeof(request), ebpf_operation_id_t::EBPF_OPERATION_QUERY_MAP_DEFINITION, reinterpret_cast<uint64_t>(handle)};
_ebpf_operation_query_map_definition_reply reply;
uint32_t retval = invoke_ioctl(device_handle, request, reply);
if (retval == ERROR_SUCCESS) {
*size = reply.map_definition.size;
*type = reply.map_definition.type;
*key_size = reply.map_definition.key_size;
*value_size = reply.map_definition.value_size;
*max_entries = reply.map_definition.max_entries;
}
return retval;
return query_map_definition(handle, size, type, key_size, value_size, max_entries);
}
uint32_t
@ -687,7 +476,7 @@ ebpf_api_program_query_information(
auto reply = reinterpret_cast<_ebpf_operation_query_program_information_reply*>(reply_buffer.data());
uint32_t retval = invoke_ioctl(device_handle, request, reply_buffer);
uint32_t retval = invoke_ioctl(request, reply_buffer);
if (retval != ERROR_SUCCESS) {
return retval;
}
@ -723,7 +512,7 @@ ebpf_api_link_program(ebpf_handle_t program_handle, ebpf_attach_type_t attach_ty
sizeof(request), EBPF_OPERATION_LINK_PROGRAM, reinterpret_cast<uint64_t>(program_handle), attach_type};
ebpf_operation_link_program_reply_t reply;
uint32_t retval = invoke_ioctl(device_handle, request, reply);
uint32_t retval = invoke_ioctl(request, reply);
if (retval != ERROR_SUCCESS) {
return retval;
}
@ -742,5 +531,5 @@ ebpf_api_close_handle(ebpf_handle_t handle)
ebpf_operation_close_handle_request_t request = {
sizeof(request), EBPF_OPERATION_CLOSE_HANDLE, reinterpret_cast<uint64_t>(handle)};
return invoke_ioctl(device_handle, request);
return invoke_ioctl(request);
}

Просмотреть файл

@ -12,5 +12,8 @@ initialize_rpc_binding(void);
RPC_STATUS
clean_up_rpc_binding(void);
int
ebpf_rpc_verify_program(ebpf_program_verify_info* info, const char** logs, uint32_t* logs_size);
ebpf_result_t
ebpf_rpc_verify_program(ebpf_program_verify_info* info, const char** logs, uint32_t* logs_size) noexcept;
ebpf_result_t
ebpf_rpc_load_program(ebpf_program_load_info* info, const char** logs, uint32_t* logs_size) noexcept;

Просмотреть файл

@ -32,7 +32,7 @@ create_map_windows(
if (options.mock_map_fds) {
EbpfMapType type = get_map_type_windows(map_type);
fd = create_map_crab(type, key_size, value_size, max_entries, options);
cache_map_file_descriptor(map_type, key_size, value_size, fd);
cache_map_file_descriptor(map_type, key_size, value_size, max_entries, fd);
return fd;
}

Просмотреть файл

@ -5,6 +5,10 @@
#include <string>
#include <vector>
#include <Windows.h>
#include "api_common.hpp"
#include "device_helper.hpp"
#include "ebpf_protocol.h"
#include "ebpf_result.h"
#pragma warning(push)
#pragma warning(disable : 4100) // 'identifier' : unreferenced formal parameter
#pragma warning(disable : 4244) // 'conversion' conversion from 'type1' to
@ -14,7 +18,7 @@
#pragma warning(pop)
const char*
allocate_error_string(const std::string& str, uint32_t* length = nullptr)
allocate_error_string(const std::string& str, uint32_t* length) noexcept
{
char* error_message;
size_t error_message_length = str.size() + 1;
@ -36,7 +40,7 @@ convert_ebpf_program_to_bytes(const std::vector<ebpf_inst>& instructions)
}
int
get_file_size(const char* filename, size_t* byte_code_size)
get_file_size(const char* filename, size_t* byte_code_size) noexcept
{
int result = 0;
*byte_code_size = NULL;
@ -49,3 +53,29 @@ get_file_size(const char* filename, size_t* byte_code_size)
return result;
}
ebpf_result_t
query_map_definition(
ebpf_handle_t handle,
uint32_t* size,
uint32_t* type,
uint32_t* key_size,
uint32_t* value_size,
uint32_t* max_entries) noexcept
{
_ebpf_operation_query_map_definition_request request{
sizeof(request), ebpf_operation_id_t::EBPF_OPERATION_QUERY_MAP_DEFINITION, reinterpret_cast<uint64_t>(handle)};
_ebpf_operation_query_map_definition_reply reply;
uint32_t result = invoke_ioctl(request, reply);
if (result == ERROR_SUCCESS) {
*size = reply.map_definition.size;
*type = reply.map_definition.type;
*key_size = reply.map_definition.key_size;
*value_size = reply.map_definition.value_size;
*max_entries = reply.map_definition.max_entries;
}
return windows_error_to_ebpf_result(result);
}

Просмотреть файл

@ -7,34 +7,53 @@
#include <stdexcept>
#include "ebpf_api.h"
#include "ebpf_execution_context.h"
#include "ebpf_platform.h"
#include "ebpf_result.h"
#undef VOID
#include "ebpf_helpers.h"
#include "platform.hpp"
extern "C"
{
#include "ubpf.h"
}
typedef struct _map_cache
{
uintptr_t handle;
EbpfMapDescriptor ebpf_map_descriptor;
_map_cache() {}
_map_cache(uintptr_t handle, EbpfMapDescriptor descriptor) : handle(handle), ebpf_map_descriptor(descriptor) {}
_map_cache(
uintptr_t handle,
int original_fd,
uint32_t type,
unsigned int key_size,
unsigned int value_size,
unsigned int max_entries,
unsigned int inner_map_fd)
: handle(handle)
{
ebpf_map_descriptor.original_fd = original_fd;
ebpf_map_descriptor.type = type;
ebpf_map_descriptor.key_size = key_size;
ebpf_map_descriptor.value_size = value_size;
ebpf_map_descriptor.max_entries = max_entries;
ebpf_map_descriptor.inner_map_fd = inner_map_fd;
}
} map_cache_t;
const char*
allocate_error_string(const std::string& str, uint32_t* length = nullptr);
allocate_error_string(const std::string& str, uint32_t* length = nullptr) noexcept;
std::vector<uint8_t>
convert_ebpf_program_to_bytes(const std::vector<ebpf_inst>& instructions);
int
get_file_size(const char* filename, size_t* byte_code_size);
get_file_size(const char* filename, size_t* byte_code_size) noexcept;
EbpfHelperPrototype
get_helper_prototype_windows(unsigned int n);
int
cache_map_handle(uint64_t handle, uint32_t type, uint32_t key_size, uint32_t value_size);
cache_map_handle(uint64_t handle, uint32_t type, uint32_t key_size, uint32_t value_size, uint32_t max_entries);
size_t
get_map_descriptor_size(void);
@ -44,3 +63,70 @@ get_map_handle(int map_fd);
std::vector<uintptr_t>
get_all_map_handles(void);
std::vector<map_cache_t>
get_all_map_descriptors();
__forceinline ebpf_result_t
windows_error_to_ebpf_result(uint32_t error)
{
ebpf_result_t result;
switch (error) {
case ERROR_SUCCESS:
result = EBPF_SUCCESS;
break;
case ERROR_OUTOFMEMORY:
case ERROR_NOT_ENOUGH_MEMORY:
result = EBPF_NO_MEMORY;
break;
case ERROR_NOT_FOUND:
result = EBPF_ERROR_NOT_FOUND;
break;
case ERROR_INVALID_PARAMETER:
result = EBPF_INVALID_ARGUMENT;
break;
case ERROR_NO_MORE_ITEMS:
result = EBPF_ERROR_NO_MORE_KEYS;
break;
case ERROR_INVALID_HANDLE:
result = EBPF_ERROR_INVALID_HANDLE;
break;
case ERROR_NOT_SUPPORTED:
result = EBPF_ERROR_NOT_SUPPORTED;
break;
case ERROR_MORE_DATA:
result = EBPF_ERROR_INSUFFICIENT_BUFFER;
break;
case ERROR_FILE_NOT_FOUND:
result = EBPF_FILE_NOT_FOUND;
break;
case ERROR_ALREADY_INITIALIZED:
result = EBPF_ALREADY_INITIALIZED;
break;
default:
result = EBPF_FAILED;
break;
}
return result;
}
ebpf_result_t
query_map_definition(
ebpf_handle_t handle,
uint32_t* size,
uint32_t* type,
uint32_t* key_size,
uint32_t* value_size,
uint32_t* max_entries) noexcept;

Просмотреть файл

@ -169,6 +169,7 @@
<None Include="packages.config" />
</ItemGroup>
<ItemGroup>
<ClCompile Include="device_helper.cpp" />
<ClCompile Include="map_descriptors.cpp" />
<ClCompile Include="api_common.cpp" />
<ClCompile Include="windows_helpers.cpp" />
@ -178,7 +179,7 @@
<ClInclude Include="map_descriptors.hpp" />
<ClInclude Include="api_common.hpp" />
<ClInclude Include="tlv.h" />
<ClInclude Include="windows_helpers.hpp" />
<ClInclude Include="device_helper.hpp" />
<ClInclude Include="windows_platform_common.hpp" />
</ItemGroup>
<ItemGroup>

Просмотреть файл

@ -28,6 +28,9 @@
<ClCompile Include="map_descriptors.cpp">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="device_helper.cpp">
<Filter>Source Files</Filter>
</ClCompile>
</ItemGroup>
<ItemGroup>
<ClInclude Include="tlv.h">
@ -42,7 +45,7 @@
<ClInclude Include="map_descriptors.hpp">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="windows_helpers.hpp">
<ClInclude Include="device_helper.hpp">
<Filter>Header Files</Filter>
</ClInclude>
</ItemGroup>

Просмотреть файл

@ -0,0 +1,68 @@
// Copyright (c) Microsoft Corporation
// SPDX-License-Identifier: MIT
// Contains code to manage device for kernel mode execution context.
#include <map>
#include <mutex>
#include <stdexcept>
#include "api_common.hpp"
#include "ebpf_api.h"
#include "ebpf_bind_program_data.h"
#include "ebpf_platform.h"
#include "ebpf_program_types.h"
#include "ebpf_protocol.h"
#include "ebpf_result.h"
#include "ebpf_xdp_program_data.h"
#include "platform.h"
#undef VOID
#include "platform.hpp"
static ebpf_handle_t _device_handle = INVALID_HANDLE_VALUE;
static std::mutex _mutex;
ebpf_result_t
initialize_device_handle()
{
std::scoped_lock lock(_mutex);
if (_device_handle != INVALID_HANDLE_VALUE) {
return EBPF_ALREADY_INITIALIZED;
}
_device_handle = Platform::CreateFile(
EBPF_DEVICE_WIN32_NAME,
GENERIC_READ | GENERIC_WRITE,
0,
nullptr,
CREATE_ALWAYS,
FILE_ATTRIBUTE_NORMAL,
nullptr);
if (_device_handle == INVALID_HANDLE_VALUE) {
return windows_error_to_ebpf_result(GetLastError());
}
return EBPF_SUCCESS;
}
void
clean_up_device_handle()
{
std::scoped_lock lock(_mutex);
if (_device_handle != INVALID_HANDLE_VALUE) {
Platform::CloseHandle(_device_handle);
_device_handle = INVALID_HANDLE_VALUE;
}
}
ebpf_handle_t
get_device_handle()
{
if (_device_handle == INVALID_HANDLE_VALUE) {
initialize_device_handle();
}
return _device_handle;
}

Просмотреть файл

@ -3,6 +3,9 @@
#pragma once
#include "ebpf_api.h"
#include "platform.h"
// Device type
#define EBPF_IOCTL_TYPE FILE_DEVICE_NETWORK
@ -18,9 +21,18 @@ typedef struct empty_reply
static empty_reply_t _empty_reply;
ebpf_result_t
initialize_device_handle();
void
clean_up_device_handle();
ebpf_handle_t
get_device_handle();
template <typename request_t, typename reply_t = empty_reply_t>
uint32_t
invoke_ioctl(ebpf_handle_t handle, request_t& request, reply_t& reply = _empty_reply)
invoke_ioctl(request_t& request, reply_t& reply = _empty_reply)
{
uint32_t actual_reply_size;
uint32_t request_size;
@ -56,7 +68,7 @@ invoke_ioctl(ebpf_handle_t handle, request_t& request, reply_t& reply = _empty_r
}
auto result = Platform::DeviceIoControl(
handle,
get_device_handle(),
IOCTL_EBPFCTL_METHOD_BUFFERED,
request_ptr,
request_size,
@ -75,5 +87,3 @@ invoke_ioctl(ebpf_handle_t handle, request_t& request, reply_t& reply = _empty_r
return ERROR_SUCCESS;
}
extern ebpf_handle_t device_handle;

Просмотреть файл

@ -11,9 +11,7 @@ cache_map_file_descriptors(const EbpfMapDescriptor* map_descriptors, uint32_t ma
{
for (uint32_t i = 0; i < map_descriptors_count; i++) {
auto descriptor = map_descriptors[i];
_map_file_descriptors.push_back(
{(uintptr_t)descriptor.original_fd,
{descriptor.original_fd, descriptor.type, descriptor.key_size, descriptor.value_size, 0}});
_map_file_descriptors.emplace_back((uintptr_t)descriptor.original_fd, descriptor);
}
}
@ -33,7 +31,7 @@ get_map_cache_entry(uint64_t map_fd)
}
}
return _map_file_descriptors[0];
throw std::runtime_error(std::string("Map cache entry for map fd ") + std::to_string(map_fd) + " not found.");
}
EbpfMapDescriptor&
@ -72,19 +70,32 @@ get_all_map_handles()
return handles;
}
void
cache_map_file_descriptor(uint32_t type, uint32_t key_size, uint32_t value_size, int fd)
std::vector<map_cache_t>
get_all_map_descriptors()
{
_map_file_descriptors.push_back({(uintptr_t)fd, {fd, type, key_size, value_size, 0}});
return _map_file_descriptors;
}
void
cache_map_file_descriptor(uint32_t type, uint32_t key_size, uint32_t value_size, uint32_t max_entries, int fd)
{
_map_file_descriptors.emplace_back((uintptr_t)fd, fd, type, key_size, value_size, max_entries, 0);
}
void
cache_map_file_descriptor_with_handle(
uint32_t type, uint32_t key_size, uint32_t value_size, uint32_t max_entries, int fd, uintptr_t handle)
{
_map_file_descriptors.emplace_back(handle, fd, type, key_size, value_size, max_entries, 0);
}
int
cache_map_handle(uint64_t handle, uint32_t type, uint32_t key_size, uint32_t value_size)
cache_map_handle(uint64_t handle, uint32_t type, uint32_t key_size, uint32_t value_size, uint32_t max_entries)
{
// TODO: Replace this with the CRT helper to create FD from handle once we
// have real handles.
int fd = static_cast<int>(_map_file_descriptors.size() + 1);
_map_file_descriptors.push_back({handle, {fd, type, key_size, value_size, 0}});
_map_file_descriptors.emplace_back(handle, fd, type, key_size, value_size, max_entries, 0);
return static_cast<int>(_map_file_descriptors.size());
}

Просмотреть файл

@ -12,7 +12,11 @@ void
cache_map_file_descriptors(const EbpfMapDescriptor* map_descriptors, uint32_t map_descriptors_count);
void
cache_map_file_descriptor(uint32_t type, uint32_t key_size, uint32_t value_size, int fd);
cache_map_file_descriptor(uint32_t type, uint32_t key_size, uint32_t value_size, uint32_t max_entries, int fd);
void
cache_map_file_descriptor_with_handle(
uint32_t type, uint32_t key_size, uint32_t value_size, uint32_t max_entries, int fd, uintptr_t handle);
void
clear_map_descriptors(void);
@ -22,3 +26,6 @@ get_map_descriptor_at_index(int index);
uintptr_t
get_map_handle_at_index(size_t index);
void
clear_program_information_cache();

Просмотреть файл

@ -3,7 +3,9 @@
#include <map>
#include <stdexcept>
#include "ebpf_api.h"
#include <vector>
#include <Windows.h>
#include "device_helper.hpp"
#include "ebpf_bind_program_data.h"
#include "ebpf_platform.h"
#include "ebpf_program_types.h"
@ -13,7 +15,6 @@
#include "platform.h"
#undef VOID
#include "platform.hpp"
#include "windows_helpers.hpp"
struct guid_compare
{
@ -24,9 +25,13 @@ struct guid_compare
}
};
thread_local std::map<GUID, ebpf_helper::ebpf_memory_ptr, guid_compare> g_program_information_cache;
static thread_local std::map<GUID, ebpf_helper::ebpf_memory_ptr, guid_compare> _program_information_cache;
ebpf_handle_t device_handle = INVALID_HANDLE_VALUE;
void
clear_program_information_cache()
{
_program_information_cache.clear();
}
uint32_t
get_program_information_data(ebpf_program_type_t program_type, ebpf_extension_data_t** program_information_data)
@ -36,7 +41,7 @@ get_program_information_data(ebpf_program_type_t program_type, ebpf_extension_da
sizeof(request), ebpf_operation_id_t::EBPF_OPERATION_GET_PROGRAM_INFORMATION, program_type};
auto reply = reinterpret_cast<ebpf_operation_get_program_information_reply_t*>(reply_buffer.data());
uint32_t retval = invoke_ioctl(device_handle, request, reply_buffer);
uint32_t retval = invoke_ioctl(request, reply_buffer);
if (retval != ERROR_SUCCESS) {
return retval;
}
@ -70,8 +75,8 @@ get_program_type_info(const ebpf_program_information_t** info)
size_t encoded_data_size = 0;
// See if we already have the program information cached.
auto it = g_program_information_cache.find(*program_type);
if (it == g_program_information_cache.end()) {
auto it = _program_information_cache.find(*program_type);
if (it == _program_information_cache.end()) {
// Try to query the information from the execution context.
ebpf_extension_data_t* program_information_data;
uint32_t error = get_program_information_data(*program_type, &program_information_data);
@ -99,10 +104,10 @@ get_program_type_info(const ebpf_program_information_t** info)
return result;
}
g_program_information_cache[*program_type] = ebpf_helper::ebpf_memory_ptr(program_information);
_program_information_cache[*program_type] = ebpf_helper::ebpf_memory_ptr(program_information);
}
*info = (const ebpf_program_information_t*)g_program_information_cache[*program_type].get();
*info = (const ebpf_program_information_t*)_program_information_cache[*program_type].get();
return EBPF_SUCCESS;
}

Просмотреть файл

@ -4,9 +4,8 @@
#include <map>
#include <stdexcept>
#include "api_common.hpp"
#include "api_internal.h"
#include "api_service.h"
#include "ebpf_api.h"
#include "device_helper.hpp"
#include "ebpf_platform.h"
#include "ebpf_protocol.h"
#include "map_descriptors.hpp"
@ -17,7 +16,189 @@ extern "C"
}
#include "Verifier.h"
#include "verifier_service.h"
#include "windows_helpers.hpp"
#define MAX_CODE_SIZE_IN_BYTES (32 * 1024) // 32 KB
static ebpf_result_t
_build_helper_id_to_address_map(
ebpf_handle_t program_handle, ebpf_code_buffer_t& byte_code, std::map<uint32_t, uint64_t>& helper_id_to_adddress)
{
ebpf_inst* instructions = reinterpret_cast<ebpf_inst*>(byte_code.data());
for (size_t index = 0; index < byte_code.size() / sizeof(ebpf_inst); index++) {
ebpf_inst& instruction = instructions[index];
if (instruction.opcode != INST_OP_CALL) {
continue;
}
helper_id_to_adddress[instruction.imm] = 0;
}
if (helper_id_to_adddress.size() == 0)
return EBPF_SUCCESS;
ebpf_protocol_buffer_t request_buffer(
offsetof(ebpf_operation_resolve_helper_request_t, helper_id) + sizeof(uint32_t) * helper_id_to_adddress.size());
ebpf_protocol_buffer_t reply_buffer(
offsetof(ebpf_operation_resolve_helper_reply_t, address) + sizeof(uint64_t) * helper_id_to_adddress.size());
auto request = reinterpret_cast<ebpf_operation_resolve_helper_request_t*>(request_buffer.data());
auto reply = reinterpret_cast<ebpf_operation_resolve_helper_reply_t*>(reply_buffer.data());
request->header.id = ebpf_operation_id_t::EBPF_OPERATION_RESOLVE_HELPER;
request->header.length = static_cast<uint16_t>(request_buffer.size());
request->program_handle = reinterpret_cast<uint64_t>(program_handle);
size_t index = 0;
for (const auto& [helper_id, address] : helper_id_to_adddress) {
request->helper_id[index++] = helper_id;
}
uint32_t result = invoke_ioctl(request_buffer, reply_buffer);
if (result != ERROR_SUCCESS) {
return windows_error_to_ebpf_result(result);
}
index = 0;
for (auto& [helper_id, address] : helper_id_to_adddress) {
address = reply->address[index++];
}
return EBPF_SUCCESS;
}
static ebpf_result_t
_resolve_ec_function(ebpf_ec_function_t function, uint64_t* address)
{
ebpf_operation_get_ec_function_request_t request = {sizeof(request), EBPF_OPERATION_GET_EC_FUNCTION, function};
ebpf_operation_get_ec_function_reply_t reply;
uint32_t result = invoke_ioctl(request, reply);
if (result != ERROR_SUCCESS) {
return windows_error_to_ebpf_result(result);
}
if (reply.header.id != ebpf_operation_id_t::EBPF_OPERATION_GET_EC_FUNCTION) {
return EBPF_INVALID_ARGUMENT;
}
*address = reply.address;
return EBPF_SUCCESS;
}
static ebpf_result_t
_resolve_maps_in_byte_code(ebpf_handle_t program_handle, ebpf_code_buffer_t& byte_code)
{
std::vector<size_t> instruction_offsets;
std::vector<uint64_t> map_handles;
ebpf_inst* instructions = reinterpret_cast<ebpf_inst*>(byte_code.data());
ebpf_inst* instruction_end = reinterpret_cast<ebpf_inst*>(byte_code.data() + byte_code.size());
for (size_t index = 0; index < byte_code.size() / sizeof(ebpf_inst); index++) {
ebpf_inst& first_instruction = instructions[index];
ebpf_inst& second_instruction = instructions[index + 1];
if (first_instruction.opcode != INST_OP_LDDW_IMM) {
continue;
}
if (&instructions[index + 1] >= instruction_end) {
return EBPF_INVALID_ARGUMENT;
}
index++;
// Check for LD_MAP flag
if (first_instruction.src != 1) {
continue;
}
uint64_t imm =
static_cast<uint64_t>(first_instruction.imm) | (static_cast<uint64_t>(second_instruction.imm) << 32);
instruction_offsets.push_back(index - 1);
map_handles.push_back(imm);
}
if (map_handles.empty()) {
return EBPF_SUCCESS;
}
ebpf_protocol_buffer_t request_buffer(
offsetof(ebpf_operation_resolve_map_request_t, map_handle) + sizeof(uint64_t) * map_handles.size());
ebpf_protocol_buffer_t reply_buffer(
offsetof(ebpf_operation_resolve_map_reply_t, address) + sizeof(uint64_t) * map_handles.size());
auto request = reinterpret_cast<ebpf_operation_resolve_map_request_t*>(request_buffer.data());
auto reply = reinterpret_cast<ebpf_operation_resolve_map_reply_t*>(reply_buffer.data());
request->header.id = ebpf_operation_id_t::EBPF_OPERATION_RESOLVE_MAP;
request->header.length = static_cast<uint16_t>(request_buffer.size());
request->program_handle = reinterpret_cast<uint64_t>(program_handle);
for (size_t index = 0; index < map_handles.size(); index++) {
if (map_handles[index] > get_map_descriptor_size()) {
return EBPF_ERROR_INVALID_HANDLE;
}
request->map_handle[index] = get_map_handle_at_index((int)map_handles[index] - 1);
}
uint32_t result = invoke_ioctl(request_buffer, reply_buffer);
if (result != ERROR_SUCCESS) {
return windows_error_to_ebpf_result(result);
}
for (size_t index = 0; index < map_handles.size(); index++) {
ebpf_inst& first_instruction = instructions[instruction_offsets[index]];
ebpf_inst& second_instruction = instructions[instruction_offsets[index] + 1];
// Clear LD_MAP flag
first_instruction.src = 0;
// Replace handle with address
uint64_t new_imm = reply->address[index];
first_instruction.imm = static_cast<uint32_t>(new_imm);
second_instruction.imm = static_cast<uint32_t>(new_imm >> 32);
}
return EBPF_SUCCESS;
}
static ebpf_result_t
_query_and_cache_map_descriptors(fd_handle_map* handle_map, uint32_t handle_map_count)
{
ebpf_result_t result;
EbpfMapDescriptor descriptor;
if (handle_map_count > 0) {
for (uint32_t i = 0; i < handle_map_count; i++) {
uint32_t size;
descriptor = {0};
result = query_map_definition(
handle_map[i].handle,
&size,
&descriptor.type,
&descriptor.key_size,
&descriptor.value_size,
&descriptor.max_entries);
if (result != EBPF_SUCCESS) {
return result;
}
cache_map_file_descriptor_with_handle(
descriptor.type,
descriptor.key_size,
descriptor.value_size,
descriptor.max_entries,
handle_map[i].file_descriptor,
(uintptr_t)handle_map[i].handle);
}
}
return EBPF_SUCCESS;
}
void
ebpf_clear_thread_local_storage() noexcept
{
clear_map_descriptors();
clear_program_information_cache();
}
ebpf_result_t
ebpf_verify_program(
@ -28,10 +209,9 @@ ebpf_verify_program(
uint32_t byte_code_size,
uint8_t* byte_code,
const char** logs,
uint32_t* logs_size)
uint32_t* logs_size) noexcept
{
ebpf_result_t result = EBPF_SUCCESS;
int error = 0;
// Only kernel execution context supported currently.
if (execution_context == execution_context_user_mode) {
@ -45,12 +225,8 @@ ebpf_verify_program(
cache_map_file_descriptors(map_descriptors, map_descriptors_count);
}
// Verify the program
error = verify_byte_code(program_type, byte_code, byte_code_size, logs, logs_size);
if (error != 0) {
result = EBPF_VALIDATION_FAILED;
}
// Verify the program.
result = verify_byte_code(program_type, byte_code, byte_code_size, logs, logs_size);
} catch (const std::bad_alloc&) {
result = EBPF_NO_MEMORY;
} catch (std::runtime_error& err) {
@ -64,3 +240,165 @@ ebpf_verify_program(
return result;
}
ebpf_result_t
ebpf_verify_and_load_program(
const GUID* program_type,
ebpf_handle_t program_handle,
ebpf_execution_context_t execution_context,
ebpf_execution_type_t execution_type,
uint32_t handle_map_count,
fd_handle_map* handle_map,
uint32_t byte_code_size,
uint8_t* byte_code,
const char** error_message,
uint32_t* error_message_size) noexcept
{
ebpf_result_t result = EBPF_SUCCESS;
int error = 0;
uint64_t log_function_address;
struct ubpf_vm* vm = nullptr;
ebpf_protocol_buffer_t request_buffer;
ebpf_operation_load_code_request_t* request = nullptr;
// Only kernel execution context supported currently.
if (execution_context == execution_context_user_mode) {
return EBPF_INVALID_ARGUMENT;
}
*error_message = nullptr;
*error_message_size = 0;
clear_map_descriptors();
// Query map descriptors from execution context.
try {
result = _query_and_cache_map_descriptors(handle_map, handle_map_count);
if (result != EBPF_SUCCESS) {
goto Exit;
}
// Verify the program
result = verify_byte_code(program_type, byte_code, byte_code_size, error_message, error_message_size);
if (result != EBPF_SUCCESS) {
goto Exit;
}
if (byte_code_size > MAX_CODE_SIZE_IN_BYTES) {
result = EBPF_PROGRAM_TOO_LARGE;
goto Exit;
}
ebpf_code_buffer_t byte_code_buffer(byte_code, byte_code + byte_code_size);
result = _resolve_maps_in_byte_code(program_handle, byte_code_buffer);
if (result != EBPF_SUCCESS) {
goto Exit;
}
result = _resolve_ec_function(EBPF_EC_FUNCTION_LOG, &log_function_address);
if (result != EBPF_SUCCESS) {
goto Exit;
}
if (execution_type == EBPF_EXECUTION_JIT) {
std::map<uint32_t, uint64_t> helper_id_to_adddress;
result = _build_helper_id_to_address_map(program_handle, byte_code_buffer, helper_id_to_adddress);
if (result != EBPF_SUCCESS)
goto Exit;
ebpf_code_buffer_t machine_code(MAX_CODE_SIZE_IN_BYTES);
size_t machine_code_size = machine_code.size();
// JIT code.
vm = ubpf_create();
if (vm == nullptr) {
result = EBPF_JIT_COMPILATION_FAILED;
goto Exit;
}
for (const auto& [helper_id, address] : helper_id_to_adddress) {
if (ubpf_register(vm, helper_id, nullptr, reinterpret_cast<void*>(address)) < 0) {
result = EBPF_JIT_COMPILATION_FAILED;
goto Exit;
}
}
ubpf_set_error_print(
vm, reinterpret_cast<int (*)(FILE * stream, const char* format, ...)>(log_function_address));
if (ubpf_load(
vm,
byte_code_buffer.data(),
static_cast<uint32_t>(byte_code_buffer.size()),
const_cast<char**>(error_message)) < 0) {
result = EBPF_JIT_COMPILATION_FAILED;
goto Exit;
}
if (ubpf_translate(vm, machine_code.data(), &machine_code_size, const_cast<char**>(error_message))) {
result = EBPF_JIT_COMPILATION_FAILED;
goto Exit;
}
machine_code.resize(machine_code_size);
byte_code_buffer = machine_code;
if (*error_message != nullptr) {
*error_message_size = (uint32_t)strlen(*error_message);
}
}
request_buffer.resize(offsetof(ebpf_operation_load_code_request_t, code) + byte_code_buffer.size());
request = reinterpret_cast<ebpf_operation_load_code_request_t*>(request_buffer.data());
request->header.id = ebpf_operation_id_t::EBPF_OPERATION_LOAD_CODE;
request->header.length = static_cast<uint16_t>(request_buffer.size());
request->program_handle = reinterpret_cast<uint64_t>(program_handle);
request->code_type = execution_type == EBPF_EXECUTION_JIT ? EBPF_CODE_NATIVE : EBPF_CODE_EBPF;
std::copy(
byte_code_buffer.begin(),
byte_code_buffer.end(),
request_buffer.begin() + offsetof(ebpf_operation_load_code_request_t, code));
error = invoke_ioctl(request_buffer);
if (error != ERROR_SUCCESS) {
result = EBPF_PROGRAM_LOAD_FAILED;
goto Exit;
}
} catch (const std::bad_alloc&) {
result = EBPF_NO_MEMORY;
} catch (std::runtime_error& err) {
auto message = err.what();
*error_message = allocate_error_string(message, error_message_size);
result = EBPF_VALIDATION_FAILED;
} catch (...) {
result = EBPF_FAILED;
}
Exit:
if (vm) {
ubpf_destroy(vm);
}
return result;
}
uint32_t
ebpf_service_initialize() noexcept
{
// This is best effort. If device handle does not initialize,
// it will be re-attempted before an IOCTL call is made.
// This is needed to ensure the service can successfully start
// even if the driver is not installed.
initialize_device_handle();
return ERROR_SUCCESS;
}
void
ebpf_service_cleanup() noexcept
{
clean_up_device_handle();
}

Просмотреть файл

@ -3,8 +3,12 @@
#pragma once
#include "api_internal.h"
#include "ebpf_api.h"
#include "ebpf_execution_context.h"
#include "ebpf_execution_type.h"
#include "ebpf_result.h"
#include "rpc_interface_h.h"
ebpf_result_t
ebpf_verify_program(
@ -15,4 +19,26 @@ ebpf_verify_program(
uint32_t byte_code_size,
uint8_t* byte_code,
const char** logs,
uint32_t* logs_size);
uint32_t* logs_size) noexcept;
ebpf_result_t
ebpf_verify_and_load_program(
const GUID* program_type,
ebpf_handle_t program_handle,
ebpf_execution_context_t execution_context,
ebpf_execution_type_t execution_type,
uint32_t handle_map_count,
fd_handle_map* handle_map,
uint32_t byte_code_size,
uint8_t* byte_code,
const char** error_message,
uint32_t* error_message_size) noexcept;
uint32_t
ebpf_service_initialize() noexcept;
void
ebpf_service_cleanup() noexcept;
void
ebpf_clear_thread_local_storage() noexcept;

Просмотреть файл

@ -131,7 +131,7 @@
<ConformanceMode>true</ConformanceMode>
<PrecompiledHeader>NotUsing</PrecompiledHeader>
<PrecompiledHeaderFile>pch.h</PrecompiledHeaderFile>
<AdditionalIncludeDirectories>$(SolutionDir)libs\api_common;$(SolutionDir)libs\api;$(SolutionDir)include;$(SolutionDir)libs\platform;$(SolutionDir)libs\platform\user;$(SolutionDir)libs\execution_context;$(SolutionDir)external\ubpf\vm;$(SolutionDir)external\ubpf\vm\inc;$(SolutionDir)external\ebpf-verifier\src;$(SolutionDir)external\ebpf-verifier\external;$(OutDir);%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
<AdditionalIncludeDirectories>$(SolutionDir)rpc_interface;$(SolutionDir)libs\api_common;$(SolutionDir)libs\api;$(SolutionDir)include;$(SolutionDir)libs\platform;$(SolutionDir)libs\platform\user;$(SolutionDir)libs\execution_context;$(SolutionDir)external\ubpf\vm;$(SolutionDir)external\ubpf\vm\inc;$(SolutionDir)external\ebpf-verifier\src;$(SolutionDir)external\ebpf-verifier\external;$(OutDir);%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
<LanguageStandard>stdcpplatest</LanguageStandard>
<RuntimeLibrary>MultiThreadedDebugDLL</RuntimeLibrary>
<TreatWarningAsError>true</TreatWarningAsError>
@ -152,7 +152,7 @@
<ConformanceMode>true</ConformanceMode>
<PrecompiledHeader>NotUsing</PrecompiledHeader>
<PrecompiledHeaderFile>pch.h</PrecompiledHeaderFile>
<AdditionalIncludeDirectories>$(SolutionDir)libs\api_common;$(SolutionDir)libs\api;$(SolutionDir)include;$(SolutionDir)libs\platform;$(SolutionDir)libs\platform\user;$(SolutionDir)libs\execution_context;$(SolutionDir)external\ubpf\vm;$(SolutionDir)external\ubpf\vm\inc;$(SolutionDir)external\ebpf-verifier\src;$(SolutionDir)external\ebpf-verifier\external;$(OutDir);%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
<AdditionalIncludeDirectories>$(SolutionDir)rpc_interface;$(SolutionDir)libs\api_common;$(SolutionDir)libs\api;$(SolutionDir)include;$(SolutionDir)libs\platform;$(SolutionDir)libs\platform\user;$(SolutionDir)libs\execution_context;$(SolutionDir)external\ubpf\vm;$(SolutionDir)external\ubpf\vm\inc;$(SolutionDir)external\ebpf-verifier\src;$(SolutionDir)external\ebpf-verifier\external;$(OutDir);%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
<LanguageStandard>stdcpplatest</LanguageStandard>
<TreatWarningAsError>true</TreatWarningAsError>
</ClCompile>

Просмотреть файл

@ -20,13 +20,13 @@
#include "tlv.h"
#include "windows_platform_service.hpp"
static int
analyze(raw_program& raw_prog, const char** error_message, uint32_t* error_message_size = nullptr)
static ebpf_result_t
_analyze(raw_program& raw_prog, const char** error_message, uint32_t* error_message_size = nullptr)
{
std::variant<InstructionSeq, std::string> prog_or_error = unmarshal(raw_prog);
if (!std::holds_alternative<InstructionSeq>(prog_or_error)) {
*error_message = allocate_error_string(std::get<std::string>(prog_or_error), error_message_size);
return 1; // Error;
return EBPF_VALIDATION_FAILED; // Error;
}
InstructionSeq& prog = std::get<InstructionSeq>(prog_or_error);
@ -43,12 +43,12 @@ analyze(raw_program& raw_prog, const char** error_message, uint32_t* error_messa
(void)ebpf_verify_program(oss, prog, raw_prog.info, &options, &stats);
*error_message = allocate_error_string(oss.str(), error_message_size);
return 1; // Error;
return EBPF_VALIDATION_FAILED; // Error;
}
return 0; // Success.
return EBPF_SUCCESS; // Success.
}
int
ebpf_result_t
verify_byte_code(
const GUID* program_type,
const uint8_t* byte_code,
@ -66,5 +66,5 @@ verify_byte_code(
raw_program raw_prog{file, section, instructions, info};
return analyze(raw_prog, error_message, error_message_size);
return _analyze(raw_prog, error_message, error_message_size);
}

Просмотреть файл

@ -7,7 +7,7 @@
#undef VOID
#include "platform.hpp"
int
ebpf_result_t
verify_byte_code(
const GUID* program_type,
const uint8_t* byte_code,

Просмотреть файл

@ -4,6 +4,7 @@
*/
import "ebpf_execution_context.h";
import "ebpf_execution_type.h";
import "ebpf_result.h";
import "stdint.h";
import "wtypes.idl";
@ -38,11 +39,14 @@ import "wtypes.idl";
typedef struct _ebpf_program_load_info
{
// Optional file name with full path.
// Optional file name.
[string] char* file_name;
// Optional section name.
[string] char* section_name;
// Optional program name.
[string] char* program_name;
GUID program_type;
ebpf_execution_type_t execution_type;
file_handle_t program_handle;
ebpf_execution_context_t execution_context;
uint32_t map_count;
@ -61,7 +65,7 @@ import "wtypes.idl";
[ size_is(byte_code_size), ref ] uint8_t* byte_code;
} ebpf_program_verify_info;
ebpf_result_t verify_and_jit_program(
ebpf_result_t verify_and_load_program(
[ in, ref ] ebpf_program_load_info * info,
[ out, ref ] uint32_t * logs_size,
[ out, size_is(, *logs_size), ref ] char** logs);

Просмотреть файл

@ -130,6 +130,7 @@
<AdditionalIncludeDirectories>$(SolutionDir)include;$(SolutionDir)libs\api;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
<DefaultCharType>Unsigned</DefaultCharType>
<AdditionalOptions>/prefix server "ebpf_server_" /prefix client "ebpf_client_" %(AdditionalOptions)</AdditionalOptions>
<MinimumTargetSystem>NT100</MinimumTargetSystem>
</Midl>
</ItemDefinitionGroup>
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'">

Просмотреть файл

@ -152,9 +152,9 @@
</Link>
</ItemDefinitionGroup>
<ItemGroup>
<ClCompile Include="..\..\ebpfapi\rpc_client.cpp" />
<ClCompile Include="main.cpp" />
<ClCompile Include="platform.cpp" />
<ClCompile Include="rpc_client.cpp" />
<ClCompile Include="service_helper.cpp" />
</ItemGroup>
<ItemGroup>
@ -184,8 +184,8 @@
<None Include="packages.config" />
</ItemGroup>
<ItemGroup>
<ClInclude Include="..\..\libs\api\rpc_client.h" />
<ClInclude Include="header.h" />
<ClInclude Include="rpc_client.h" />
<ClInclude Include="service_helper.h" />
</ItemGroup>
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />

Просмотреть файл

@ -15,9 +15,6 @@
</Filter>
</ItemGroup>
<ItemGroup>
<ClCompile Include="rpc_client.cpp">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="platform.cpp">
<Filter>Source Files</Filter>
</ClCompile>
@ -27,6 +24,9 @@
<ClCompile Include="service_helper.cpp">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="..\..\ebpfapi\rpc_client.cpp">
<Filter>Source Files</Filter>
</ClCompile>
</ItemGroup>
<ItemGroup>
<None Include="packages.config" />
@ -38,7 +38,7 @@
<ClInclude Include="service_helper.h">
<Filter>Source Files</Filter>
</ClInclude>
<ClInclude Include="rpc_client.h">
<ClInclude Include="..\..\libs\api\rpc_client.h">
<Filter>Header Files</Filter>
</ClInclude>
</ItemGroup>

Просмотреть файл

@ -1,80 +0,0 @@
// Copyright (c) Microsoft Corporation
// SPDX-License-Identifier: MIT
#include "ebpf_api.h"
#pragma warning(push)
#pragma warning(disable : 4100) // 'identifier' : unreferenced formal parameter
#pragma warning(disable : 4244) // 'conversion' conversion from 'type1' to
// 'type2', possible loss of data
#undef VOID
#include "ebpf_verifier.hpp"
#pragma warning(pop)
#include "ebpf_windows.h"
#include "header.h"
#include "rpc_interface_c.c"
#pragma comment(lib, "Rpcrt4.lib")
static RPC_WSTR _string_binding = nullptr;
static const WCHAR* _protocol_sequence = L"ncacn_np";
#define RPC_SERVER_ENDPOINT L"\\pipe\\ebpf_service"
int
ebpf_rpc_verify_program(ebpf_program_verify_info* info, const char** logs, uint32_t* logs_size)
{
unsigned long code;
int result;
RpcTryExcept { result = (int)ebpf_client_verify_program(info, logs_size, const_cast<char**>(logs)); }
RpcExcept(RpcExceptionFilter(RpcExceptionCode()))
{
code = RpcExceptionCode();
printf("ebpf_rpc_verify_program: runtime reported exception 0x%lx = %ld\n", code, code);
result = (int)EBPF_FAILED;
}
RpcEndExcept
printf("ebpf_rpc_verify_program: got return code %d from the server\n\n", result);
return result;
}
RPC_STATUS
initialize_rpc_binding()
{
RPC_STATUS status;
RPC_WSTR uuid = nullptr;
const WCHAR* network_address = nullptr;
RPC_WSTR options = nullptr;
status = RpcStringBindingCompose(
uuid,
(RPC_WSTR)_protocol_sequence,
(RPC_WSTR)network_address,
(RPC_WSTR)RPC_SERVER_ENDPOINT,
options,
&_string_binding);
if (status != RPC_S_OK) {
return status;
}
return RpcBindingFromStringBinding(_string_binding, &ebpf_service_interface_handle);
}
RPC_STATUS
clean_up_rpc_binding()
{
RPC_STATUS status = RpcStringFree(&_string_binding);
if (status != RPC_S_OK) {
printf("RpcStringFree failed with error %d\n", status);
}
status = RpcBindingFree(&ebpf_service_interface_handle);
if (status != RPC_S_OK) {
printf("RpcBindingFree failed with error %d\n", status);
}
return status;
}

Просмотреть файл

@ -80,7 +80,7 @@
<SDLCheck>true</SDLCheck>
<PreprocessorDefinitions>_DEBUG;_CONSOLE;%(PreprocessorDefinitions)</PreprocessorDefinitions>
<ConformanceMode>true</ConformanceMode>
<AdditionalIncludeDirectories>$(SolutionDir)include;$(SolutionDir)libs\platform;$(SolutionDir)libs\platform\user;$(SolutionDir)libs\execution_context;$(SolutionDir)libs\api;$(OutDir);%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
<AdditionalIncludeDirectories>$(SolutionDir)external\ebpf-verifier\src;$(SolutionDir)libs\service;$(SolutionDir)rpc_interface;$(SolutionDir)include;$(SolutionDir)libs\platform;$(SolutionDir)libs\platform\user;$(SolutionDir)libs\execution_context;$(SolutionDir)libs\api;$(OutDir);%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
<LanguageStandard>stdcpp17</LanguageStandard>
<TreatWarningAsError>true</TreatWarningAsError>
<RuntimeLibrary>MultiThreadedDebugDLL</RuntimeLibrary>
@ -98,7 +98,7 @@
<SDLCheck>true</SDLCheck>
<PreprocessorDefinitions>NDEBUG;_CONSOLE;%(PreprocessorDefinitions)</PreprocessorDefinitions>
<ConformanceMode>true</ConformanceMode>
<AdditionalIncludeDirectories>$(SolutionDir)include;$(SolutionDir)libs\platform;$(SolutionDir)libs\platform\user;$(SolutionDir)libs\execution_context;$(SolutionDir)libs\api;$(OutDir);%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
<AdditionalIncludeDirectories>$(SolutionDir)external\ebpf-verifier\src;$(SolutionDir)libs\service;$(SolutionDir)rpc_interface;$(SolutionDir)include;$(SolutionDir)libs\platform;$(SolutionDir)libs\platform\user;$(SolutionDir)libs\execution_context;$(SolutionDir)libs\api;$(OutDir);%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
<LanguageStandard>stdcpp17</LanguageStandard>
<TreatWarningAsError>true</TreatWarningAsError>
</ClCompile>

Просмотреть файл

@ -3,8 +3,11 @@
* SPDX-License-Identifier: MIT
*/
#include "mock.h"
#include "api_service.h"
#include "ebpf_api.h"
#include "mock.h"
#include "rpc_interface_h.h"
std::function<decltype(CreateFileW)> create_file_handler;
std::function<decltype(DeviceIoControl)> device_io_control_handler;
std::function<decltype(CloseHandle)> close_handle_handler;
@ -59,3 +62,29 @@ CloseHandle(_In_ _Post_ptr_invalid_ ebpf_handle_t handle)
}
} // namespace Platform
// RPC related mock functions.
RPC_STATUS
initialize_rpc_binding() { return RPC_S_OK; }
RPC_STATUS
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)
{
// Short circuit rpc call to service lib.
return ebpf_verify_and_load_program(
&info->program_type,
info->program_handle,
info->execution_context,
info->execution_type,
info->map_count,
info->handle_map,
info->byte_code_size,
info->byte_code,
const_cast<const char**>(logs),
logs_size);
}