Merged PR 4216677: Add test code for bind hook

Add test code for bind hook
This commit is contained in:
Alan Jowett 2021-03-09 17:59:43 +00:00
Родитель f974145fbc
Коммит b2312a4790
20 изменённых файлов: 579 добавлений и 119 удалений

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

@ -42,6 +42,8 @@ extern "C" {
const char* section_name,
ebpf_execution_type_t execution_type,
ebpf_handle_t* handle,
uint32_t* count_of_map_handles,
ebpf_handle_t* map_handles,
const char** error_message);
/**
@ -105,6 +107,22 @@ extern "C" {
uint32_t key_size,
const uint8_t* key);
/**
* @brief Return the next key in an eBPF map.
* @param[in] handle Handle to eBPF map.
* @param[in] key_size Size of the key buffer.
* @param[in] previous_key Pointer to buffer containing
previous key or NULL to restart enumeration.
* @param[out] next_key Pointer to buffer that contains next
* key on success.
* @retval ERROR_NO_MORE_ITEMS previous_key was the last key.
* @retval ERROR_NOT_FOUND previous_key was deleted.
*/
uint32_t ebpf_api_map_next_key(ebpf_handle_t handle,
uint32_t key_size,
const uint8_t* previous_key,
uint8_t* next_key);
/**
* @brief Enumerate through eBPF maps.
* @param[in] previous_handle Handle to previous eBPF map or

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

@ -55,6 +55,10 @@ ebpf_error_code_t ebpf_core_protocol_map_delete_element(
_In_ const struct _ebpf_operation_map_delete_element_request* request,
_Inout_ void* reply);
ebpf_error_code_t ebpf_core_protocol_map_get_next_key(
_In_ const struct _ebpf_operation_map_next_key_request* request,
_Inout_ struct _ebpf_operation_map_next_key_reply* reply);
ebpf_error_code_t ebpf_core_protocol_enumerate_maps(
_In_ const struct _ebpf_operation_enumerate_maps_request* request,
_Inout_ struct _ebpf_operation_enumerate_maps_reply* reply);

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

@ -20,6 +20,7 @@ typedef struct _ebpf_map_function_table
uint8_t* (*lookup_entry)(_In_ ebpf_core_map_t* map, _In_ const uint8_t* key);
ebpf_error_code_t (*update_entry)(_In_ ebpf_core_map_t* map, _In_ const uint8_t* key, _In_ const uint8_t* value);
ebpf_error_code_t (*delete_entry)(_In_ ebpf_core_map_t* map, _In_ const uint8_t* key);
ebpf_error_code_t (*next_key)(_In_ ebpf_core_map_t* map, _In_ const uint8_t* previous_key, _Out_ uint8_t* next_key);
} ebpf_map_function_table_t;
extern ebpf_map_function_table_t ebpf_map_function_tables[EBPF_MAP_TYPE_ARRAY + 1];

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

@ -45,6 +45,7 @@ void ebpf_hash_table_destroy(ebpf_hash_table_t* hash_table);
ebpf_error_code_t ebpf_hash_table_lookup(ebpf_hash_table_t* hash_table, const uint8_t* key, uint8_t** value);
ebpf_error_code_t ebpf_hash_table_update(ebpf_hash_table_t* hash_table, const uint8_t* key, const uint8_t* value);
ebpf_error_code_t ebpf_hash_table_delete(ebpf_hash_table_t* hash_table, const uint8_t* key);
ebpf_error_code_t ebpf_hash_table_next_key(ebpf_hash_table_t* hash_table, const uint8_t* previous_key, uint8_t* next_key);
#ifdef __cplusplus
}

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

@ -17,6 +17,7 @@ typedef enum _ebpf_operation_id {
EBPF_OPERATION_MAP_LOOKUP_ELEMENT,
EBPF_OPERATION_MAP_UPDATE_ELEMENT,
EBPF_OPERATION_MAP_DELETE_ELEMENT,
EBPF_OPERATION_MAP_NEXT_KEY,
EBPF_OPERATION_ENUMERATE_MAPS,
EBPF_OPERATION_QUERY_MAP_DEFINITION,
} ebpf_operation_id_t;
@ -135,3 +136,15 @@ typedef struct _ebpf_operation_query_map_definition_reply {
struct _ebpf_operation_header header;
struct _ebpf_map_definition map_definition;
} ebpf_operation_query_map_definition_reply;
typedef struct _ebpf_operation_map_next_key_request {
struct _ebpf_operation_header header;
uint64_t handle;
uint8_t previous_key[1];
} ebpf_operation_map_next_key_request_t;
typedef struct _ebpf_operation_map_next_key_reply {
struct _ebpf_operation_header header;
uint64_t handle;
uint8_t next_key[1];
} ebpf_operation_map_next_key_reply_t;

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

@ -39,6 +39,9 @@ typedef enum _ebpf_error_code
EBPF_ERROR_NOT_FOUND,
EBPF_ERROR_INVALID_PARAMETER,
EBPF_ERROR_BLOCKED_BY_POLICY,
EBPF_ERROR_NO_MORE_KEYS,
EBPF_ERROR_INVALID_HANDLE,
EBPF_ERROR_NOT_SUPPORTED
} ebpf_error_code_t;
typedef struct _ebpf_map_definition {

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

@ -264,7 +264,13 @@ static uint32_t resolve_maps_in_byte_code(std::vector<uint8_t>& byte_code)
return ERROR_SUCCESS;
}
uint32_t ebpf_api_load_program(const char* file_name, const char* section_name, ebpf_execution_type_t execution_type, ebpf_handle_t* handle, const char** error_message)
uint32_t ebpf_api_load_program(const char* file_name,
const char* section_name,
ebpf_execution_type_t execution_type,
ebpf_handle_t* handle,
uint32_t* count_of_map_handles,
ebpf_handle_t* map_handles,
const char** error_message)
{
std::vector<uint8_t> byte_code(MAX_CODE_SIZE);
size_t byte_code_size = byte_code.size();
@ -279,6 +285,10 @@ uint32_t ebpf_api_load_program(const char* file_name, const char* section_name,
{
ubpf_destroy(vm);
}
for (auto& map : _map_file_descriptors)
{
ebpf_api_delete_map(reinterpret_cast<ebpf_handle_t>(map.handle));
}
});
uint32_t result;
@ -305,6 +315,18 @@ uint32_t ebpf_api_load_program(const char* file_name, const char* section_name,
return ERROR_INVALID_PARAMETER;
}
if (_map_file_descriptors.size() > *count_of_map_handles)
{
return ERROR_INSUFFICIENT_BUFFER;
}
*count_of_map_handles = 0;
for (const auto& map : _map_file_descriptors)
{
map_handles[*count_of_map_handles] = reinterpret_cast<HANDLE>(map.handle);
(*count_of_map_handles)++;
}
byte_code.resize(byte_code_size);
result = resolve_maps_in_byte_code(byte_code);
if (result != ERROR_SUCCESS)
@ -368,6 +390,10 @@ uint32_t ebpf_api_load_program(const char* file_name, const char* section_name,
*handle = reinterpret_cast<ebpf_handle_t>(reply.handle);
if (result == ERROR_SUCCESS)
{
_map_file_descriptors.clear();
}
return result;
}
@ -378,7 +404,13 @@ void ebpf_api_free_error_message(const char* error_message)
void ebpf_api_unload_program(ebpf_handle_t handle)
{
CloseHandle(handle);
_ebpf_operation_unload_code_request request{
sizeof(request),
ebpf_operation_id_t::EBPF_OPERATION_UNLOAD_CODE,
reinterpret_cast<uint64_t>(handle) };
invoke_ioctl(device_handle, request);
return;
}
@ -458,6 +490,39 @@ uint32_t ebpf_api_map_delete_element(ebpf_handle_t handle, uint32_t key_size, co
return invoke_ioctl(device_handle, request_buffer);
}
uint32_t ebpf_api_map_next_key(ebpf_handle_t handle, uint32_t key_size, const uint8_t* previous_key, uint8_t* next_key)
{
std::vector<uint8_t> request_buffer(offsetof(ebpf_operation_map_next_key_request_t, previous_key) + key_size);
std::vector<uint8_t> reply_buffer(offsetof(ebpf_operation_map_next_key_reply_t, next_key) + key_size);
auto request = reinterpret_cast<ebpf_operation_map_next_key_request_t*>(request_buffer.data());
auto reply = reinterpret_cast<ebpf_operation_map_next_key_reply_t*>(reply_buffer.data());
request->header.length = static_cast<uint16_t>(request_buffer.size());
request->header.id = ebpf_operation_id_t::EBPF_OPERATION_MAP_NEXT_KEY;
request->handle = reinterpret_cast<uint64_t>(handle);
if (previous_key)
{
std::copy(previous_key, previous_key + key_size, request->previous_key);
}
else
{
request->header.length = offsetof(ebpf_operation_map_next_key_request_t, previous_key);
}
auto retval = invoke_ioctl(device_handle, request_buffer, reply_buffer);
if (reply->header.id != ebpf_operation_id_t::EBPF_OPERATION_MAP_NEXT_KEY)
{
return ERROR_INVALID_PARAMETER;
}
if (retval == ERROR_SUCCESS)
{
std::copy(reply->next_key, reply->next_key + key_size, next_key);
}
return retval;
}
uint32_t ebpf_api_map_enumerate(ebpf_handle_t previous_handle, ebpf_handle_t* next_handle)
{
_ebpf_operation_enumerate_maps_request request{

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

@ -33,9 +33,20 @@ constexpr EbpfContextDescriptor xdp_context_descriptor = {
const EbpfProgramType windows_xdp_program_type =
PTYPE("xdp", xdp_context_descriptor, EBPF_PROGRAM_TYPE_XDP, {"xdp"});
constexpr EbpfContextDescriptor bind_context_descriptor = {
43, // Size of ctx struct.
0, // Offset into ctx struct of pointer to data, or -1 if none.
8, // Offset into ctx struct of pointer to end of data, or -1 if none.
-1, // Offset into ctx struct of pointer to metadata, or -1 if none.
};
const EbpfProgramType windows_bind_program_type =
PTYPE("bind", bind_context_descriptor, EBPF_PROGRAM_TYPE_BIND, { "bind" });
const std::vector<EbpfProgramType> windows_program_types = {
PTYPE("unspecified", {0}, EBPF_PROGRAM_TYPE_UNSPECIFIED, {}),
windows_xdp_program_type,
windows_bind_program_type,
};
static EbpfProgramType get_program_type_windows(const std::string& section, const std::string&)

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

@ -201,6 +201,10 @@ static ebpf_error_code_t _ebpf_core_delete_code_entry(uint64_t handle)
_ebpf_core_code_entry_table[handle] = NULL;
retval = EBPF_ERROR_SUCCESS;
}
else
{
retval = EBPF_ERROR_INVALID_HANDLE;
}
ebpf_lock_unlock(&_ebpf_core_code_entry_table_lock, &state);
return retval;
}
@ -250,12 +254,15 @@ ebpf_core_protocol_attach_code(
}
code = _ebpf_core_find_user_code(request->handle);
if (code)
if (!code)
{
retval = EBPF_ERROR_INVALID_HANDLE;
goto Done;
}
code->hook_point = request->hook;
_ebpf_core_set_hook_entry(code, code->hook_point);
retval = EBPF_ERROR_SUCCESS;
}
Done:
return retval;
@ -272,13 +279,17 @@ ebpf_core_protocol_detach_code(
UNREFERENCED_PARAMETER(reply);
code = _ebpf_core_find_user_code(request->handle);
if (code)
if (!code)
{
retval = EBPF_ERROR_INVALID_HANDLE;
goto Done;
}
_ebpf_core_set_hook_entry(NULL, code->hook_point);
code->hook_point = EBPF_PROGRAM_TYPE_UNSPECIFIED;
retval = EBPF_ERROR_SUCCESS;
}
Done:
return retval;
}
@ -401,7 +412,7 @@ ebpf_error_code_t ebpf_core_protocol_resolve_map(
}
else
{
return EBPF_ERROR_NOT_FOUND;
return EBPF_ERROR_INVALID_HANDLE;
}
}
@ -433,7 +444,8 @@ ebpf_error_code_t ebpf_core_invoke_hook(
return EBPF_ERROR_SUCCESS;
}
}
return EBPF_ERROR_NOT_FOUND;
// Nothing to do if no hook is registered.
return EBPF_ERROR_SUCCESS;
}
ebpf_error_code_t ebpf_core_protocol_create_map(
@ -447,12 +459,12 @@ ebpf_error_code_t ebpf_core_protocol_create_map(
if (type >= RTL_COUNT_OF(ebpf_map_function_tables))
{
return EBPF_ERROR_NOT_FOUND;
return EBPF_ERROR_INVALID_PARAMETER;
}
if (ebpf_map_function_tables[type].create_map == NULL)
{
retval = EBPF_ERROR_NOT_FOUND;
retval = EBPF_ERROR_NOT_SUPPORTED;
goto Done;
}
@ -494,7 +506,7 @@ ebpf_error_code_t ebpf_core_protocol_map_lookup_element(
map = _ebpf_core_find_map_entry(request->handle);
if (!map)
{
retval = EBPF_ERROR_NOT_FOUND;
retval = EBPF_ERROR_INVALID_HANDLE;
goto Done;
}
@ -538,7 +550,7 @@ ebpf_error_code_t ebpf_core_protocol_map_update_element(
map = _ebpf_core_find_map_entry(request->handle);
if (!map)
{
retval = EBPF_ERROR_NOT_FOUND;
retval = EBPF_ERROR_INVALID_HANDLE;
goto Done;
}
@ -568,7 +580,7 @@ ebpf_error_code_t ebpf_core_protocol_map_delete_element(
map = _ebpf_core_find_map_entry(request->handle);
if (!map)
{
retval = EBPF_ERROR_NOT_FOUND;
retval = EBPF_ERROR_INVALID_HANDLE;
goto Done;
}
@ -586,6 +598,58 @@ Done:
return retval;
}
ebpf_error_code_t ebpf_core_protocol_map_get_next_key(
_In_ const ebpf_operation_map_next_key_request_t* request,
_Inout_ ebpf_operation_map_next_key_reply_t* reply)
{
ebpf_error_code_t retval;
ebpf_core_map_t* map = NULL;
size_t type;
const uint8_t* previous_key;
uint8_t* next_key = NULL;
map = _ebpf_core_find_map_entry(request->handle);
if (!map)
{
retval = EBPF_ERROR_INVALID_HANDLE;
goto Done;
}
type = map->ebpf_map_definition.type;
if (ebpf_map_function_tables[type].next_key == NULL)
{
retval = EBPF_ERROR_INVALID_PARAMETER;
goto Done;
}
// If request length shows zero key, treat as restart.
if (request->header.length == RTL_OFFSET_OF(ebpf_operation_map_next_key_request_t, previous_key))
{
previous_key = NULL;
}
else if (request->header.length < (RTL_OFFSET_OF(ebpf_operation_map_next_key_request_t, previous_key) + map->ebpf_map_definition.key_size))
{
retval = EBPF_ERROR_INVALID_PARAMETER;
goto Done;
}
else
{
previous_key = request->previous_key;
}
next_key = reply->next_key;
if (reply->header.length < (RTL_OFFSET_OF(ebpf_operation_map_next_key_reply_t, next_key) + map->ebpf_map_definition.key_size))
{
retval = EBPF_ERROR_INVALID_PARAMETER;
goto Done;
}
retval = ebpf_map_function_tables[type].next_key(map, previous_key, next_key);
Done:
return retval;
}
ebpf_error_code_t
ebpf_core_protocol_enumerate_maps(
_In_ const struct _ebpf_operation_enumerate_maps_request* request,
@ -625,7 +689,7 @@ ebpf_core_protocol_query_map_definition(
_In_ const struct _ebpf_operation_query_map_definition_request* request,
_Inout_ struct _ebpf_operation_query_map_definition_reply* reply)
{
ebpf_error_code_t retval = EBPF_ERROR_NOT_FOUND;
ebpf_error_code_t retval = EBPF_ERROR_INVALID_HANDLE;
ebpf_core_map_t* map = NULL;
map = _ebpf_core_find_map_entry(request->handle);

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

@ -66,7 +66,7 @@ static uint8_t* ebpf_lookup_array_map_entry(
return &map->data[key_value * map->ebpf_map_definition.value_size];
}
static ebpf_error_code_t ebpf_update_array_map(
static ebpf_error_code_t ebpf_update_array_map_entry(
_In_ ebpf_core_map_t* map,
_In_ const uint8_t* key,
_In_ const uint8_t* data)
@ -85,7 +85,7 @@ static ebpf_error_code_t ebpf_update_array_map(
return EBPF_ERROR_SUCCESS;
}
static ebpf_error_code_t ebpf_delete_array_entry(
static ebpf_error_code_t ebpf_delete_array_map_entry(
_In_ ebpf_core_map_t* map,
_In_ const uint8_t* key)
{
@ -126,6 +126,8 @@ ebpf_core_map_t* ebpf_create_hash_map(
{
goto Done;
}
ebpf_lock_create(&map->lock);
retval = EBPF_ERROR_SUCCESS;
Done:
@ -144,6 +146,7 @@ Done:
static void ebpf_delete_hash_map(
_In_ ebpf_core_map_t* map)
{
ebpf_lock_destroy(&map->lock);
ebpf_hash_table_destroy((ebpf_hash_table_t*)map->data);
ebpf_free(map);
}
@ -167,7 +170,7 @@ static uint8_t* ebpf_lookup_hash_map_entry(
return value;
}
static ebpf_error_code_t ebpf_update_hash_map(
static ebpf_error_code_t ebpf_update_hash_map_entry(
_In_ ebpf_core_map_t* map,
_In_ const uint8_t* key,
_In_ const uint8_t* data)
@ -183,7 +186,7 @@ static ebpf_error_code_t ebpf_update_hash_map(
return EBPF_ERROR_SUCCESS;
}
static ebpf_error_code_t ebpf_delete_hash_entry(
static ebpf_error_code_t ebpf_delete_hash_map_entry(
_In_ ebpf_core_map_t* map,
_In_ const uint8_t* key)
{
@ -199,6 +202,22 @@ static ebpf_error_code_t ebpf_delete_hash_entry(
}
static ebpf_error_code_t ebpf_next_hash_map_key(
_In_ ebpf_core_map_t* map,
_In_ const uint8_t* previous_key,
_Out_ uint8_t* next_key)
{
ebpf_error_code_t result;
ebpf_lock_state_t lock_state;
if (!map || !next_key)
return EBPF_ERROR_INVALID_PARAMETER;
ebpf_lock_lock(&map->lock, &lock_state);
result = ebpf_hash_table_next_key((ebpf_hash_table_t*)map->data, previous_key, next_key);
ebpf_lock_unlock(&map->lock, &lock_state);
return result;
}
ebpf_map_function_table_t ebpf_map_function_tables[] =
{
{ // EBPF_MAP_TYPE_UNSPECIFIED
@ -208,14 +227,16 @@ ebpf_map_function_table_t ebpf_map_function_tables[] =
ebpf_create_hash_map,
ebpf_delete_hash_map,
ebpf_lookup_hash_map_entry,
ebpf_update_hash_map,
ebpf_delete_hash_entry
ebpf_update_hash_map_entry,
ebpf_delete_hash_map_entry,
ebpf_next_hash_map_key
},
{ // EBPF_MAP_TYPE_ARRAY
ebpf_create_array_map,
ebpf_delete_array_map,
ebpf_lookup_array_map_entry,
ebpf_update_array_map,
ebpf_delete_array_entry
ebpf_update_array_map_entry,
ebpf_delete_array_map_entry,
NULL
},
};

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

@ -253,10 +253,58 @@ Done:
ebpf_error_code_t ebpf_hash_table_delete(ebpf_hash_table_t* hash_table, const uint8_t* key)
{
PRTL_AVL_TABLE table = NULL;
BOOLEAN result;
RTL_AVL_TABLE* table = (RTL_AVL_TABLE*)hash_table;
result = RtlDeleteElementGenericTableAvl(table, (uint8_t*)key);
return result == FALSE ? EBPF_ERROR_NOT_FOUND : EBPF_ERROR_SUCCESS;
}
ebpf_error_code_t ebpf_hash_table_next_key(ebpf_hash_table_t* hash_table, const uint8_t* previous_key, uint8_t* next_key)
{
RTL_AVL_TABLE* table = (RTL_AVL_TABLE*)hash_table;
uint8_t* entry;
size_t sizes = (size_t)table->TableContext;
uint16_t key_size = (uint16_t)(sizes >> 16);
void* restart_key;
if (!previous_key)
{
entry = RtlEnumerateGenericTableAvl(table, TRUE);
}
else
{
// Note - We need a better option to resume the search when the element was deleted.
entry = RtlLookupFirstMatchingElementGenericTableAvl(table, (void*)previous_key, &restart_key);
if (!entry)
{
// Entry deleted.
// Start at the begining of the table.
entry = RtlEnumerateGenericTableAvl(table, TRUE);
// Advance the cursor until we reach the first entry that is greater than the key.
while (!(ebpf_hash_map_compare(table, (uint8_t*)previous_key, entry) == GenericGreaterThan))
{
entry = RtlEnumerateGenericTableAvl(table, FALSE);
if (!entry)
{
break;
}
}
}
else
{
entry = RtlEnumerateGenericTableAvl(table, FALSE);
}
}
if (entry == NULL)
{
return EBPF_ERROR_NO_MORE_KEYS;
}
else
{
memcpy(next_key, entry, key_size);
}
return EBPF_ERROR_SUCCESS;
}

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

@ -108,7 +108,12 @@ ebpf_error_code_t ebpf_hash_table_create(ebpf_hash_table_t** hash_table, size_t
{
UNREFERENCED_PARAMETER(key_size);
UNREFERENCED_PARAMETER(value_size);
*hash_table = reinterpret_cast<ebpf_hash_table_t *>(new hash_table_t());
auto local_hash_table = new hash_table_t();
local_hash_table->key_size = key_size;
local_hash_table->value_size = value_size;
*hash_table = reinterpret_cast<ebpf_hash_table_t *>(local_hash_table);
return EBPF_ERROR_SUCCESS;
}
@ -129,7 +134,7 @@ ebpf_error_code_t ebpf_hash_table_lookup(ebpf_hash_table_t* hash_table, const ui
return EBPF_ERROR_NOT_FOUND;
}
std::copy(local_value->second.begin(), local_value->second.end(), *value);
*value = local_value->second.data();
return EBPF_ERROR_SUCCESS;
}
@ -157,3 +162,22 @@ ebpf_error_code_t ebpf_hash_table_delete(ebpf_hash_table_t* hash_table, const ui
return EBPF_ERROR_SUCCESS;
}
ebpf_error_code_t ebpf_hash_table_next_key(ebpf_hash_table_t* hash_table, const uint8_t* previous_key, uint8_t* next_key)
{
hash_table_t* local_hash_table = reinterpret_cast<decltype(local_hash_table)>(hash_table);
auto iter = local_hash_table->hash_table.begin();
if (previous_key)
{
std::vector<uint8_t> local_key(previous_key, previous_key + local_hash_table->key_size);
iter = local_hash_table->hash_table.upper_bound(local_key);
}
if (iter == local_hash_table->hash_table.end())
{
return EBPF_ERROR_NO_MORE_KEYS;
}
std::copy(iter->first.begin(), iter->first.end(), next_key);
return EBPF_ERROR_SUCCESS;
}

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

@ -34,6 +34,8 @@ Environment:
#include "ebpf_protocol.h"
#include "ebpf_core.h"
#define RTL_OFFSET_OF(s, m) (((size_t)&((s*)0)->m))
// Driver global variables
static DEVICE_OBJECT* _wdm_device_object;
@ -92,6 +94,12 @@ inline NTSTATUS ebpf_error_code_to_ntstatus(ebpf_error_code_t error)
case EBPF_ERROR_BLOCKED_BY_POLICY:
// TODO: Find a better erorr code for this.
return STATUS_NOT_SUPPORTED;
case EBPF_ERROR_NO_MORE_KEYS:
return STATUS_NO_MORE_MATCHES;
case EBPF_ERROR_INVALID_HANDLE:
return STATUS_INVALID_HANDLE;
case EBPF_ERROR_NOT_SUPPORTED:
return STATUS_NOT_SUPPORTED;
default:
return STATUS_INVALID_PARAMETER;
}
@ -271,6 +279,7 @@ static const struct {
{ ebpf_core_protocol_map_lookup_element, sizeof(struct _ebpf_operation_map_lookup_element_request), sizeof(struct _ebpf_operation_map_lookup_element_reply) },
{ ebpf_core_protocol_map_update_element, sizeof(struct _ebpf_operation_map_update_element_request), 0 },
{ ebpf_core_protocol_map_delete_element, sizeof(struct _ebpf_operation_map_delete_element_request), 0 },
{ ebpf_core_protocol_map_get_next_key, RTL_OFFSET_OF(ebpf_operation_map_next_key_request_t, previous_key), sizeof(ebpf_operation_map_next_key_reply_t) },
{ ebpf_core_protocol_enumerate_maps, sizeof(struct _ebpf_operation_enumerate_maps_request), sizeof(struct _ebpf_operation_enumerate_maps_reply) },
{ ebpf_core_protocol_query_map_definition, sizeof(struct _ebpf_operation_query_map_definition_request), sizeof(struct _ebpf_operation_query_map_definition_reply) },
};

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

@ -20,6 +20,7 @@ namespace ebpf
};
#include "unwind_helper.h"
#include <WinSock2.h>
static const struct {
ebpf_error_code_t(*protocol_handler)(_In_ const void* input_buffer, void* output_buffer);
@ -37,6 +38,7 @@ static const struct {
{ reinterpret_cast<ebpf_error_code_t(*)(_In_ const void* input_buffer, void* output_buffer)>(ebpf_core_protocol_map_lookup_element), sizeof(struct _ebpf_operation_map_lookup_element_request), sizeof(struct _ebpf_operation_map_lookup_element_reply) },
{ reinterpret_cast<ebpf_error_code_t(*)(_In_ const void* input_buffer, void* output_buffer)>(ebpf_core_protocol_map_update_element), sizeof(struct _ebpf_operation_map_update_element_request), 0 },
{ reinterpret_cast<ebpf_error_code_t(*)(_In_ const void* input_buffer, void* output_buffer)>(ebpf_core_protocol_map_delete_element), sizeof(struct _ebpf_operation_map_delete_element_request), 0 },
{ reinterpret_cast<ebpf_error_code_t(*)(_In_ const void* input_buffer, void* output_buffer)>(ebpf_core_protocol_map_get_next_key), offsetof(ebpf_operation_map_next_key_request_t, previous_key), sizeof(ebpf_operation_map_next_key_reply_t) },
{ reinterpret_cast<ebpf_error_code_t(*)(_In_ const void* input_buffer, void* output_buffer)>(ebpf_core_protocol_enumerate_maps), sizeof(struct _ebpf_operation_enumerate_maps_request), sizeof(struct _ebpf_operation_enumerate_maps_reply) },
{ reinterpret_cast<ebpf_error_code_t(*)(_In_ const void* input_buffer, void* output_buffer)>(ebpf_core_protocol_query_map_definition), sizeof(struct _ebpf_operation_query_map_definition_request), sizeof(struct _ebpf_operation_query_map_definition_reply) },
};
@ -130,6 +132,9 @@ GlueDeviceIoControl(
case EBPF_ERROR_INVALID_PARAMETER:
SetLastError(ERROR_INVALID_PARAMETER);
break;
case EBPF_ERROR_NO_MORE_KEYS:
SetLastError(ERROR_NO_MORE_ITEMS);
break;
default:
SetLastError(ERROR_INVALID_PARAMETER);
break;
@ -167,6 +172,8 @@ TEST_CASE("droppacket-jit", "[droppacket_jit]")
close_handle_handler = GlueCloseHandle;
ebpf_handle_t program_handle;
ebpf_handle_t map_handle;
uint32_t count_of_map_handle = 1;
uint32_t result = 0;
const char* error_message = NULL;
bool ec_initialized = false;
@ -185,7 +192,7 @@ TEST_CASE("droppacket-jit", "[droppacket_jit]")
REQUIRE(ebpf_api_initiate() == ERROR_SUCCESS);
api_initialized = true;
REQUIRE(ebpf_api_load_program(SAMPLE_PATH "droppacket.o", "xdp", EBPF_EXECUTION_JIT, &program_handle, &error_message) == ERROR_SUCCESS);
REQUIRE(ebpf_api_load_program(SAMPLE_PATH "droppacket.o", "xdp", EBPF_EXECUTION_JIT, &program_handle, &count_of_map_handle, &map_handle, &error_message) == ERROR_SUCCESS);
REQUIRE(ebpf_api_attach_program(program_handle, EBPF_PROGRAM_TYPE_XDP) == ERROR_SUCCESS);
@ -193,19 +200,19 @@ TEST_CASE("droppacket-jit", "[droppacket_jit]")
uint32_t key = 0;
uint64_t value = 1000;
REQUIRE(ebpf_api_map_update_element((ebpf_handle_t)1, sizeof(key), (uint8_t*)&key, sizeof(value), (uint8_t*)&value) == ERROR_SUCCESS);
REQUIRE(ebpf_api_map_update_element(map_handle, sizeof(key), (uint8_t*)&key, sizeof(value), (uint8_t*)&value) == ERROR_SUCCESS);
// Test that we drop the packet and increment the map
ebpf::xdp_md_t ctx{ packet.data(), packet.data() + packet.size() };
REQUIRE(ebpf_core_invoke_hook(EBPF_PROGRAM_TYPE_XDP, &ctx, &result) == EBPF_ERROR_SUCCESS);
REQUIRE(result == 2);
REQUIRE(ebpf_api_map_lookup_element((ebpf_handle_t)1, sizeof(key), (uint8_t*)&key, sizeof(value), (uint8_t*)&value) == ERROR_SUCCESS);
REQUIRE(ebpf_api_map_lookup_element(map_handle, sizeof(key), (uint8_t*)&key, sizeof(value), (uint8_t*)&value) == ERROR_SUCCESS);
REQUIRE(value == 1001);
REQUIRE(ebpf_api_map_delete_element((ebpf_handle_t)1, sizeof(key), (uint8_t*)&key) == ERROR_SUCCESS);
REQUIRE(ebpf_api_map_delete_element(map_handle, sizeof(key), (uint8_t*)&key) == ERROR_SUCCESS);
REQUIRE(ebpf_api_map_lookup_element((ebpf_handle_t)1, sizeof(key), (uint8_t*)&key, sizeof(value), (uint8_t*)&value) == ERROR_SUCCESS);
REQUIRE(ebpf_api_map_lookup_element(map_handle, sizeof(key), (uint8_t*)&key, sizeof(value), (uint8_t*)&value) == ERROR_SUCCESS);
REQUIRE(value == 0);
@ -215,7 +222,7 @@ TEST_CASE("droppacket-jit", "[droppacket_jit]")
REQUIRE(ebpf_core_invoke_hook(EBPF_PROGRAM_TYPE_XDP, &ctx2, &result) == EBPF_ERROR_SUCCESS);
REQUIRE(result == 1);
REQUIRE(ebpf_api_map_lookup_element((ebpf_handle_t)1, sizeof(key), (uint8_t*)&key, sizeof(value), (uint8_t*)&value) == ERROR_SUCCESS);
REQUIRE(ebpf_api_map_lookup_element(map_handle, sizeof(key), (uint8_t*)&key, sizeof(value), (uint8_t*)&value) == ERROR_SUCCESS);
REQUIRE(value == 0);
}
@ -229,6 +236,8 @@ TEST_CASE("droppacket-interpret", "[droppacket_interpret]")
const char* error_message = NULL;
bool ec_initialized = false;
bool api_initialized = false;
ebpf_handle_t map_handle;
uint32_t count_of_map_handle = 1;
_unwind_helper on_exit([&]
{
ebpf_api_free_error_message(error_message);
@ -245,7 +254,7 @@ TEST_CASE("droppacket-interpret", "[droppacket_interpret]")
REQUIRE(ebpf_api_initiate() == ERROR_SUCCESS);
api_initialized = true;
REQUIRE(ebpf_api_load_program(SAMPLE_PATH "droppacket.o", "xdp", EBPF_EXECUTION_INTERPRET, &program_handle, &error_message) == ERROR_SUCCESS);
REQUIRE(ebpf_api_load_program(SAMPLE_PATH "droppacket.o", "xdp", EBPF_EXECUTION_INTERPRET, &program_handle, &count_of_map_handle, &map_handle, &error_message) == ERROR_SUCCESS);
REQUIRE(ebpf_api_attach_program(program_handle, EBPF_PROGRAM_TYPE_XDP) == ERROR_SUCCESS);
@ -349,3 +358,121 @@ TEST_CASE("verify section", "[verify section]") {
REQUIRE(error_message == nullptr);
}
typedef struct _process_entry
{
uint32_t count;
uint8_t name[64];
uint64_t appid_length;
} process_entry_t;
uint32_t get_bind_count_for_pid(ebpf_handle_t handle, uint64_t pid)
{
process_entry_t entry{};
REQUIRE(ebpf_api_map_lookup_element(handle, sizeof(pid), (uint8_t*)&pid, sizeof(entry), (uint8_t*)&entry) == ERROR_SUCCESS);
return entry.count;
}
ebpf::bind_action_t emulate_bind(uint64_t pid, const char * appid)
{
uint32_t result;
std::string app_id = appid;
ebpf::bind_md_t ctx{0};
ctx.app_id_start = const_cast<char*>(app_id.c_str());
ctx.app_id_end = const_cast<char*>(app_id.c_str()) + app_id.size();
ctx.process_id = pid;
ctx.operation = ebpf::BIND_OPERATION_BIND;
REQUIRE(ebpf_core_invoke_hook(EBPF_PROGRAM_TYPE_BIND, &ctx, &result) == EBPF_ERROR_SUCCESS);
return static_cast<ebpf::bind_action_t>(result);
}
void emulate_unbind(uint64_t pid, const char* appid)
{
uint32_t result;
std::string app_id = appid;
ebpf::bind_md_t ctx{ 0 };
ctx.process_id = pid;
ctx.operation = ebpf::BIND_OPERATION_UNBIND;
REQUIRE(ebpf_core_invoke_hook(EBPF_PROGRAM_TYPE_BIND, &ctx, &result) == EBPF_ERROR_SUCCESS);
}
void set_bind_limit(ebpf_handle_t handle, uint32_t limit)
{
uint32_t limit_key = 0;
REQUIRE(ebpf_api_map_update_element(handle, sizeof(limit_key), (uint8_t*)&limit_key, sizeof(limit), (uint8_t*)&limit) == ERROR_SUCCESS);
}
TEST_CASE("bindmonitor-interpret", "[bindmonitor_interpret]")
{
device_io_control_handler = GlueDeviceIoControl;
create_file_handler = GlueCreateFileW;
close_handle_handler = GlueCloseHandle;
ebpf_handle_t program_handle;
const char* error_message = NULL;
bool ec_initialized = false;
bool api_initialized = false;
ebpf_handle_t map_handles[2];
uint32_t count_of_map_handles = 2;
uint64_t fake_pid = 12345;
_unwind_helper on_exit([&] {
ebpf_api_free_error_message(error_message);
if (api_initialized)
ebpf_api_terminate();
if (ec_initialized)
ebpf_core_terminate();
});
REQUIRE(ebpf_core_initialize() == EBPF_ERROR_SUCCESS);
ec_initialized = true;
REQUIRE(ebpf_api_initiate() == ERROR_SUCCESS);
api_initialized = true;
REQUIRE(ebpf_api_load_program(SAMPLE_PATH "bindmonitor.o", "bind", EBPF_EXECUTION_INTERPRET, &program_handle, &count_of_map_handles, map_handles, &error_message) == ERROR_SUCCESS);
REQUIRE(error_message == NULL);
REQUIRE(ebpf_api_attach_program(program_handle, EBPF_PROGRAM_TYPE_BIND) == ERROR_SUCCESS);
// Apply policy of maximum 2 binds per process
set_bind_limit(map_handles[1], 2);
// Bind first port - success
REQUIRE(emulate_bind(fake_pid, "fake_app_1") == ebpf::BIND_PERMIT);
REQUIRE(get_bind_count_for_pid(map_handles[0], fake_pid) == 1);
// Bind second port - success
REQUIRE(emulate_bind(fake_pid, "fake_app_1") == ebpf::BIND_PERMIT);
REQUIRE(get_bind_count_for_pid(map_handles[0], fake_pid) == 2);
// Bind third port - blocked
REQUIRE(emulate_bind(fake_pid, "fake_app_1") == ebpf::BIND_DENY);
REQUIRE(get_bind_count_for_pid(map_handles[0], fake_pid) == 2);
// Unbind second port
emulate_unbind(fake_pid, "fake_app_1");
REQUIRE(get_bind_count_for_pid(map_handles[0], fake_pid) == 1);
// Unbind first port
emulate_unbind(fake_pid, "fake_app_1");
REQUIRE(get_bind_count_for_pid(map_handles[0], fake_pid) == 0);
// Unbind a port we don't own
emulate_unbind(fake_pid, "fake_app_1");
REQUIRE(get_bind_count_for_pid(map_handles[0], fake_pid) == 0);
fake_pid = 54321;
REQUIRE(emulate_bind(fake_pid, "fake_app_2") == ebpf::BIND_PERMIT);
REQUIRE(get_bind_count_for_pid(map_handles[0], fake_pid) == 1);
uint64_t pid;
REQUIRE(ebpf_api_map_next_key(map_handles[0], sizeof(uint64_t), NULL, reinterpret_cast<uint8_t*>(&pid)) == ERROR_SUCCESS);
REQUIRE(pid != 0);
REQUIRE(ebpf_api_map_next_key(map_handles[0], sizeof(uint64_t), reinterpret_cast<uint8_t*>(&pid), reinterpret_cast<uint8_t*>(&pid)) == ERROR_SUCCESS);
REQUIRE(pid != 0);
REQUIRE(ebpf_api_map_next_key(map_handles[0], sizeof(uint64_t), reinterpret_cast<uint8_t*>(&pid), reinterpret_cast<uint8_t*>(&pid)) == ERROR_NO_MORE_ITEMS);
}

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

@ -11,35 +11,78 @@
#include "ebpf.h"
typedef struct _process_entry
{
uint32_t count;
uint8_t name[64];
} process_entry_t;
#pragma clang section data="maps"
bpf_map_def_t process_map = {
.size = sizeof(bpf_map_def_t),
.type = EBPF_MAP_TYPE_HASH,
.key_size = sizeof(__u64),
.value_size = sizeof(__u32),
.key_size = sizeof(uint64_t),
.value_size = sizeof(process_entry_t),
.max_entries = 1024
};
bpf_map_def_t limits_map = {
.size = sizeof(bpf_map_def_t),
.type = EBPF_MAP_TYPE_ARRAY,
.key_size = sizeof(uint32_t),
.value_size = sizeof(uint32_t),
.max_entries = 1
};
inline void copy_app_id(process_entry_t* entry, uint64_t start_index, char* begin, char* end)
{
uint64_t index = 0;
for (index = start_index; index < start_index + 16; index++)
{
entry->name[index] = (begin + index < end) ? begin[index] : 0;
}
}
#pragma clang section text="bind"
int BindMonitor(bind_md_t* ctx)
{
long key = ctx->process_id;
__u32* count = ebpf_map_lookup_elem(&process_map, &key);
if (!count)
uint64_t key = ctx->process_id;
uint32_t limit_key = 0;
uint32_t* limit = ebpf_map_lookup_elem(&limits_map, &limit_key);
if (!limit || *limit == 0)
{
long value = 0;
ebpf_map_update_element(&process_map, &value, &key, 0);
count = ebpf_map_lookup_elem(&process_map, &key);
return BIND_PERMIT;
}
process_entry_t* entry = ebpf_map_lookup_elem(&process_map, &key);
if (!entry)
{
process_entry_t value = { 0 };
// To work around a limitation in eBPF verifier, copy the string
// in blocks of 16 bytes. Copying all 64 bytes triggers a verification
// failure.
copy_app_id(&value, 0, ctx->app_id_start, ctx->app_id_end);
copy_app_id(&value, 16, ctx->app_id_start, ctx->app_id_end);
copy_app_id(&value, 32, ctx->app_id_start, ctx->app_id_end);
copy_app_id(&value, 48, ctx->app_id_start, ctx->app_id_end);
ebpf_map_update_element(&process_map, &key, &value, 0);
entry = ebpf_map_lookup_elem(&process_map, &key);
}
if (count)
switch (ctx->operation)
{
case BIND_OPERATION_BIND:
*count = (*count) + 1;
if (entry->count >= *limit)
{
return BIND_DENY;
}
entry->count++;
break;
case BIND_OPERATION_UNBIND:
*count = (*count) - 1;
if (entry->count > 0)
entry->count--;
break;
default:
break;
}

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

@ -15,8 +15,8 @@
bpf_map_def_t port_map = {
.size = sizeof(bpf_map_def_t),
.type = EBPF_MAP_TYPE_ARRAY,
.key_size = sizeof(__u32),
.value_size = sizeof(__u64),
.key_size = sizeof(uint32_t),
.value_size = sizeof(uint64_t),
.max_entries = 1
};

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

@ -3,12 +3,17 @@
* SPDX-License-Identifier: MIT
*/
typedef unsigned long __u64;
typedef unsigned int __u32;
typedef unsigned short __u16;
typedef unsigned char __u8;
#if defined(_MSC_VER)
typedef unsigned long long uint64_t;
#else
typedef unsigned long uint64_t;
#endif
__u16 ntohs(__u16 us)
typedef unsigned int uint32_t;
typedef unsigned short uint16_t;
typedef unsigned char uint8_t;
uint16_t ntohs(uint16_t us)
{
return us << 8 | us >> 8;
}
@ -17,17 +22,17 @@ typedef struct _xdp_md
{
void* data;
void* data_end;
__u64 data_meta;
uint64_t data_meta;
} xdp_md_t;
typedef struct _bind_md {
void* app_id_start; // 0,8
void* app_id_end; // 8,8
__u64 process_id; // 16,8
__u8 socket_address[16]; // 24,16
__u8 socket_address_length; // 40,1
__u8 operation; // 41,1
__u8 protocol; // 42,1
char* app_id_start;
char* app_id_end;
uint64_t process_id;
uint8_t socket_address[16];
uint8_t socket_address_length;
uint8_t operation;
uint8_t protocol;
} bind_md_t;
typedef enum _bind_operation
@ -46,51 +51,51 @@ typedef enum _bind_action
typedef struct _IPV4_HEADER {
union {
__u8 VersionAndHeaderLength; // Version and header length.
uint8_t VersionAndHeaderLength; // Version and header length.
struct {
__u8 HeaderLength : 4;
__u8 Version : 4;
uint8_t HeaderLength : 4;
uint8_t Version : 4;
};
};
union {
__u8 TypeOfServiceAndEcnField; // Type of service & ECN (RFC 3168).
uint8_t TypeOfServiceAndEcnField; // Type of service & ECN (RFC 3168).
struct {
__u8 EcnField : 2;
__u8 TypeOfService : 6;
uint8_t EcnField : 2;
uint8_t TypeOfService : 6;
};
};
__u16 TotalLength; // Total length of datagram.
__u16 Identification;
uint16_t TotalLength; // Total length of datagram.
uint16_t Identification;
union {
__u16 FlagsAndOffset; // Flags and fragment offset.
uint16_t FlagsAndOffset; // Flags and fragment offset.
struct {
__u16 DontUse1 : 5; // High bits of fragment offset.
__u16 MoreFragments : 1;
__u16 DontFragment : 1;
__u16 Reserved : 1;
__u16 DontUse2 : 8; // Low bits of fragment offset.
uint16_t DontUse1 : 5; // High bits of fragment offset.
uint16_t MoreFragments : 1;
uint16_t DontFragment : 1;
uint16_t Reserved : 1;
uint16_t DontUse2 : 8; // Low bits of fragment offset.
};
};
__u8 TimeToLive;
__u8 Protocol;
__u16 HeaderChecksum;
__u32 SourceAddress;
__u32 DestinationAddress;
uint8_t TimeToLive;
uint8_t Protocol;
uint16_t HeaderChecksum;
uint32_t SourceAddress;
uint32_t DestinationAddress;
} IPV4_HEADER, *PIPV4_HEADER;
typedef struct UDP_HEADER_ {
__u16 srcPort;
__u16 destPort;
__u16 length;
__u16 checksum;
uint16_t srcPort;
uint16_t destPort;
uint16_t length;
uint16_t checksum;
} UDP_HEADER;
typedef struct _bpf_map_def {
__u32 size;
__u32 type;
__u32 key_size;
__u32 value_size;
__u32 max_entries;
uint32_t size;
uint32_t type;
uint32_t key_size;
uint32_t value_size;
uint32_t max_entries;
} bpf_map_def_t;
typedef enum _ebpf_map_type {
@ -102,5 +107,5 @@ typedef enum _ebpf_map_type {
typedef void* (*ebpf_map_lookup_elem_t)(void * map, void* key);
#define ebpf_map_lookup_elem ((ebpf_map_lookup_elem_t)1)
typedef void (*ebpf_map_update_element_t)(void* map, void* key, void* data, __u64 flags);
typedef void (*ebpf_map_update_element_t)(void* map, void* key, void* data, uint64_t flags);
#define ebpf_map_update_element ((ebpf_map_update_element_t)2)

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

@ -90,6 +90,8 @@
<FileType>CppCode</FileType>
<Command>clang -target bpf -O2 -Wall -c %(Filename).c -o %(Filename).o</Command>
<Outputs>%(Filename).o;%(Outputs)</Outputs>
<TreatOutputAsContent Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">true</TreatOutputAsContent>
<TreatOutputAsContent Condition="'$(Configuration)|$(Platform)'=='Release|x64'">true</TreatOutputAsContent>
</CustomBuild>
<CustomBuild Include="bpf.c">
<FileType>CppCode</FileType>

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

@ -14,23 +14,23 @@
<Extensions>rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav;mfcribbon-ms</Extensions>
</Filter>
</ItemGroup>
<ItemGroup>
<ClCompile Include="bindmonitor.c">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="bpf.c">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="bpf_call.c">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="droppacket.c">
<Filter>Source Files</Filter>
</ClCompile>
</ItemGroup>
<ItemGroup>
<ClInclude Include="ebpf.h">
<Filter>Header Files</Filter>
</ClInclude>
</ItemGroup>
<ItemGroup>
<CustomBuild Include="bindmonitor.c">
<Filter>Source Files</Filter>
</CustomBuild>
<CustomBuild Include="bpf.c">
<Filter>Source Files</Filter>
</CustomBuild>
<CustomBuild Include="bpf_call.c">
<Filter>Source Files</Filter>
</CustomBuild>
<CustomBuild Include="droppacket.c">
<Filter>Source Files</Filter>
</CustomBuild>
</ItemGroup>
</Project>

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

@ -11,7 +11,8 @@
#include "api.h"
#include <iostream>
static HANDLE _program_handle = INVALID_HANDLE_VALUE;
static ebpf_handle_t _program_handle = INVALID_HANDLE_VALUE;
static ebpf_handle_t _map_handles[10];
typedef enum {
PINNED_ANY = 0,
@ -134,8 +135,8 @@ unsigned long handle_ebpf_add_program(
}
const char* error_message = nullptr;
status = ebpf_api_load_program(filename.c_str(), section.c_str(), execution, &_program_handle, &error_message);
uint32_t count_of_map_handles = sizeof(_map_handles);
status = ebpf_api_load_program(filename.c_str(), section.c_str(), execution, &_program_handle, &count_of_map_handles, _map_handles, &error_message);
if (status != ERROR_SUCCESS)
{
if (error_message != nullptr) {