From 55b951c01d88f3f491ffac1b62150e858595462d Mon Sep 17 00:00:00 2001 From: Michal Stanek Date: Wed, 4 Mar 2026 17:18:44 +0100 Subject: [PATCH 1/3] Migrate to using skeleton headers for TcFilter and KprobeConnectHook Skeleton headers are already used by other non-HostIsolation related probes. Migrate the two host isolation probes so that we do not have to ship eBPF binaries separately and rely on the binaries being at a fixed path. Note that the eBPF maps used by the probes are still pinned at our bpf sysfs path. Another change in this patch is adding a prefix "el-endpo_" to TCA_BPF_NAME of the tc filter. This will help us identify our own tc filter on the system without having to store state (after restart, etc). --- .../Demos/KprobeConnectHookDemo.c | 58 ++++++++++-------- non-GPL/HostIsolation/Demos/TcLoaderDemo.c | 30 +++++----- non-GPL/HostIsolation/Lib/KprobeLoader.c | 60 ++++--------------- non-GPL/HostIsolation/Lib/KprobeLoader.h | 18 ++---- non-GPL/HostIsolation/Lib/TcLoader.c | 17 ++++-- non-GPL/HostIsolation/Lib/TcLoader.h | 3 +- 6 files changed, 77 insertions(+), 109 deletions(-) diff --git a/non-GPL/HostIsolation/Demos/KprobeConnectHookDemo.c b/non-GPL/HostIsolation/Demos/KprobeConnectHookDemo.c index 91f5cf5b..e8cad40e 100644 --- a/non-GPL/HostIsolation/Demos/KprobeConnectHookDemo.c +++ b/non-GPL/HostIsolation/Demos/KprobeConnectHookDemo.c @@ -22,27 +22,25 @@ #include #include "KprobeLoader.h" +#include "KprobeConnectHook.skel.h" // try to load and attach an eBPF kprobe program with a specified load_method -static int try_load_ebpf_kprobe(const char *ebpf_file, - enum ebpf_load_method load_method, - struct bpf_object **bpf_obj, - struct bpf_link **bpf_link) +static int try_load_ebpf_kprobe(enum ebpf_load_method load_method, + struct KprobeConnectHook_bpf **ctx_out) { - struct bpf_object *obj = NULL; - struct bpf_link *link = NULL; - int rv = 0; + struct KprobeConnectHook_bpf *ctx = NULL; + int rv = 0; - obj = ebpf_open_object_file(ebpf_file); - if (!obj) { - printf("failed to open BPF object\n"); + ctx = KprobeConnectHook_bpf__open(); + if (!ctx || !ctx->obj || libbpf_get_error(ctx->obj)) { + printf("failed to open BPF skeleton\n"); rv = -1; goto cleanup; } - printf("BPF FILE OPENED\n"); + printf("BPF SKELETON OPENED\n"); // pin allowed_IPs map when program is loaded - rv = ebpf_map_set_pin_path(obj, EBPF_ALLOWED_IPS_MAP_NAME, EBPF_ALLOWED_IPS_MAP_PATH); + rv = ebpf_map_set_pin_path(ctx->obj, EBPF_ALLOWED_IPS_MAP_NAME, EBPF_ALLOWED_IPS_MAP_PATH); if (rv) { printf("failed to init " EBPF_ALLOWED_IPS_MAP_NAME " BPF map\n"); rv = -1; @@ -51,7 +49,7 @@ static int try_load_ebpf_kprobe(const char *ebpf_file, printf("BPF ALLOWED_IPS MAP LOADED\n"); // pin allowed_pids map when program is loaded - rv = ebpf_map_set_pin_path(obj, EBPF_ALLOWED_PIDS_MAP_NAME, EBPF_ALLOWED_PIDS_MAP_PATH); + rv = ebpf_map_set_pin_path(ctx->obj, EBPF_ALLOWED_PIDS_MAP_NAME, EBPF_ALLOWED_PIDS_MAP_PATH); if (rv) { printf("failed to init " EBPF_ALLOWED_PIDS_MAP_NAME " BPF map\n"); rv = -1; @@ -71,9 +69,23 @@ static int try_load_ebpf_kprobe(const char *ebpf_file, goto cleanup; } - link = ebpf_load_and_attach_kprobe(obj, "tcp_v4_connect", load_method); - if (!link) { - printf("failed to load and attach kprobe\n"); + rv = ebpf_object_set_kernel_version(ctx->obj, load_method); + if (rv != 0) { + printf("failed to set kernel version\n"); + rv = -1; + goto cleanup; + } + + rv = KprobeConnectHook_bpf__load(ctx); + if (rv != 0) { + printf("failed to load kprobe skeleton\n"); + rv = -1; + goto cleanup; + } + + rv = KprobeConnectHook_bpf__attach(ctx); + if (rv != 0) { + printf("failed to attach kprobe skeleton\n"); rv = -1; goto cleanup; } @@ -83,19 +95,16 @@ static int try_load_ebpf_kprobe(const char *ebpf_file, cleanup: if (rv) { - bpf_object__close(obj); - bpf_link__destroy(link); + KprobeConnectHook_bpf__destroy(ctx); } else { - *bpf_obj = obj; - *bpf_link = link; + *ctx_out = ctx; } return rv; } int main(int argc, char **argv) { - struct bpf_object *obj = NULL; - struct bpf_link *link = NULL; + struct KprobeConnectHook_bpf *ctx = NULL; enum ebpf_load_method load_method = EBPF_METHOD_NO_OVERRIDE; struct rlimit rl = {}; int rv = -1; @@ -123,7 +132,7 @@ int main(int argc, char **argv) rv = -1; while (rv && load_method < EBPF_MAX_LOAD_METHODS) { printf("trying loading method %d\n", load_method); - rv = try_load_ebpf_kprobe("./KprobeConnectHook.bpf.o", load_method, &obj, &link); + rv = try_load_ebpf_kprobe(load_method, &ctx); load_method++; } @@ -137,7 +146,6 @@ int main(int argc, char **argv) cleanup: // release libbpf resources - bpf_object__close(obj); - bpf_link__destroy(link); + KprobeConnectHook_bpf__destroy(ctx); return rv; } diff --git a/non-GPL/HostIsolation/Demos/TcLoaderDemo.c b/non-GPL/HostIsolation/Demos/TcLoaderDemo.c index 4033ef4d..eb5b87df 100644 --- a/non-GPL/HostIsolation/Demos/TcLoaderDemo.c +++ b/non-GPL/HostIsolation/Demos/TcLoaderDemo.c @@ -28,17 +28,17 @@ #include #include "TcLoader.h" +#include "TcFilter.skel.h" /* UPDATE ACCORDINGLY */ #define IFNAME_TO_ATTACH_TO "ens33" -#define EBPF_OBJ_FILE_NAME "TcFilter.bpf.o" int main(int argc, char **argv) { struct netlink_ctx nl_ctx; struct bpf_program *prog = NULL; struct bpf_program *p = NULL; - struct bpf_object *obj = NULL; + struct TcFilter_bpf *ctx = NULL; struct bpf_map *map = NULL; const char *map_name = NULL; char buf[256] = {0}; @@ -95,16 +95,16 @@ int main(int argc, char **argv) DECLARE_LIBBPF_OPTS(bpf_object_open_opts, open_opts, .relaxed_maps = true, .pin_root_path = EBPF_MAP_DIRECTORY, ); - obj = bpf_object__open_file(EBPF_OBJ_FILE_NAME, &open_opts); - if (!obj || libbpf_get_error(obj)) { - fprintf(stderr, "failed to open BPF object\n"); - bpf_object__close(obj); + ctx = TcFilter_bpf__open_opts(&open_opts); + if (!ctx || !ctx->obj || libbpf_get_error(ctx->obj)) { + fprintf(stderr, "failed to open BPF skeleton\n"); + TcFilter_bpf__destroy(ctx); rv = -1; goto out; } - printf("BPF FILE OPENED\n"); + printf("BPF SKELETON OPENED\n"); - bpf_object__for_each_program(p, obj) + bpf_object__for_each_program(p, ctx->obj) { bpf_program__set_type(p, BPF_PROG_TYPE_SCHED_CLS); bpf_program__set_ifindex(p, 0); //? @@ -112,7 +112,7 @@ int main(int argc, char **argv) prog = p; } } - bpf_object__for_each_map(map, obj) + bpf_object__for_each_map(map, ctx->obj) { bpf_map__set_ifindex(map, 0); //? map_name = bpf_map__name(map); @@ -127,10 +127,10 @@ int main(int argc, char **argv) } } - rv = bpf_object__load(obj); + rv = TcFilter_bpf__load(ctx); if (rv) { fprintf(stderr, "failed to load BPF program\n"); - bpf_object__close(obj); + TcFilter_bpf__destroy(ctx); rv = -1; goto out; } @@ -139,16 +139,16 @@ int main(int argc, char **argv) prog_fd_dupd = fcntl(bpf_program__fd(prog), F_DUPFD_CLOEXEC, 1); if (prog_fd_dupd < 0) { perror("bad prog_fd_dupd"); - bpf_object__close(obj); + TcFilter_bpf__destroy(ctx); rv = -1; goto out; } - bpf_object__close(obj); - obj = NULL; + TcFilter_bpf__destroy(ctx); + ctx = NULL; /* tc filter add continued */ - if (netlink_filter_add_end(prog_fd_dupd, &nl_ctx, EBPF_OBJ_FILE_NAME) != 0) { + if (netlink_filter_add_end(prog_fd_dupd, &nl_ctx) != 0) { fprintf(stderr, "filter_add_end() failed\n"); close(prog_fd_dupd); rv = -1; diff --git a/non-GPL/HostIsolation/Lib/KprobeLoader.c b/non-GPL/HostIsolation/Lib/KprobeLoader.c index e85a8d81..eef95e86 100644 --- a/non-GPL/HostIsolation/Lib/KprobeLoader.c +++ b/non-GPL/HostIsolation/Lib/KprobeLoader.c @@ -166,28 +166,6 @@ static unsigned int get_kernel_version(enum ebpf_load_method method) return code; } -struct bpf_object *ebpf_open_object_file(const char *file_path) -{ - struct bpf_object *obj = NULL; - - if (!file_path) { - ebpf_log("error: file path is NULL\n"); - obj = NULL; - goto cleanup; - } - - obj = bpf_object__open_file(file_path, NULL); - if (!obj || libbpf_get_error(obj)) { - ebpf_log("failed to open BPF object\n"); - bpf_object__close(obj); - obj = NULL; - goto cleanup; - } - -cleanup: - return obj; -} - int ebpf_map_set_pin_path(struct bpf_object *obj, const char *map_name, const char *map_path) { struct bpf_map *map = NULL; @@ -217,14 +195,17 @@ int ebpf_map_set_pin_path(struct bpf_object *obj, const char *map_name, const ch return rv; } -struct bpf_link *ebpf_load_and_attach_kprobe(struct bpf_object *obj, - const char *program_name, - enum ebpf_load_method load_method) +int ebpf_object_set_kernel_version(struct bpf_object *obj, enum ebpf_load_method load_method) { - struct bpf_program *prog = NULL; - struct bpf_link *link = NULL; + int rv = 0; unsigned int kernel_version = 0; + if (!obj) { + ebpf_log("ebpf_object_set_kernel_version error: NULL parameter\n"); + rv = -1; + goto cleanup; + } + // Load may fail if an incorrect kernel version number was passed to the // bpf() syscall (old Linux kernels verify that, while newer kernels ignore // it). Try one of the methods of getting the kernel version, set it in @@ -234,30 +215,11 @@ struct bpf_link *ebpf_load_and_attach_kprobe(struct bpf_object *obj, ebpf_log("got kernel_version=%d according to method=%d\n", kernel_version, load_method); if (bpf_object__set_kversion(obj, kernel_version) != 0) { ebpf_log("failed to set kversion\n"); + rv = -1; + goto cleanup; } } - if (bpf_object__load(obj) < 0) { - ebpf_log("failed to load BPF program\n"); - link = NULL; - goto cleanup; - } - - prog = bpf_object__find_program_by_name(obj, program_name); - if (!prog || libbpf_get_error(prog)) { - ebpf_log("failed to find BPF program by name\n"); - link = NULL; - goto cleanup; - } - - link = bpf_program__attach(prog); - if (!link || libbpf_get_error(link)) { - ebpf_log("failed to attach BPF program\n"); - bpf_link__destroy(link); - link = NULL; - goto cleanup; - } - cleanup: - return link; + return rv; } diff --git a/non-GPL/HostIsolation/Lib/KprobeLoader.h b/non-GPL/HostIsolation/Lib/KprobeLoader.h index 94f3103d..b066dfb5 100644 --- a/non-GPL/HostIsolation/Lib/KprobeLoader.h +++ b/non-GPL/HostIsolation/Lib/KprobeLoader.h @@ -19,14 +19,6 @@ enum ebpf_load_method { EBPF_MAX_LOAD_METHODS, }; -/** - * @brief Open eBPF object file - * - * @param[in] file_path Path to the eBPF object file - * @returns eBPF object handle to be passed to other functions - */ -struct bpf_object *ebpf_open_object_file(const char *file_path); - /** * @brief Pin eBPF map by name and path * @@ -38,14 +30,12 @@ struct bpf_object *ebpf_open_object_file(const char *file_path); int ebpf_map_set_pin_path(struct bpf_object *obj, const char *map_name, const char *map_path); /** - * @brief Load and attach eBPF program to a kprobe + * @brief Set kernel version override for a specific load method * * @param[in] obj eBPF object handle - * @param[in] program_name eBPF program name - * @returns eBPF link handle to be passed to other functions + * @param[in] load_method Method used to derive kernel version + * @return Error value (0 for success) */ -struct bpf_link *ebpf_load_and_attach_kprobe(struct bpf_object *obj, - const char *program_name, - enum ebpf_load_method load_method); +int ebpf_object_set_kernel_version(struct bpf_object *obj, enum ebpf_load_method load_method); #endif diff --git a/non-GPL/HostIsolation/Lib/TcLoader.c b/non-GPL/HostIsolation/Lib/TcLoader.c index 8ac11580..3b46d07b 100644 --- a/non-GPL/HostIsolation/Lib/TcLoader.c +++ b/non-GPL/HostIsolation/Lib/TcLoader.c @@ -14,6 +14,7 @@ #include "Common.h" #include +#include #include #include #include @@ -470,25 +471,33 @@ int netlink_filter_add_begin(struct netlink_ctx *ctx, const char *ifname) return rv; } -int netlink_filter_add_end(int fd, struct netlink_ctx *ctx, const char *ebpf_obj_filename) +int netlink_filter_add_end(int fd, struct netlink_ctx *ctx) { struct nlmsghdr *nl = NULL; + struct bpf_prog_info info = {}; + unsigned int info_len = sizeof(info); char buf[128]; int rv = -1; int len = 0; - if (!ctx || !ebpf_obj_filename) { + if (!ctx) { ebpf_log("netlink_filter_add_end error: NULL parameter\n"); rv = -1; goto out; } + rv = bpf_obj_get_info_by_fd(fd, &info, &info_len); + if (rv < 0) { + ebpf_log("netlink_filter_add_end error: failed to get info by fd\n"); + goto out; + } + nl = &ctx->msg.n; memset(buf, 0, sizeof(buf)); - len = snprintf(buf, sizeof(buf), "%s:[.text]", ebpf_obj_filename); + len = snprintf(buf, sizeof(buf), "el-endpo_%s:[%u]", info.name, info.id); if (len < 0 || len >= (int)sizeof(buf)) { - ebpf_log("netlink_filter_add_end error: filename too long\n"); + ebpf_log("netlink_filter_add_end error: name too long\n"); rv = -1; goto out; } diff --git a/non-GPL/HostIsolation/Lib/TcLoader.h b/non-GPL/HostIsolation/Lib/TcLoader.h index 83647005..1ae2f6d1 100644 --- a/non-GPL/HostIsolation/Lib/TcLoader.h +++ b/non-GPL/HostIsolation/Lib/TcLoader.h @@ -77,8 +77,7 @@ int netlink_filter_add_begin(struct netlink_ctx *ctx, const char *ifname); * @param[in] fd eBPF program file descriptor * @param[in] ctx Context containing netlink state (from previous add_begin() * call) - passed by caller - * @param[in] ebpf_obj_filename eBPF object filename * @return Error value (0 for success) */ -int netlink_filter_add_end(int fd, struct netlink_ctx *ctx, const char *ebpf_obj_filename); +int netlink_filter_add_end(int fd, struct netlink_ctx *ctx); #endif From 62528f4bcfca19567b840b29a09b2fe25cf6ae44 Mon Sep 17 00:00:00 2001 From: Michal Stanek Date: Mon, 9 Mar 2026 23:32:35 +0100 Subject: [PATCH 2/3] Parameterize TcLoaderDemo interface Require the network interface name to be provided on the command line --- docs/hostisolation.md | 4 +-- non-GPL/HostIsolation/Demos/TcLoaderDemo.c | 38 +++++++++++++++++----- 2 files changed, 31 insertions(+), 11 deletions(-) diff --git a/docs/hostisolation.md b/docs/hostisolation.md index 10044025..4318794c 100644 --- a/docs/hostisolation.md +++ b/docs/hostisolation.md @@ -22,13 +22,13 @@ These binaries can be used to demo/test host isolation locally as follows: 1. Build the repository 2. Run `cd /target/ebpf` -3. Run `sudo ../../non-GPL/TcLoader/TcLoaderDemo` - packet filter is now attached to ens33 +3. Run `sudo ../../non-GPL/TcLoader/TcLoaderDemo ` - packet filter is now attached to `` (e.g. `ens33`) 4. Run `sudo ../../non-GPL/HostIsolation/KprobeConnectHook/KprobeConnectHookDemo` - connect hook is attached 5. Run `firefox` in another tab - verify that all internet access is blocked 6. Run `pgrep firefox` to get the PID of the browser 7. Run `sudo ../../non-GPL/HostIsolationMapsUtil/UpdatePidsDemo ` 8. Verify that firefox connects to any page -9. Quit KprobeConnectHook with Ctrl+C and run `sudo ../../non-GPL/TcLoader/TcLoaderDemo unload` to detach both eBPF programs +9. Quit KprobeConnectHook with Ctrl+C and run `sudo ../../non-GPL/TcLoader/TcLoaderDemo unload` to detach both eBPF programs ## Tests diff --git a/non-GPL/HostIsolation/Demos/TcLoaderDemo.c b/non-GPL/HostIsolation/Demos/TcLoaderDemo.c index eb5b87df..43250496 100644 --- a/non-GPL/HostIsolation/Demos/TcLoaderDemo.c +++ b/non-GPL/HostIsolation/Demos/TcLoaderDemo.c @@ -30,9 +30,6 @@ #include "TcLoader.h" #include "TcFilter.skel.h" -/* UPDATE ACCORDINGLY */ -#define IFNAME_TO_ATTACH_TO "ens33" - int main(int argc, char **argv) { struct netlink_ctx nl_ctx; @@ -44,34 +41,57 @@ int main(int argc, char **argv) char buf[256] = {0}; int prog_fd_dupd = 0; int rv = -1; + const char *ifname = NULL; + bool unload_only = false; memset(&nl_ctx, 0, sizeof(nl_ctx)); + if (argc < 2) { + fprintf(stderr, "Usage: %s [unload]\n", argv[0]); + return -1; + } + + ifname = argv[1]; + if (argc >= 3) { + if (!strcmp(argv[2], "unload")) { + unload_only = true; + } else { + fprintf(stderr, "Unknown argument: %s\n", argv[2]); + fprintf(stderr, "Usage: %s [unload]\n", argv[0]); + return -1; + } + } + if (argc > 3) { + fprintf(stderr, "Too many arguments\n"); + fprintf(stderr, "Usage: %s [unload]\n", argv[0]); + return -1; + } + /* do the same things as 'tc qdisc del dev clsact' */ - if (netlink_qdisc_del(IFNAME_TO_ATTACH_TO) != 0) { + if (netlink_qdisc_del(ifname) != 0) { fprintf(stderr, "failed to del qdisc\n"); } else { - printf("DELETED QDISC\n"); + printf("DELETED QDISC (%s)\n", ifname); } /* if 'unload' is passed as arg, only delete qdisc */ - if ((argc > 1) && !strcmp(argv[1], "unload")) { + if (unload_only) { rv = 0; goto out; } /* 'tc qdisc add dev clsact' */ - if (netlink_qdisc_add(IFNAME_TO_ATTACH_TO) != 0) { + if (netlink_qdisc_add(ifname) != 0) { fprintf(stderr, "failed to add qdisc\n"); rv = -1; goto out; } - printf("ADDED QDISC\n"); + printf("ADDED QDISC (%s)\n", ifname); /* 'tc filter add dev egress bpf da obj sec .text' */ /* finished when netlink_filter_add_end() is called */ - if (netlink_filter_add_begin(&nl_ctx, IFNAME_TO_ATTACH_TO) != 0) { + if (netlink_filter_add_begin(&nl_ctx, ifname) != 0) { fprintf(stderr, "filter_add_begin() failed\n"); rv = -1; goto out; From 4ec57d823835a5e2add46547cef033a787ab3ce7 Mon Sep 17 00:00:00 2001 From: Michal Stanek Date: Tue, 10 Mar 2026 21:51:49 +0100 Subject: [PATCH 3/3] clang-format --- non-GPL/HostIsolation/Demos/KprobeConnectHookDemo.c | 2 +- non-GPL/HostIsolation/Demos/TcLoaderDemo.c | 2 +- non-GPL/HostIsolation/Lib/TcLoader.c | 4 ++-- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/non-GPL/HostIsolation/Demos/KprobeConnectHookDemo.c b/non-GPL/HostIsolation/Demos/KprobeConnectHookDemo.c index e8cad40e..f61b400b 100644 --- a/non-GPL/HostIsolation/Demos/KprobeConnectHookDemo.c +++ b/non-GPL/HostIsolation/Demos/KprobeConnectHookDemo.c @@ -21,8 +21,8 @@ #include #include -#include "KprobeLoader.h" #include "KprobeConnectHook.skel.h" +#include "KprobeLoader.h" // try to load and attach an eBPF kprobe program with a specified load_method static int try_load_ebpf_kprobe(enum ebpf_load_method load_method, diff --git a/non-GPL/HostIsolation/Demos/TcLoaderDemo.c b/non-GPL/HostIsolation/Demos/TcLoaderDemo.c index 43250496..7b1c2d8a 100644 --- a/non-GPL/HostIsolation/Demos/TcLoaderDemo.c +++ b/non-GPL/HostIsolation/Demos/TcLoaderDemo.c @@ -27,8 +27,8 @@ #include #include -#include "TcLoader.h" #include "TcFilter.skel.h" +#include "TcLoader.h" int main(int argc, char **argv) { diff --git a/non-GPL/HostIsolation/Lib/TcLoader.c b/non-GPL/HostIsolation/Lib/TcLoader.c index 3b46d07b..f984440a 100644 --- a/non-GPL/HostIsolation/Lib/TcLoader.c +++ b/non-GPL/HostIsolation/Lib/TcLoader.c @@ -473,9 +473,9 @@ int netlink_filter_add_begin(struct netlink_ctx *ctx, const char *ifname) int netlink_filter_add_end(int fd, struct netlink_ctx *ctx) { - struct nlmsghdr *nl = NULL; + struct nlmsghdr *nl = NULL; struct bpf_prog_info info = {}; - unsigned int info_len = sizeof(info); + unsigned int info_len = sizeof(info); char buf[128]; int rv = -1; int len = 0;