diff --git a/.gitmodules b/.gitmodules index b90d85fbb..744d216c3 100644 --- a/.gitmodules +++ b/.gitmodules @@ -6,3 +6,6 @@ path = external/ubpf url = https://github.com/iovisor/ubpf.git branch = master +[submodule "external/libbpf"] + path = external/libbpf + url = https://github.com/libbpf/libbpf.git diff --git a/ebpfapi/Source.def b/ebpfapi/Source.def index d48d860aa..0138f99f8 100644 --- a/ebpfapi/Source.def +++ b/ebpfapi/Source.def @@ -5,34 +5,65 @@ LIBRARY EXPORTS - ebpf_api_initiate - ebpf_api_terminate - ebpf_api_load_program - ebpf_api_create_map - ebpf_api_map_find_element - ebpf_api_map_update_element - ebpf_api_map_delete_element - ebpf_api_get_next_map_key - ebpf_api_elf_enumerate_sections - ebpf_api_elf_disassemble_section - ebpf_api_elf_verify_section - ebpf_api_elf_free - ebpf_api_pin_object - ebpf_api_unpin_object - ebpf_api_get_next_map - ebpf_api_get_pinned_map - ebpf_api_get_next_program - ebpf_api_program_query_info - ebpf_api_link_program - ebpf_api_close_handle - ebpf_api_get_pinned_map_info - ebpf_api_map_info_free - ebpf_free_string - ebpf_map_get_fd - ebpf_map_next - ebpf_map_previous - ebpf_object_close - ebpf_program_get_fd - ebpf_program_load - ebpf_program_next - ebpf_program_previous + bpf_link__destroy + bpf_map__fd + bpf_map__is_pinned + bpf_map__key_size + bpf_map__max_entries + bpf_map__next + bpf_map__pin + bpf_map__prev + bpf_map__type + bpf_map__unpin + bpf_map__value_size + bpf_object__close + bpf_object__find_program_by_name + bpf_object__name + bpf_object__pin + bpf_object__pin_maps + bpf_object__pin_programs + bpf_object__unpin_maps + bpf_object__unpin_programs + bpf_prog_load + bpf_program__attach + bpf_program__attach_xdp + bpf_program__fd + bpf_program__get_expected_attach_type + bpf_program__name + bpf_program__next + bpf_program__pin + bpf_program__prev + bpf_program__section_name + bpf_program__set_expected_attach_type + bpf_program__unpin + ebpf_api_initiate + ebpf_api_terminate + ebpf_api_load_program + ebpf_api_create_map + ebpf_api_map_find_element + ebpf_api_map_update_element + ebpf_api_map_delete_element + ebpf_api_get_next_map_key + ebpf_api_elf_enumerate_sections + ebpf_api_elf_disassemble_section + ebpf_api_elf_verify_section + ebpf_api_elf_free + ebpf_api_pin_object + ebpf_api_unpin_object + ebpf_api_get_next_map + ebpf_api_get_pinned_map + ebpf_api_get_next_program + ebpf_api_program_query_info + ebpf_api_link_program + ebpf_api_close_handle + ebpf_api_get_pinned_map_info + ebpf_api_map_info_free + ebpf_free_string + ebpf_map_get_fd + ebpf_map_next + ebpf_map_previous + ebpf_object_close + ebpf_program_get_fd + ebpf_program_load + ebpf_program_next + ebpf_program_previous diff --git a/external/libbpf b/external/libbpf new file mode 160000 index 000000000..90405ffa9 --- /dev/null +++ b/external/libbpf @@ -0,0 +1 @@ +Subproject commit 90405ffa91abbdbfd9f56b70adbfa0be6cc0fe3f diff --git a/include/linux/bpf.h b/include/linux/bpf.h new file mode 100644 index 000000000..f94b9498d --- /dev/null +++ b/include/linux/bpf.h @@ -0,0 +1,47 @@ +// Copyright (c) Microsoft Corporation +// // SPDX-License-Identifier: MIT +#pragma once + +// This file is needed since bpf.h includes it. +// It should be thought of as platform/bpf.h not Linux per se. + +#include "ebpf_program_types.h" +#include "ebpf_api.h" +#define LIBBPF_API +#include "libbpf_common.h" +#undef LIBBPF_DEPRECATED +#define LIBBPF_DEPRECATED(x) + +typedef uint8_t __u8; +typedef uint32_t __u32; +typedef uint64_t __u64; +typedef uint32_t pid_t; + +#define bpf_map _ebpf_map +#define bpf_map_type _ebpf_map_type +#define bpf_object _ebpf_object +#define bpf_program _ebpf_program +#define bpf_prog_info _ebpf_program_info +#define BPF_MAP_TYPE_ARRAY EBPF_MAP_TYPE_ARRAY + +enum bpf_prog_type +{ + BPF_PROG_TYPE_UNKNOWN, + BPF_PROG_TYPE_XDP, +}; + +enum bpf_attach_type +{ + BPF_ATTACH_TYPE_UNKNOWN, + BPF_ATTACH_TYPE_XDP, +}; + +enum bpf_func_id +{ + BPF_FUNC_ID_UNKNOWN +}; + +enum bpf_stats_type +{ + BPF_STATS_TYPE_UNKNOWN +}; diff --git a/libs/api/api.vcxproj b/libs/api/api.vcxproj index e25cdd4cc..ee6cb70bb 100644 --- a/libs/api/api.vcxproj +++ b/libs/api/api.vcxproj @@ -97,6 +97,7 @@ pch.h stdcpplatest MultiThreadedDebug + $(SolutionDir)external\libbpf\src;$(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;$(SolutionDir)external\ebpf-verifier\external\elfio;$(OutDir);%(AdditionalIncludeDirectories) Windows @@ -115,6 +116,7 @@ Use pch.h stdcpplatest + $(SolutionDir)external\libbpf\src; Windows @@ -132,7 +134,7 @@ true NotUsing pch.h - $(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;$(SolutionDir)external\ebpf-verifier\external\elfio;$(OutDir);%(AdditionalIncludeDirectories) + $(SolutionDir)external\libbpf\src;$(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;$(SolutionDir)external\ebpf-verifier\external\elfio;$(OutDir);%(AdditionalIncludeDirectories) stdcpplatest MultiThreadedDebug true @@ -154,7 +156,7 @@ true NotUsing pch.h - $(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;$(SolutionDir)external\ebpf-verifier\external\elfio;$(OutDir);%(AdditionalIncludeDirectories) + $(SolutionDir)external\libbpf\src;$(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;$(SolutionDir)external\ebpf-verifier\external\elfio;$(OutDir);%(AdditionalIncludeDirectories) stdcpplatest true @@ -171,6 +173,9 @@ + + + @@ -201,4 +206,4 @@ - + \ No newline at end of file diff --git a/libs/api/api.vcxproj.filters b/libs/api/api.vcxproj.filters index 544a54720..ba57c7f12 100644 --- a/libs/api/api.vcxproj.filters +++ b/libs/api/api.vcxproj.filters @@ -25,6 +25,15 @@ Source Files + + Source Files + + + Source Files + + + Source Files + diff --git a/libs/api/api_internal.h b/libs/api/api_internal.h index bfe52eec4..55a17ca51 100644 --- a/libs/api/api_internal.h +++ b/libs/api/api_internal.h @@ -29,7 +29,7 @@ typedef struct _ebpf_map char* name; ebpf_handle_t map_handle; fd_t map_fd; - ebpf_map_definition_t map_defintion; + ebpf_map_definition_t map_definition; char* pin_path; bool pinned; } ebpf_map_t; diff --git a/libs/api/ebpf_api.cpp b/libs/api/ebpf_api.cpp index 84cd15a54..3198a2eba 100644 --- a/libs/api/ebpf_api.cpp +++ b/libs/api/ebpf_api.cpp @@ -3,6 +3,7 @@ #include "pch.h" +#include #include #include "api_common.hpp" #include "api_internal.h" @@ -677,7 +678,7 @@ clean_up_ebpf_program(_In_ _Post_invalid_ ebpf_program_t* program) _ebpf_programs.erase(program->fd); } if (program->handle != ebpf_handle_invalid) { - CloseHandle(program->handle); + Platform::CloseHandle(program->handle); } free(program->byte_code); free(program->program_name); @@ -702,7 +703,7 @@ clean_up_ebpf_map(_In_ _Post_invalid_ ebpf_map_t* map) _ebpf_maps.erase(map->map_fd); } if (map->map_handle != ebpf_handle_invalid) { - CloseHandle(map->map_handle); + Platform::CloseHandle(map->map_handle); } free(map); @@ -758,10 +759,10 @@ _initialize_map(_Out_ ebpf_map_t* map, _In_ const ebpf_object_t* object, _In_ co map->object = object; map->map_handle = (ebpf_handle_t)map_cache.handle; map->map_fd = map_cache.ebpf_map_descriptor.original_fd; - map->map_defintion.type = (ebpf_map_type_t)map_cache.ebpf_map_descriptor.type; - map->map_defintion.key_size = map_cache.ebpf_map_descriptor.key_size; - map->map_defintion.value_size = map_cache.ebpf_map_descriptor.value_size; - map->map_defintion.max_entries = map_cache.ebpf_map_descriptor.max_entries; + map->map_definition.type = (ebpf_map_type_t)map_cache.ebpf_map_descriptor.type; + map->map_definition.key_size = map_cache.ebpf_map_descriptor.key_size; + map->map_definition.value_size = map_cache.ebpf_map_descriptor.value_size; + map->map_definition.max_entries = map_cache.ebpf_map_descriptor.max_entries; map->pinned = false; map->pin_path = nullptr; } diff --git a/libs/api/libbpf_internal.h b/libs/api/libbpf_internal.h new file mode 100644 index 000000000..9c36e5935 --- /dev/null +++ b/libs/api/libbpf_internal.h @@ -0,0 +1,14 @@ +// Copyright (c) Microsoft Corporation +// SPDX-License-Identifier: MIT +#pragma once + +#define PATH_MAX MAX_PATH +#define strdup _strdup + +static inline int +libbpf_err(int ret) +{ + if (ret < 0) + errno = -ret; + return ret; +} diff --git a/libs/api/libbpf_map.cpp b/libs/api/libbpf_map.cpp new file mode 100644 index 000000000..a0169eb79 --- /dev/null +++ b/libs/api/libbpf_map.cpp @@ -0,0 +1,184 @@ +// Copyright (c) Microsoft Corporation +// SPDX-License-Identifier: MIT + +#include "api_internal.h" +#pragma warning(push) +#pragma warning(disable : 4200) +#include "libbpf.h" +#pragma warning(pop) +#include "libbpf_internal.h" + +// This file implements APIs in LibBPF's libbpf.h and is based on code in external/libbpf/src/libbpf.c +// used under the BSD-2-Clause license , so the coding style tries to match the libbpf.c style to +// minimize diffs until libbpf becomes cross-platform capable. This is a temporary workaround for +// issue #351 until we can compile and use libbpf.c directly. + +struct bpf_map* +bpf_map__next(const struct bpf_map* previous, const struct bpf_object* object) +{ + return ebpf_map_next(previous, object); +} + +struct bpf_map* +bpf_map__prev(const struct bpf_map* next, const struct bpf_object* object) +{ + return ebpf_map_previous(next, object); +} + +int +bpf_map__unpin(struct bpf_map* map, const char* path) +{ + int err; + + if (map == NULL) { + return libbpf_err(-EINVAL); + } + + err = ebpf_api_unpin_object((const uint8_t*)path, (uint32_t)strlen(path)); + if (err) { + return libbpf_err(err); + } + + map->pinned = false; + + return 0; +} + +int +bpf_map__pin(struct bpf_map* map, const char* path) +{ + int err; + + if (map == NULL) { + return libbpf_err(-EINVAL); + } + + err = ebpf_api_pin_object(map->map_handle, (const uint8_t*)path, (uint32_t)strlen(path)); + if (err) + return libbpf_err(-err); + + map->pinned = true; + + return 0; +} + +int +bpf_object__pin_maps(struct bpf_object* obj, const char* path) +{ + struct bpf_map* map; + int err; + + if (!obj) + return libbpf_err(-ENOENT); + + bpf_object__for_each_map(map, obj) + { + char* pin_path = NULL; + char buf[PATH_MAX]; + + if (path) { + int len; + + len = snprintf(buf, PATH_MAX, "%s/%s", path, bpf_map__name(map)); + if (len < 0) { + err = -EINVAL; + goto err_unpin_maps; + } else if (len >= PATH_MAX) { + err = -ENAMETOOLONG; + goto err_unpin_maps; + } + pin_path = buf; + } else { + continue; + } + + err = bpf_map__pin(map, pin_path); + if (err) + goto err_unpin_maps; + } + + return 0; + +err_unpin_maps: + while ((map = bpf_map__prev(map, obj)) != NULL) { + bpf_map__unpin(map, NULL); + } + return libbpf_err(err); +} + +int +bpf_object__unpin_maps(struct bpf_object* obj, const char* path) +{ + struct bpf_map* map; + int err; + + if (!obj) + return libbpf_err(-ENOENT); + + bpf_object__for_each_map(map, obj) + { + char* pin_path = NULL; + char buf[PATH_MAX]; + + if (path) { + int len; + + len = snprintf(buf, PATH_MAX, "%s/%s", path, bpf_map__name(map)); + if (len < 0) + return libbpf_err(-EINVAL); + else if (len >= PATH_MAX) + return libbpf_err(-ENAMETOOLONG); + pin_path = buf; + } else { + continue; + } + + err = bpf_map__unpin(map, pin_path); + if (err) + return libbpf_err(err); + } + + return 0; +} + +const char* +bpf_map__name(const struct bpf_map* map) +{ + return map ? map->name : NULL; +} + +enum bpf_map_type +bpf_map__type(const struct bpf_map* map) +{ + return map->map_definition.type; +} + +__u32 +bpf_map__key_size(const struct bpf_map* map) +{ + return map->map_definition.key_size; +} + +__u32 +bpf_map__value_size(const struct bpf_map* map) +{ + return map->map_definition.value_size; +} + +__u32 +bpf_map__max_entries(const struct bpf_map* map) +{ + return map->map_definition.max_entries; +} + +bool +bpf_map__is_pinned(const struct bpf_map* map) +{ + return map->pinned; +} + +int +bpf_map__fd(const struct bpf_map* map) +{ + return map ? map->map_fd : libbpf_err(-EINVAL); +} diff --git a/libs/api/libbpf_object.cpp b/libs/api/libbpf_object.cpp new file mode 100644 index 000000000..d1dbb5ea8 --- /dev/null +++ b/libs/api/libbpf_object.cpp @@ -0,0 +1,58 @@ +// Copyright (c) Microsoft Corporation +// SPDX-License-Identifier: MIT + +#include "api_internal.h" +#pragma warning(push) +#pragma warning(disable : 4200) +#include "libbpf.h" +#pragma warning(pop) +#include "libbpf_internal.h" + +// This file implements APIs in LibBPF's libbpf.h and is based on code in external/libbpf/src/libbpf.c +// used under the BSD-2-Clause license , so the coding style tries to match the libbpf.c style to +// minimize diffs until libbpf becomes cross-platform capable. This is a temporary workaround for +// issue #351 until we can compile and use libbpf.c directly. + +const char* +bpf_object__name(const struct bpf_object* object) +{ + return object->file_name; +} + +int +bpf_object__pin(struct bpf_object* obj, const char* path) +{ + int err; + + err = bpf_object__pin_maps(obj, path); + if (err) + return libbpf_err(err); + + err = bpf_object__pin_programs(obj, path); + if (err) { + bpf_object__unpin_maps(obj, path); + return libbpf_err(err); + } + + return 0; +} + +void +bpf_object__close(struct bpf_object* object) +{ + ebpf_object_close(object); +} + +struct bpf_program* +bpf_object__find_program_by_name(const struct bpf_object* obj, const char* name) +{ + struct bpf_program* prog; + + bpf_object__for_each_program(prog, obj) + { + if (!strcmp(prog->program_name, name)) + return prog; + } + errno = ENOENT; + return nullptr; +} diff --git a/libs/api/libbpf_program.cpp b/libs/api/libbpf_program.cpp new file mode 100644 index 000000000..08c851e23 --- /dev/null +++ b/libs/api/libbpf_program.cpp @@ -0,0 +1,278 @@ +// Copyright (c) Microsoft Corporation +// SPDX-License-Identifier: MIT + +#include "api_internal.h" +#pragma warning(push) +#pragma warning(disable : 4200) +#include "libbpf.h" +#pragma warning(pop) +#include "libbpf_internal.h" + +// This file implements APIs in LibBPF's libbpf.h and is based on code in external/libbpf/src/libbpf.c +// used under the BSD-2-Clause license , so the coding style tries to match the libbpf.c style to +// minimize diffs until libbpf becomes cross-platform capable. This is a temporary workaround for +// issue #351 until we can compile and use libbpf.c directly. + +static const ebpf_program_type_t* +_get_ebpf_program_type(enum bpf_prog_type type) +{ + // TODO(issue #223): read this mapping from the registry + switch (type) { + case BPF_PROG_TYPE_XDP: + return &EBPF_PROGRAM_TYPE_XDP; + } + return nullptr; +} + +static const ebpf_attach_type_t* +_get_ebpf_attach_type(enum bpf_attach_type type) +{ + // TODO(issue #223): read this mapping from the registry + switch (type) { + case BPF_ATTACH_TYPE_XDP: + return &EBPF_ATTACH_TYPE_XDP; + } + return nullptr; +} + +static enum bpf_attach_type +_get_bpf_attach_type(const ebpf_attach_type_t* type) +{ + // TODO(issue #223): read this mapping from the registry + if (memcmp(type, &EBPF_ATTACH_TYPE_XDP, sizeof(*type)) == 0) { + return BPF_ATTACH_TYPE_XDP; + } + return BPF_ATTACH_TYPE_UNKNOWN; +} + +int +bpf_prog_load(const char* file_name, enum bpf_prog_type type, struct bpf_object** object, int* program_fd) +{ + const ebpf_program_type_t* program_type = _get_ebpf_program_type(type); + + if (program_type == nullptr) { + return EBPF_INVALID_ARGUMENT; + } + + const char* log_buffer; + return (int)ebpf_program_load( + file_name, program_type, nullptr, EBPF_EXECUTION_ANY, object, (fd_t*)program_fd, &log_buffer); +} + +int +bpf_program__fd(const struct bpf_program* program) +{ + return (int)ebpf_program_get_fd(program); +} + +const char* +bpf_program__name(const struct bpf_program* program) +{ + return program->program_name; +} + +const char* +bpf_program__section_name(const struct bpf_program* program) +{ + return program->section_name; +} + +size_t +bpf_program__size(const struct bpf_program* program) +{ + return program->byte_code_size; +} + +struct bpf_link* +bpf_program__attach(struct bpf_program* program) +{ + if (program == nullptr) { + return nullptr; + } + + ebpf_handle_t link_handle; + uint32_t result = ebpf_api_link_program(program->handle, program->attach_type, &link_handle); + if (result) { + errno = result; + return nullptr; + } + + // The bpf_link structure is opaque so we can use the link handle directly. + // TODO(issue #81): return pointer to ebpf_link_t. + return (struct bpf_link*)link_handle; +} + +struct bpf_link* +bpf_program__attach_xdp(struct bpf_program* program, int ifindex) +{ + if (program == nullptr) { + return nullptr; + } + + // TODO: use ifindex + UNREFERENCED_PARAMETER(ifindex); + + ebpf_handle_t link_handle; + uint32_t result = ebpf_api_link_program(program->handle, EBPF_ATTACH_TYPE_XDP, &link_handle); + if (result) { + return nullptr; + } + + // The bpf_link structure is opaque so we can use the link handle directly. + // TODO(issue #81): return pointer to ebpf_link_t. + return (struct bpf_link*)link_handle; +} + +struct bpf_program* +bpf_program__next(struct bpf_program* previous, const struct bpf_object* object) +{ + return ebpf_program_next(previous, object); +} + +struct bpf_program* +bpf_program__prev(struct bpf_program* next, const struct bpf_object* object) +{ + return ebpf_program_previous(next, object); +} + +int +bpf_program__unpin(struct bpf_program* prog, const char* path) +{ + int err; + + if (prog == NULL) { + return libbpf_err(-EINVAL); + } + + err = ebpf_api_unpin_object((const uint8_t*)path, (uint32_t)strlen(path)); + if (err) + return libbpf_err(-err); + + return 0; +} + +int +bpf_program__pin(struct bpf_program* prog, const char* path) +{ + int err; + + if (prog == NULL) { + return libbpf_err(-EINVAL); + } + + err = ebpf_api_pin_object(prog->handle, (const uint8_t*)path, (uint32_t)strlen(path)); + if (err) { + return libbpf_err(err); + } + + return 0; +} + +static char* +__bpf_program__pin_name(struct bpf_program* prog) +{ + char *name, *p; + + name = p = strdup(prog->section_name); + while ((p = strchr(p, '/')) != NULL) + *p = '_'; + + return name; +} + +int +bpf_object__pin_programs(struct bpf_object* obj, const char* path) +{ + struct bpf_program* prog; + int err; + + if (!obj) + return libbpf_err(-ENOENT); + + bpf_object__for_each_program(prog, obj) + { + char buf[PATH_MAX]; + int len; + + len = snprintf(buf, PATH_MAX, "%s/%s", path, __bpf_program__pin_name(prog)); + if (len < 0) { + err = -EINVAL; + goto err_unpin_programs; + } else if (len >= PATH_MAX) { + err = -ENAMETOOLONG; + goto err_unpin_programs; + } + + err = bpf_program__pin(prog, buf); + if (err) { + goto err_unpin_programs; + } + } + + return 0; + +err_unpin_programs: + while ((prog = bpf_program__prev(prog, obj)) != NULL) { + char buf[PATH_MAX]; + int len; + + len = snprintf(buf, PATH_MAX, "%s/%s", path, __bpf_program__pin_name(prog)); + if (len < 0) + continue; + else if (len >= PATH_MAX) + continue; + + bpf_program__unpin(prog, path); + } + return err; +} + +int +bpf_object__unpin_programs(struct bpf_object* obj, const char* path) +{ + struct bpf_program* prog; + int err; + + if (!obj) + return libbpf_err(-ENOENT); + + bpf_object__for_each_program(prog, obj) + { + char buf[PATH_MAX]; + int len; + + len = snprintf(buf, PATH_MAX, "%s/%s", path, __bpf_program__pin_name(prog)); + if (len < 0) + return libbpf_err(-EINVAL); + else if (len >= PATH_MAX) + return libbpf_err(-ENAMETOOLONG); + + err = bpf_program__unpin(prog, buf); + if (err) + return libbpf_err(err); + } + + return 0; +} + +int +bpf_link__destroy(struct bpf_link* link) +{ + // TODO(issue #81): get handle from ebpf_link_t, and + // detach before closing the handle. + ebpf_handle_t link_handle = (ebpf_handle_t)link; + uint32_t result = ebpf_api_close_handle(link_handle); + return (int)result; +} + +enum bpf_attach_type +bpf_program__get_expected_attach_type(const struct bpf_program* program) +{ + return _get_bpf_attach_type(&program->attach_type); +} + +void +bpf_program__set_expected_attach_type(struct bpf_program* program, enum bpf_attach_type type) +{ + program->attach_type = *_get_ebpf_attach_type(type); +} diff --git a/tests/end_to_end/end_to_end.cpp b/tests/end_to_end/end_to_end.cpp index 5efa2c1be..17742281a 100644 --- a/tests/end_to_end/end_to_end.cpp +++ b/tests/end_to_end/end_to_end.cpp @@ -11,6 +11,7 @@ #include "catch_wrapper.hpp" #include "common_tests.h" #include "ebpf_bind_program_data.h" +#include "ebpf_core.h" #include "ebpf_xdp_program_data.h" #include "helpers.h" #include "mock.h" @@ -161,7 +162,7 @@ droppacket_test(ebpf_execution_type_t execution_type) uint32_t result = 0; const char* error_message = NULL; - single_instance_hook_t hook(EBPF_PROGRAM_TYPE_XDP); + single_instance_hook_t hook(EBPF_PROGRAM_TYPE_XDP, EBPF_ATTACH_TYPE_XDP); program_info_provider_t xdp_program_info(EBPF_PROGRAM_TYPE_XDP); REQUIRE( @@ -231,7 +232,7 @@ divide_by_zero_test(ebpf_execution_type_t execution_type) uint32_t result = 0; const char* error_message = NULL; - single_instance_hook_t hook(EBPF_PROGRAM_TYPE_XDP); + single_instance_hook_t hook(EBPF_PROGRAM_TYPE_XDP, EBPF_ATTACH_TYPE_XDP); program_info_provider_t xdp_program_info(EBPF_PROGRAM_TYPE_XDP); REQUIRE( @@ -339,7 +340,7 @@ bindmonitor_test(ebpf_execution_type_t execution_type) error_message = nullptr, result == EBPF_SUCCESS)); - single_instance_hook_t hook(EBPF_PROGRAM_TYPE_BIND); + single_instance_hook_t hook(EBPF_PROGRAM_TYPE_BIND, EBPF_ATTACH_TYPE_BIND); REQUIRE(hook.attach(program_handle) == EBPF_SUCCESS); @@ -481,7 +482,7 @@ TEST_CASE("map_pinning_test", "[end_to_end]") error_message = nullptr, result == EBPF_SUCCESS)); - single_instance_hook_t hook(EBPF_PROGRAM_TYPE_BIND); + single_instance_hook_t hook(EBPF_PROGRAM_TYPE_BIND, EBPF_ATTACH_TYPE_BIND); std::string process_maps_name = "bindmonitor::process_maps"; std::string limit_maps_name = "bindmonitor::limits_map"; @@ -562,7 +563,7 @@ TEST_CASE("enumerate_and_query_maps", "[end_to_end]") error_message = nullptr, result == EBPF_SUCCESS)); - single_instance_hook_t hook(EBPF_PROGRAM_TYPE_BIND); + single_instance_hook_t hook(EBPF_PROGRAM_TYPE_BIND, EBPF_ATTACH_TYPE_BIND); std::string process_maps_name = "bindmonitor::process_maps"; std::string limit_maps_name = "bindmonitor::limits_map"; diff --git a/tests/end_to_end/helpers.h b/tests/end_to_end/helpers.h index 6582af9da..d96d4c9b2 100644 --- a/tests/end_to_end/helpers.h +++ b/tests/end_to_end/helpers.h @@ -23,13 +23,13 @@ typedef std::unique_ptr ebpf_memory_t; typedef class _single_instance_hook { public: - _single_instance_hook(ebpf_program_type_t program_type) + _single_instance_hook(ebpf_program_type_t program_type, ebpf_attach_type_t attach_type) : provider(nullptr), client_binding_context(nullptr), client_data(nullptr), client_dispatch_table(nullptr), link_handle(nullptr) { - ebpf_guid_create(&attach_type); ebpf_guid_create(&client_id); attach_provider_data.supported_program_type = program_type; + this->attach_type = attach_type; REQUIRE( ebpf_provider_load( &provider, @@ -120,15 +120,13 @@ static ebpf_helper_function_prototype_t _ebpf_map_helper_function_prototype[] = EBPF_RETURN_TYPE_INTEGER, {EBPF_ARGUMENT_TYPE_PTR_TO_MAP, EBPF_ARGUMENT_TYPE_PTR_TO_MAP_KEY}}}; -static ebpf_context_descriptor_t _ebpf_xdp_context_descriptor = { - sizeof(xdp_md_t), - EBPF_OFFSET_OF(xdp_md_t, data), - EBPF_OFFSET_OF(xdp_md_t, data_end), - EBPF_OFFSET_OF(xdp_md_t, data_meta)}; -static ebpf_program_info_t _ebpf_xdp_program_info = { - {"xdp", &_ebpf_xdp_context_descriptor, {0}}, - EBPF_COUNT_OF(_ebpf_map_helper_function_prototype), - _ebpf_map_helper_function_prototype}; +static ebpf_context_descriptor_t _ebpf_xdp_context_descriptor = {sizeof(xdp_md_t), + EBPF_OFFSET_OF(xdp_md_t, data), + EBPF_OFFSET_OF(xdp_md_t, data_end), + EBPF_OFFSET_OF(xdp_md_t, data_meta)}; +static ebpf_program_info_t _ebpf_xdp_program_info = {{"xdp", &_ebpf_xdp_context_descriptor, {0}}, + EBPF_COUNT_OF(_ebpf_map_helper_function_prototype), + _ebpf_map_helper_function_prototype}; static ebpf_program_data_t _ebpf_xdp_program_data = {&_ebpf_xdp_program_info, NULL}; @@ -137,10 +135,9 @@ static ebpf_extension_data_t _ebpf_xdp_program_info_provider_data = { static ebpf_context_descriptor_t _ebpf_bind_context_descriptor = { sizeof(bind_md_t), EBPF_OFFSET_OF(bind_md_t, app_id_start), EBPF_OFFSET_OF(bind_md_t, app_id_end), -1}; -static ebpf_program_info_t _ebpf_bind_program_info = { - {"bind", &_ebpf_bind_context_descriptor, {0}}, - EBPF_COUNT_OF(_ebpf_map_helper_function_prototype), - _ebpf_map_helper_function_prototype}; +static ebpf_program_info_t _ebpf_bind_program_info = {{"bind", &_ebpf_bind_context_descriptor, {0}}, + EBPF_COUNT_OF(_ebpf_map_helper_function_prototype), + _ebpf_map_helper_function_prototype}; static ebpf_program_data_t _ebpf_bind_program_data = {&_ebpf_bind_program_info, NULL}; diff --git a/tests/end_to_end/test_helper.cpp b/tests/end_to_end/test_helper.cpp new file mode 100644 index 000000000..74d99b883 --- /dev/null +++ b/tests/end_to_end/test_helper.cpp @@ -0,0 +1,67 @@ +// Copyright (c) Microsoft Corporation +// SPDX-License-Identifier: MIT +#include "catch_wrapper.hpp" +#include "ebpf_api.h" +#include "ebpf_core.h" +#include "helpers.h" +#include "mock.h" +#include "test_helper.hpp" + +BOOL +GlueCloseHandle(ebpf_handle_t hObject); + +ebpf_handle_t +GlueCreateFileW( + PCWSTR lpFileName, + DWORD dwDesiredAccess, + DWORD dwShareMode, + PSECURITY_ATTRIBUTES lpSecurityAttributes, + DWORD dwCreationDisposition, + DWORD dwFlagsAndAttributes, + ebpf_handle_t hTemplateFile); + +BOOL +GlueDeviceIoControl( + ebpf_handle_t hDevice, + DWORD dwIoControlCode, + PVOID lpInBuffer, + DWORD nInBufferSize, + LPVOID lpOutBuffer, + DWORD nOutBufferSize, + PDWORD lpBytesReturned, + OVERLAPPED* lpOverlapped); + +_test_helper_end_to_end::_test_helper_end_to_end() +{ + device_io_control_handler = GlueDeviceIoControl; + create_file_handler = GlueCreateFileW; + close_handle_handler = GlueCloseHandle; + REQUIRE(ebpf_core_initiate() == EBPF_SUCCESS); + ec_initialized = true; + REQUIRE(ebpf_api_initiate() == EBPF_SUCCESS); + api_initialized = true; +} + +_test_helper_end_to_end::~_test_helper_end_to_end() +{ + if (api_initialized) + ebpf_api_terminate(); + if (ec_initialized) + ebpf_core_terminate(); + + device_io_control_handler = nullptr; + create_file_handler = nullptr; + close_handle_handler = nullptr; +} + +_test_helper_libbpf::_test_helper_libbpf() +{ + xdp_program_info = new program_info_provider_t(EBPF_PROGRAM_TYPE_XDP); + hook = new single_instance_hook_t(EBPF_PROGRAM_TYPE_XDP, EBPF_ATTACH_TYPE_XDP); +} + +_test_helper_libbpf::~_test_helper_libbpf() +{ + delete hook; + delete xdp_program_info; +} diff --git a/tests/end_to_end/test_helper.hpp b/tests/end_to_end/test_helper.hpp index 88b64d708..c2a8a7814 100644 --- a/tests/end_to_end/test_helper.hpp +++ b/tests/end_to_end/test_helper.hpp @@ -1,60 +1,29 @@ // Copyright (c) Microsoft Corporation // SPDX-License-Identifier: MIT #pragma once -#include "ebpf_api.h" -#include "ebpf_core.h" -#include "mock.h" - -BOOL -GlueCloseHandle(ebpf_handle_t hObject); - -ebpf_handle_t -GlueCreateFileW( - PCWSTR lpFileName, - DWORD dwDesiredAccess, - DWORD dwShareMode, - PSECURITY_ATTRIBUTES lpSecurityAttributes, - DWORD dwCreationDisposition, - DWORD dwFlagsAndAttributes, - ebpf_handle_t hTemplateFile); - -BOOL -GlueDeviceIoControl( - ebpf_handle_t hDevice, - DWORD dwIoControlCode, - PVOID lpInBuffer, - DWORD nInBufferSize, - LPVOID lpOutBuffer, - DWORD nOutBufferSize, - PDWORD lpBytesReturned, - OVERLAPPED* lpOverlapped); class _test_helper_end_to_end { public: - _test_helper_end_to_end() - { - device_io_control_handler = GlueDeviceIoControl; - create_file_handler = GlueCreateFileW; - close_handle_handler = GlueCloseHandle; - REQUIRE(ebpf_core_initiate() == EBPF_SUCCESS); - ec_initialized = true; - REQUIRE(ebpf_api_initiate() == EBPF_SUCCESS); - api_initialized = true; - } - ~_test_helper_end_to_end() - { - if (api_initialized) - ebpf_api_terminate(); - if (ec_initialized) - ebpf_core_terminate(); - - device_io_control_handler = nullptr; - create_file_handler = nullptr; - close_handle_handler = nullptr; - } + _test_helper_end_to_end(); + ~_test_helper_end_to_end(); private: bool ec_initialized = false; bool api_initialized = false; }; + +class _program_info_provider; +class _single_instance_hook; + +class _test_helper_libbpf +{ + public: + _test_helper_libbpf(); + ~_test_helper_libbpf(); + + private: + _test_helper_end_to_end test_helper_end_to_end; + _program_info_provider* xdp_program_info; + _single_instance_hook* hook; +}; diff --git a/tests/unit/libbpf_test.cpp b/tests/unit/libbpf_test.cpp new file mode 100644 index 000000000..de4f7bf22 --- /dev/null +++ b/tests/unit/libbpf_test.cpp @@ -0,0 +1,251 @@ +// Copyright (c) Microsoft Corporation +// SPDX-License-Identifier: MIT +#include "catch_wrapper.hpp" +#pragma warning(push) +#pragma warning(disable : 4200) +#include "libbpf.h" +#pragma warning(pop) +#include "test_helper.hpp" + +// libbpf.h uses enum types and generates the +// following warning whenever an enum type is used below: +// "The enum type 'bpf_attach_type' is unscoped. +// Prefer 'enum class' over 'enum'" +#pragma warning(disable : 26812) + +TEST_CASE("libbpf program", "[libbpf]") +{ + _test_helper_libbpf test_helper; + + struct bpf_object* object; + int program_fd; + int result = bpf_prog_load("droppacket.o", BPF_PROG_TYPE_XDP, &object, &program_fd); + REQUIRE(result == 0); + REQUIRE(object != nullptr); + REQUIRE(program_fd != -1); + + const char* name = bpf_object__name(object); + REQUIRE(strcmp(name, "droppacket.o") == 0); + + struct bpf_program* program = bpf_object__find_program_by_name(object, "DropPacket"); + REQUIRE(program != nullptr); + + name = bpf_program__section_name(program); + REQUIRE(strcmp(name, "xdp") == 0); + + name = bpf_program__name(program); + REQUIRE(strcmp(name, "DropPacket") == 0); + + int fd2 = bpf_program__fd(program); + REQUIRE(fd2 == program_fd); + + size_t size = bpf_program__size(program); + REQUIRE(size == 192); + + REQUIRE(bpf_program__next(program, object) == nullptr); + REQUIRE(bpf_program__prev(program, object) == nullptr); + REQUIRE(bpf_program__next(nullptr, object) == program); + REQUIRE(bpf_program__prev(nullptr, object) == program); + + bpf_object__close(object); +} + +TEST_CASE("libbpf program pinning", "[libbpf]") +{ + _test_helper_libbpf test_helper; + const char* pin_path = "\\temp\\test"; + + struct bpf_object* object; + int program_fd; + int result = bpf_prog_load("droppacket.o", BPF_PROG_TYPE_XDP, &object, &program_fd); + REQUIRE(result == 0); + REQUIRE(object != nullptr); + + struct bpf_program* program = bpf_object__find_program_by_name(object, "DropPacket"); + REQUIRE(program != nullptr); + + // Try to pin the program. + result = bpf_program__pin(program, pin_path); + REQUIRE(result == 0); + + // Make sure a duplicate pin fails. + result = bpf_program__pin(program, pin_path); + REQUIRE(result != 0); + + result = bpf_program__unpin(program, pin_path); + REQUIRE(result == 0); + + // Make sure a duplicate unpin fails. + result = bpf_program__unpin(program, pin_path); + REQUIRE(result != 0); + + // Try to pin all (1) programs in the object. + result = bpf_object__pin_programs(object, pin_path); + REQUIRE(result == 0); + + // Make sure a duplicate pin fails. + result = bpf_object__pin_programs(object, pin_path); + REQUIRE(result != 0); + + result = bpf_object__unpin_programs(object, pin_path); + REQUIRE(result == 0); + + // Try to pin all programs and maps in the object. + result = bpf_object__pin(object, pin_path); + REQUIRE(result == 0); + + // Make sure a duplicate pin fails. + result = bpf_object__pin_programs(object, pin_path); + REQUIRE(result != 0); + + // There is no bpf_object__unpin API, so + // we have to unpin programs and maps separately. + result = bpf_object__unpin_programs(object, pin_path); + REQUIRE(result == 0); + + result = bpf_object__unpin_maps(object, pin_path); + REQUIRE(result == 0); + + bpf_object__close(object); +} + +TEST_CASE("libbpf program attach", "[libbpf]") +{ + _test_helper_libbpf test_helper; + + struct bpf_object* object; + int program_fd; + int result = bpf_prog_load("droppacket.o", BPF_PROG_TYPE_XDP, &object, &program_fd); + REQUIRE(result == 0); + REQUIRE(object != nullptr); + + struct bpf_program* program = bpf_object__find_program_by_name(object, "DropPacket"); + REQUIRE(program != nullptr); + + // Based on the program type, verify that the + // default attach type is set correctly. + // TODO: it is not currently set. Update this + // test once it is set correctly. + enum bpf_attach_type type = bpf_program__get_expected_attach_type(program); + REQUIRE(type == BPF_ATTACH_TYPE_UNKNOWN); + + bpf_program__set_expected_attach_type(program, BPF_ATTACH_TYPE_XDP); + + type = bpf_program__get_expected_attach_type(program); + REQUIRE(type == BPF_ATTACH_TYPE_XDP); + + bpf_link* link = bpf_program__attach(program); + REQUIRE(link != nullptr); + + result = bpf_link__destroy(link); + REQUIRE(result == 0); + + // Verify that a duplicate link destroy fails. + result = bpf_link__destroy(link); + REQUIRE(result != 0); + + bpf_object__close(object); +} + +TEST_CASE("libbpf map", "[libbpf]") +{ + _test_helper_libbpf test_helper; + + struct bpf_object* object; + int program_fd; + int result = bpf_prog_load("droppacket.o", BPF_PROG_TYPE_XDP, &object, &program_fd); + REQUIRE(result == 0); + REQUIRE(object != nullptr); + + // Get the first (and only) map. + struct bpf_map* map = bpf_map__next(nullptr, object); + REQUIRE(map != nullptr); + + // Verify that it's the only map. + REQUIRE(bpf_map__next(map, object) == nullptr); + REQUIRE(bpf_map__prev(map, object) == nullptr); + REQUIRE(bpf_map__prev(nullptr, object) == map); + + const char* name = bpf_map__name(map); + REQUIRE(name == nullptr); // droppacket.o has no map name. + REQUIRE(bpf_map__type(map) == BPF_MAP_TYPE_ARRAY); + REQUIRE(bpf_map__key_size(map) == 4); + REQUIRE(bpf_map__value_size(map) == 8); + REQUIRE(bpf_map__max_entries(map) == 1); + REQUIRE(bpf_map__fd(map) > 0); + + bpf_object__close(object); +} + +TEST_CASE("libbpf map pinning", "[libbpf]") +{ + _test_helper_libbpf test_helper; + const char* pin_path = "\\temp\\test"; + + struct bpf_object* object; + int program_fd; + int result = bpf_prog_load("droppacket.o", BPF_PROG_TYPE_XDP, &object, &program_fd); + REQUIRE(result == 0); + REQUIRE(object != nullptr); + + struct bpf_map* map = bpf_map__next(nullptr, object); + REQUIRE(map != nullptr); + + REQUIRE(bpf_map__is_pinned(map) == false); + + // Try to pin the map. + result = bpf_map__pin(map, pin_path); + REQUIRE(result == 0); + + REQUIRE(bpf_map__is_pinned(map) == true); + + // Make sure a duplicate pin fails. + result = bpf_map__pin(map, pin_path); + REQUIRE(result != 0); + + result = bpf_map__unpin(map, pin_path); + REQUIRE(result == 0); + + REQUIRE(bpf_map__is_pinned(map) == false); + + // Make sure a duplicate unpin fails. + result = bpf_map__unpin(map, pin_path); + REQUIRE(result != 0); + + // Try to pin all (1) maps in the object. + result = bpf_object__pin_maps(object, pin_path); + REQUIRE(result == 0); + + REQUIRE(bpf_map__is_pinned(map) == true); + + // Make sure a duplicate pin fails. + result = bpf_object__pin_maps(object, pin_path); + REQUIRE(result != 0); + + result = bpf_object__unpin_maps(object, pin_path); + REQUIRE(result == 0); + + REQUIRE(bpf_map__is_pinned(map) == false); + + // Try to pin all programs and maps in the object. + result = bpf_object__pin(object, pin_path); + REQUIRE(result == 0); + + REQUIRE(bpf_map__is_pinned(map) == true); + + // Make sure a duplicate pin fails. + result = bpf_object__pin_maps(object, pin_path); + REQUIRE(result != 0); + + // There is no bpf_object__unpin API, so + // we have to unpin programs and maps separately. + result = bpf_object__unpin_programs(object, pin_path); + REQUIRE(result == 0); + + result = bpf_object__unpin_maps(object, pin_path); + REQUIRE(result == 0); + + REQUIRE(bpf_map__is_pinned(map) == false); + + bpf_object__close(object); +} diff --git a/tests/unit/test.vcxproj b/tests/unit/test.vcxproj index 26bab6936..a0c8d8e19 100644 --- a/tests/unit/test.vcxproj +++ b/tests/unit/test.vcxproj @@ -95,7 +95,7 @@ WIN32;_DEBUG;_CONSOLE;%(PreprocessorDefinitions) true MultiThreadedDebug - $(SolutionDir)include;$(SolutionDir)libs\api;$(SolutionDir)libs\ebpfnetsh;$(SolutionDir)tests\libs\util;$(SolutionDir)tests\libs\common;$(OutDir);$(SolutionDir)external\ebpf-verifier\src;$(SolutionDir)libs\service;$(SolutionDir)rpc_interface;$(SolutionDir)libs\platform;$(SolutionDir)libs\platform\user;$(SolutionDir)libs\execution_context;$(SolutionDir)tests\end_to_end;%(AdditionalIncludeDirectories) + $(SolutionDir)include;$(SolutionDir)libs\api;$(SolutionDir)libs\ebpfnetsh;$(SolutionDir)tests\libs\util;$(SolutionDir)tests\libs\common;$(OutDir);$(SolutionDir)external\ebpf-verifier\src;$(SolutionDir)libs\service;$(SolutionDir)rpc_interface;$(SolutionDir)libs\platform;$(SolutionDir)libs\platform\user;$(SolutionDir)libs\execution_context;$(SolutionDir)tests\end_to_end;$(SolutionDir)external\libbpf\src;%(AdditionalIncludeDirectories) Console @@ -110,7 +110,7 @@ true WIN32;NDEBUG;_CONSOLE;%(PreprocessorDefinitions) true - $(SolutionDir)include;$(SolutionDir)libs\api;$(SolutionDir)libs\ebpfnetsh;$(SolutionDir)tests\libs\util;$(SolutionDir)tests\libs\common;$(OutDir);$(SolutionDir)external\ebpf-verifier\src;$(SolutionDir)libs\service;$(SolutionDir)rpc_interface;$(SolutionDir)libs\platform;$(SolutionDir)libs\platform\user;$(SolutionDir)libs\execution_context;$(SolutionDir)tests\end_to_end;%(AdditionalIncludeDirectories) + $(SolutionDir)include;$(SolutionDir)libs\api;$(SolutionDir)libs\ebpfnetsh;$(SolutionDir)tests\libs\util;$(SolutionDir)tests\libs\common;$(OutDir);$(SolutionDir)external\ebpf-verifier\src;$(SolutionDir)libs\service;$(SolutionDir)rpc_interface;$(SolutionDir)libs\platform;$(SolutionDir)libs\platform\user;$(SolutionDir)libs\execution_context;$(SolutionDir)tests\end_to_end;$(SolutionDir)external\libbpf\src;%(AdditionalIncludeDirectories) Console @@ -125,7 +125,7 @@ true _DEBUG;_CONSOLE;%(PreprocessorDefinitions) true - $(SolutionDir)include;$(SolutionDir)libs\api;$(SolutionDir)libs\ebpfnetsh;$(SolutionDir)tests\libs\util;$(SolutionDir)tests\libs\common;$(OutDir);$(SolutionDir)external\ebpf-verifier\src;$(SolutionDir)libs\service;$(SolutionDir)rpc_interface;$(SolutionDir)libs\platform;$(SolutionDir)libs\platform\user;$(SolutionDir)libs\execution_context;$(SolutionDir)tests\end_to_end;%(AdditionalIncludeDirectories) + $(SolutionDir)include;$(SolutionDir)libs\api;$(SolutionDir)libs\ebpfnetsh;$(SolutionDir)tests\libs\util;$(SolutionDir)tests\libs\common;$(OutDir);$(SolutionDir)external\ebpf-verifier\src;$(SolutionDir)libs\service;$(SolutionDir)rpc_interface;$(SolutionDir)libs\platform;$(SolutionDir)libs\platform\user;$(SolutionDir)libs\execution_context;$(SolutionDir)tests\end_to_end;$(SolutionDir)external\libbpf\src;%(AdditionalIncludeDirectories) true stdcpp17 MultiThreadedDebug @@ -144,7 +144,7 @@ true NDEBUG;_CONSOLE;%(PreprocessorDefinitions) true - $(SolutionDir)include;$(SolutionDir)libs\api;$(SolutionDir)libs\ebpfnetsh;$(SolutionDir)tests\libs\util;$(SolutionDir)tests\libs\common;$(OutDir);$(SolutionDir)external\ebpf-verifier\src;$(SolutionDir)libs\service;$(SolutionDir)rpc_interface;$(SolutionDir)libs\platform;$(SolutionDir)libs\platform\user;$(SolutionDir)libs\execution_context;$(SolutionDir)tests\end_to_end;%(AdditionalIncludeDirectories) + $(SolutionDir)include;$(SolutionDir)libs\api;$(SolutionDir)libs\ebpfnetsh;$(SolutionDir)tests\libs\util;$(SolutionDir)tests\libs\common;$(OutDir);$(SolutionDir)external\ebpf-verifier\src;$(SolutionDir)libs\service;$(SolutionDir)rpc_interface;$(SolutionDir)libs\platform;$(SolutionDir)libs\platform\user;$(SolutionDir)libs\execution_context;$(SolutionDir)tests\end_to_end;$(SolutionDir)external\libbpf\src;%(AdditionalIncludeDirectories) true stdcpp17 @@ -203,6 +203,8 @@ + + diff --git a/tests/unit/test.vcxproj.filters b/tests/unit/test.vcxproj.filters index c0acc4f4b..3f23ad73b 100644 --- a/tests/unit/test.vcxproj.filters +++ b/tests/unit/test.vcxproj.filters @@ -40,6 +40,12 @@ Source Files + + Source Files + + + Source Files +