Introduce filter table support for INPUT, FORWARD, and OUTPUT chains. All those chains are supported by SCHED_CLS_BPF. INPUT and FORWARD programs are attached to TC_INGRESS hook and leverage generate_inline_forward_packet_assessment() to check whether they should or not process the incoming packet. OUTPUT program is attached to TC_EGRESS hook. create_filter_table() is used to create a default filter table (statically stored in filter_table_replace_blob). This table doesn't contain any rule and defaults to ACCEPTing packets on each chain. Co-developed-by: Dmitrii Banshchikov <me@xxxxxxxxxxxxx> Signed-off-by: Dmitrii Banshchikov <me@xxxxxxxxxxxxx> Signed-off-by: Quentin Deslandes <qde@xxxxxxxx> --- net/bpfilter/Makefile | 1 + net/bpfilter/context.c | 3 +- net/bpfilter/filter-table.c | 344 ++++++++++++++++++ net/bpfilter/filter-table.h | 18 + .../testing/selftests/bpf/bpfilter/.gitignore | 1 + tools/testing/selftests/bpf/bpfilter/Makefile | 3 + .../selftests/bpf/bpfilter/bpfilter_util.h | 27 ++ .../selftests/bpf/bpfilter/test_codegen.c | 338 +++++++++++++++++ 8 files changed, 734 insertions(+), 1 deletion(-) create mode 100644 net/bpfilter/filter-table.c create mode 100644 net/bpfilter/filter-table.h create mode 100644 tools/testing/selftests/bpf/bpfilter/test_codegen.c diff --git a/net/bpfilter/Makefile b/net/bpfilter/Makefile index 4a78a665b3f1..7def305f0af3 100644 --- a/net/bpfilter/Makefile +++ b/net/bpfilter/Makefile @@ -15,6 +15,7 @@ bpfilter_umh-objs := main.o logger.o map-common.o bpfilter_umh-objs += context.o codegen.o bpfilter_umh-objs += match.o xt_udp.o target.o rule.o table.o bpfilter_umh-objs += sockopt.o +bpfilter_umh-objs += filter-table.o bpfilter_umh-userldlibs := $(LIBBPF_A) -lelf -lz userccflags += -I $(srctree)/tools/include/ -I $(srctree)/tools/include/uapi diff --git a/net/bpfilter/context.c b/net/bpfilter/context.c index 81c9751a2a2d..6bb0362a79c8 100644 --- a/net/bpfilter/context.c +++ b/net/bpfilter/context.c @@ -13,6 +13,7 @@ #include <string.h> +#include "filter-table.h" #include "logger.h" #include "map-common.h" #include "match.h" @@ -73,7 +74,7 @@ static int init_target_ops_map(struct context *ctx) return 0; } -static const struct table_ops *table_ops[] = {}; +static const struct table_ops *table_ops[] = { &filter_table_ops }; static int init_table_ops_map(struct context *ctx) { diff --git a/net/bpfilter/filter-table.c b/net/bpfilter/filter-table.c new file mode 100644 index 000000000000..452a6d5b2fd0 --- /dev/null +++ b/net/bpfilter/filter-table.c @@ -0,0 +1,344 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Copyright (c) 2021 Telegram FZ-LLC + * Copyright (c) 2022 Meta Platforms, Inc. and affiliates. + */ + +#define _GNU_SOURCE + +#include "filter-table.h" + +#include "../../include/uapi/linux/bpfilter.h" + +#include <linux/kernel.h> +#include <linux/err.h> + +#include <errno.h> +#include <stdlib.h> +#include <unistd.h> + +#include <bpf/bpf.h> + +#include "codegen.h" +#include "context.h" +#include "logger.h" +#include "msgfmt.h" +#include "rule.h" +#include "sockopt.h" + +struct filter_table_context { + struct shared_codegen shared; + struct codegen local_in; + struct codegen forward; + struct codegen local_out; +}; + +static struct table *filter_table_create(struct context *ctx, + const struct bpfilter_ipt_replace *ipt_replace) +{ + struct table *t = create_table(ctx, ipt_replace); + + if (!IS_ERR_OR_NULL(t)) + t->table_ops = &filter_table_ops; + + return t; +} + +static int filter_table_codegen(struct context *ctx, struct table *table) +{ + struct filter_table_context *table_ctx; + int r; + + BUG_ON(table->table_ops != &filter_table_ops); + + if (table->ctx) { + BFLOG_ERR("filter table has no context"); + return -EINVAL; + } + + table_ctx = calloc(1, sizeof(*table_ctx)); + if (!table_ctx) { + BFLOG_ERR("out of memory"); + return -ENOMEM; + } + + create_shared_codegen(&table_ctx->shared); + + // INPUT chain + r = create_codegen(&table_ctx->local_in, BPF_PROG_TYPE_SCHED_CLS); + if (r) { + BFLOG_ERR("TC codegen for INPUT chain creation failed"); + goto err_free_table_ctx; + } + + table_ctx->local_in.ctx = ctx; + table_ctx->local_in.shared_codegen = &table_ctx->shared; + table_ctx->local_in.iptables_hook = BPFILTER_INET_HOOK_LOCAL_IN; + table_ctx->local_in.bpf_tc_hook = BPF_TC_INGRESS; + + r = try_codegen(&table_ctx->local_in, table); + if (r) { + BFLOG_ERR("failed to generate LOCAL_IN code"); + goto err_free_local_in; + } + + // FORWARD chain + r = create_codegen(&table_ctx->forward, BPF_PROG_TYPE_SCHED_CLS); + if (r) { + BFLOG_ERR("TC codegen for FORWARD chain create failed"); + goto err_free_local_in; + } + + table_ctx->forward.ctx = ctx; + table_ctx->forward.shared_codegen = &table_ctx->shared; + table_ctx->forward.iptables_hook = BPFILTER_INET_HOOK_FORWARD; + table_ctx->forward.bpf_tc_hook = BPF_TC_INGRESS; + + r = try_codegen(&table_ctx->forward, table); + if (r) { + BFLOG_ERR("failed to generate LOCAL_FORWARD code"); + goto err_free_local_fwd; + } + + // OUTPUT chain + r = create_codegen(&table_ctx->local_out, BPF_PROG_TYPE_SCHED_CLS); + if (r) { + BFLOG_ERR("TC codegen for OUTPUT chain creation failed"); + goto err_free_local_fwd; + } + + table_ctx->local_out.ctx = ctx; + table_ctx->local_out.shared_codegen = &table_ctx->shared; + table_ctx->local_out.iptables_hook = BPFILTER_INET_HOOK_LOCAL_OUT; + table_ctx->local_out.bpf_tc_hook = BPF_TC_EGRESS; + + r = try_codegen(&table_ctx->local_out, table); + if (r) { + BFLOG_ERR("failed to generate LOCAL_OUT code"); + goto err_free_local_out; + } + + table->ctx = table_ctx; + + return 0; + +err_free_local_out: + free_codegen(&table_ctx->local_out); +err_free_local_fwd: + free_codegen(&table_ctx->forward); +err_free_local_in: + free_codegen(&table_ctx->local_in); +err_free_table_ctx: + free(table_ctx); + + return r; +} + +static int filter_table_install(struct context *ctx, struct table *table) +{ + struct filter_table_context *table_ctx; + int r; + + if (!table->ctx) + return -EINVAL; + + table_ctx = (struct filter_table_context *)table->ctx; + + r = table_ctx->local_in.codegen_ops->load_img(&table_ctx->local_in); + if (r < 0) { + BFLOG_ERR("failed to load chain INPUT in table filter: %s", + table_ctx->local_in.log_buf); + return r; + } + + r = table_ctx->forward.codegen_ops->load_img(&table_ctx->forward); + if (r < 0) { + BFLOG_ERR("failed to load chain FORWARD in table filter: %s", + table_ctx->forward.log_buf); + goto err_unload_local_in; + } + + r = table_ctx->local_out.codegen_ops->load_img(&table_ctx->local_out); + if (r < 0) { + BFLOG_ERR("failed to load chain OUTPUT in table filter: %s", + table_ctx->local_out.log_buf); + goto err_unload_forward; + } + + BFLOG_DBG("installed filter table"); + + return 0; + +err_unload_forward: + table_ctx->forward.codegen_ops->unload_img(&table_ctx->forward); +err_unload_local_in: + table_ctx->local_in.codegen_ops->unload_img(&table_ctx->local_in); + + return r; +} + +static void filter_table_uninstall(struct context *ctx, struct table *table) +{ + struct filter_table_context *table_ctx; + + BUG_ON(!table->ctx); + + table_ctx = (struct filter_table_context *)table->ctx; + + table_ctx->local_in.codegen_ops->unload_img(&table_ctx->local_in); + table_ctx->forward.codegen_ops->unload_img(&table_ctx->forward); + table_ctx->local_out.codegen_ops->unload_img(&table_ctx->local_out); +} + +static void filter_table_free(struct table *table) +{ + if (table->ctx) { + struct filter_table_context *table_ctx; + + table_ctx = (struct filter_table_context *)table->ctx; + + free_codegen(&table_ctx->local_in); + free_codegen(&table_ctx->forward); + free_codegen(&table_ctx->local_out); + free(table_ctx); + } + + free_table(table); +} + +static void filter_table_update_counters(struct table *table) +{ + int r; + struct rule *rule; + struct filter_table_context *ctx = table->ctx; + struct shared_codegen *shared = &ctx->shared; + int map_fd = shared->maps_fd[CODEGEN_MAP_COUNTERS]; + + for (uint32_t i = 0; i < table->num_rules; ++i) { + rule = &table->rules[i]; + + r = bpf_map_lookup_elem(map_fd, &rule->index, + (void *)&rule->ipt_entry->counters); + if (r < 0) { + BFLOG_DBG("couldn't fetch counter for rule at %p", + rule); + } + } +} + +const struct table_ops filter_table_ops = { + .name = "filter", + .create = filter_table_create, + .codegen = filter_table_codegen, + .install = filter_table_install, + .uninstall = filter_table_uninstall, + .free = filter_table_free, + .update_counters = filter_table_update_counters +}; + +static uint8_t filter_table_replace_blob[] = { + 0x66, 0x69, 0x6c, 0x74, 0x65, 0x72, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x0e, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, + 0x78, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x98, 0x00, 0x00, 0x00, + 0x30, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x98, 0x00, 0x00, 0x00, 0x30, 0x01, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, + 0x10, 0x32, 0x40, 0x36, 0x43, 0x56, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x70, 0x00, 0x98, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x28, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0xfe, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x70, 0x00, 0x98, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x28, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0xfe, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x70, 0x00, 0x98, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x28, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0xfe, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x70, 0x00, 0xb0, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x40, 0x00, 0x45, 0x52, 0x52, 0x4f, 0x52, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x45, 0x52, 0x52, 0x4f, 0x52, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 +}; + +int create_filter_table(struct context *ctx) +{ + struct mbox_request req; + + req.addr = (__u64)filter_table_replace_blob; + req.len = ARRAY_SIZE(filter_table_replace_blob); + req.is_set = 1; + req.cmd = BPFILTER_IPT_SO_SET_REPLACE; + req.pid = getpid(); + + return handle_sockopt_request(ctx, &req); +} diff --git a/net/bpfilter/filter-table.h b/net/bpfilter/filter-table.h new file mode 100644 index 000000000000..7d5a8464456f --- /dev/null +++ b/net/bpfilter/filter-table.h @@ -0,0 +1,18 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* + * Copyright (c) 2021 Telegram FZ-LLC + * Copyright (c) 2022 Meta Platforms, Inc. and affiliates. + */ + +#ifndef NET_BPFILTER_FILTER_TABLE_H +#define NET_BPFILTER_FILTER_TABLE_H + +#include "table.h" + +struct context; + +extern const struct table_ops filter_table_ops; + +int create_filter_table(struct context *ctx); + +#endif // NET_BPFILTER_FILTER_TABLE_H diff --git a/tools/testing/selftests/bpf/bpfilter/.gitignore b/tools/testing/selftests/bpf/bpfilter/.gitignore index a934ddef58d2..926cbb0cfb59 100644 --- a/tools/testing/selftests/bpf/bpfilter/.gitignore +++ b/tools/testing/selftests/bpf/bpfilter/.gitignore @@ -5,3 +5,4 @@ test_match test_xt_udp test_target test_rule +test_codegen diff --git a/tools/testing/selftests/bpf/bpfilter/Makefile b/tools/testing/selftests/bpf/bpfilter/Makefile index 53634699d427..9eb558dfc99a 100644 --- a/tools/testing/selftests/bpf/bpfilter/Makefile +++ b/tools/testing/selftests/bpf/bpfilter/Makefile @@ -15,6 +15,7 @@ TEST_GEN_PROGS += test_match TEST_GEN_PROGS += test_xt_udp TEST_GEN_PROGS += test_target TEST_GEN_PROGS += test_rule +TEST_GEN_PROGS += test_codegen KSFT_KHDR_INSTALL := 1 @@ -46,9 +47,11 @@ BPFILTER_COMMON_SRCS := $(BPFILTER_MAP_SRCS) $(BPFILTER_CODEGEN_SRCS) BPFILTER_COMMON_SRCS += $(BPFILTERSRCDIR)/context.c $(BPFILTERSRCDIR)/logger.c BPFILTER_COMMON_SRCS += $(BPFILTER_MATCH_SRCS) $(BPFILTER_TARGET_SRCS) BPFILTER_COMMON_SRCS += $(BPFILTER_RULE_SRCS) $(BPFILTERSRCDIR)/table.c +BPFILTER_COMMON_SRCS += $(BPFILTERSRCDIR)/filter-table.c $(BPFILTERSRCDIR)/sockopt.c $(OUTPUT)/test_map: test_map.c $(BPFILTER_MAP_SRCS) $(OUTPUT)/test_match: test_match.c $(BPFILTER_COMMON_SRCS) $(OUTPUT)/test_xt_udp: test_xt_udp.c $(BPFILTER_COMMON_SRCS) $(OUTPUT)/test_target: test_target.c $(BPFILTER_COMMON_SRCS) $(OUTPUT)/test_rule: test_rule.c $(BPFILTER_COMMON_SRCS) +$(OUTPUT)/test_codegen: test_codegen.c $(BPFILTER_COMMON_SRCS) diff --git a/tools/testing/selftests/bpf/bpfilter/bpfilter_util.h b/tools/testing/selftests/bpf/bpfilter/bpfilter_util.h index 8dd7911fa06f..846b50bdab07 100644 --- a/tools/testing/selftests/bpf/bpfilter/bpfilter_util.h +++ b/tools/testing/selftests/bpf/bpfilter/bpfilter_util.h @@ -3,12 +3,39 @@ #ifndef BPFILTER_UTIL_H #define BPFILTER_UTIL_H +#include <linux/bpf.h> #include <linux/netfilter/x_tables.h> #include <linux/netfilter_ipv4/ip_tables.h> #include <stdio.h> #include <stdint.h> #include <string.h> +#include <sys/syscall.h> +#include <unistd.h> + +static inline int sys_bpf(int cmd, union bpf_attr *attr, unsigned int size) +{ + return syscall(SYS_bpf, cmd, attr, size); +} + +static inline int bpf_prog_test_run(int fd, const void *data, + unsigned int data_size, uint32_t *retval) +{ + union bpf_attr attr = {}; + int r; + + attr.test.prog_fd = fd; + attr.test.data_in = (uintptr_t)data; + attr.test.data_size_in = data_size; + attr.test.repeat = 1000000; + + r = sys_bpf(BPF_PROG_TEST_RUN, &attr, sizeof(attr)); + + if (retval) + *retval = attr.test.retval; + + return r; +} static inline void init_entry_match(struct xt_entry_match *match, uint16_t size, uint8_t revision, diff --git a/tools/testing/selftests/bpf/bpfilter/test_codegen.c b/tools/testing/selftests/bpf/bpfilter/test_codegen.c new file mode 100644 index 000000000000..9f11b7b8a126 --- /dev/null +++ b/tools/testing/selftests/bpf/bpfilter/test_codegen.c @@ -0,0 +1,338 @@ +// SPDX-License-Identifier: GPL-2.0 + +#define _GNU_SOURCE + +#include <linux/bpf.h> +#include <linux/bpfilter.h> + +#include <linux/err.h> +#include <linux/pkt_cls.h> + +#include "../../kselftest_harness.h" + +#include <stdint.h> + +#include "codegen.h" +#include "context.h" +#include "filter-table.h" +#include "logger.h" +#include "table.h" + +#include "bpfilter_util.h" + +FIXTURE(test_codegen) +{ + struct context ctx; + struct shared_codegen shared_codegen; + struct codegen codegen; + struct table *table; + int prog_fd; + uint32_t retval; + union bpf_attr attr; +}; + +FIXTURE_VARIANT(test_codegen) +{ + const struct bpfilter_ipt_replace *replace_blob; + size_t replace_blob_size; + const uint8_t *packet; + size_t packet_size; + enum bpf_prog_type prog_type; + int hook; + int expected_retval; +}; + +/* + * Generated by iptables-save v1.8.2 on Sat May 8 05:22:41 2021 + * *filter + * :INPUT ACCEPT [0:0] + * :FORWARD ACCEPT [0:0] + * :OUTPUT ACCEPT [0:0] + * -A INPUT -s 1.1.1.1/32 -d 2.2.2.2/32 -j DROP + * -A INPUT -s 2.2.0.0/16 -d 3.0.0.0/8 -j DROP + * -A INPUT -p udp -m udp --sport 100 --dport 500 -j DROP + * COMMIT + */ + +static const uint8_t user_defined_chain_blob[] = { + 0x66, 0x69, 0x6c, 0x74, 0x65, 0x72, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x0e, 0x00, 0x00, 0x00, 0x07, 0x00, 0x00, 0x00, + 0x70, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x90, 0x02, 0x00, 0x00, + 0x28, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0xf8, 0x01, 0x00, 0x00, + 0x90, 0x02, 0x00, 0x00, 0x28, 0x03, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, + 0xe0, 0x26, 0x99, 0xca, 0x67, 0x55, 0x00, 0x00, + 0x01, 0x01, 0x01, 0x01, 0x02, 0x02, 0x02, 0x02, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x70, 0x00, 0x98, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x28, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, + 0x02, 0x02, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, + 0xff, 0xff, 0x00, 0x00, 0xff, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x70, 0x00, 0x98, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x28, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x11, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0xa0, 0x00, 0xc8, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x30, 0x00, 0x75, 0x64, 0x70, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x64, 0x00, 0x64, 0x00, 0xf4, 0x01, 0xf4, 0x01, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x28, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x70, 0x00, 0x98, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x28, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0xfe, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x70, 0x00, 0x98, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x28, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0xfe, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x70, 0x00, 0x98, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x28, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0xfe, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x70, 0x00, 0xb0, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x40, 0x00, 0x45, 0x52, 0x52, 0x4f, 0x52, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x45, 0x52, 0x52, 0x4f, 0x52, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 +}; + +// Generated by scapy +// Ether(src='00:11:22:33:44:55',dst='66:77:88:99:aa:bb')/IP(src='1.1.1.1',dst='2.2.2.2')/UDP(sport=100,dport=200) +static const uint8_t udp_packet_1[] = { + 0x66, 0x77, 0x88, 0x99, 0xaa, 0xbb, 0x00, 0x11, + 0x22, 0x33, 0x44, 0x55, 0x08, 0x00, 0x45, 0x00, + 0x00, 0x1c, 0x00, 0x01, 0x00, 0x00, 0x40, 0x11, + 0x74, 0xcb, 0x01, 0x01, 0x01, 0x01, 0x02, 0x02, + 0x02, 0x02, 0x00, 0x64, 0x00, 0xc8, 0x00, 0x08, + 0xf8, 0xac, +}; + +// Ether(src='00:11:22:33:44:55',dst='66:77:88:99:aa:bb')/IP(src='2.2.2.2',dst='3.1.4.1')/UDP(sport=100,dport=200) +static const uint8_t udp_packet_2[] = { + 0x66, 0x77, 0x88, 0x99, 0xaa, 0xbb, 0x00, 0x11, + 0x22, 0x33, 0x44, 0x55, 0x08, 0x00, 0x45, 0x00, + 0x00, 0x1c, 0x00, 0x01, 0x00, 0x00, 0x40, 0x11, + 0x6f, 0xcb, 0x02, 0x02, 0x02, 0x02, 0x03, 0x01, + 0x04, 0x01, 0x00, 0x64, 0x00, 0xc8, 0x00, 0x08, + 0xf3, 0xac, +}; + +// Ether(src='00:11:22:33:44:55',dst='66:77:88:99:aa:bb')/IP(src='2.7.1.8',dst='3.1.4.1')/UDP(sport=100,dport=500) +static const uint8_t udp_packet_3[] = { + 0x66, 0x77, 0x88, 0x99, 0xaa, 0xbb, 0x00, 0x11, + 0x22, 0x33, 0x44, 0x55, 0x08, 0x00, 0x45, 0x00, + 0x00, 0x1c, 0x00, 0x01, 0x00, 0x00, 0x40, 0x11, + 0x70, 0xc0, 0x02, 0x07, 0x01, 0x08, 0x03, 0x01, + 0x04, 0x01, 0x00, 0x64, 0x01, 0xf4, 0x00, 0x08, + 0xf3, 0x75, +}; + +// Ether(src='00:11:22:33:44:55',dst='66:77:88:99:aa:bb')/IP(src='5.5.5.5',dst='5.5.5.5')/UDP(sport=300,dport=300) +static const uint8_t udp_packet_4[] = { + 0x66, 0x77, 0x88, 0x99, 0xaa, 0xbb, 0x00, 0x11, + 0x22, 0x33, 0x44, 0x55, 0x08, 0x00, 0x45, 0x00, + 0x00, 0x1c, 0x00, 0x01, 0x00, 0x00, 0x40, 0x11, + 0x66, 0xbd, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, + 0x05, 0x05, 0x01, 0x2c, 0x01, 0x2c, 0x00, 0x08, + 0xe9, 0x72, +}; + +FIXTURE_VARIANT_ADD(test_codegen, drop_by_ip_tc) { + .replace_blob = (const struct bpfilter_ipt_replace *)user_defined_chain_blob, + .replace_blob_size = ARRAY_SIZE(user_defined_chain_blob), + .packet = udp_packet_1, + .packet_size = ARRAY_SIZE(udp_packet_1), + .prog_type = BPF_PROG_TYPE_SCHED_CLS, + .hook = BPFILTER_INET_HOOK_LOCAL_IN, + .expected_retval = TC_ACT_SHOT, +}; + +FIXTURE_VARIANT_ADD(test_codegen, drop_by_net_tc) { + .replace_blob = (const struct bpfilter_ipt_replace *)user_defined_chain_blob, + .replace_blob_size = ARRAY_SIZE(user_defined_chain_blob), + .packet = udp_packet_2, + .packet_size = ARRAY_SIZE(udp_packet_2), + .prog_type = BPF_PROG_TYPE_SCHED_CLS, + .hook = BPFILTER_INET_HOOK_LOCAL_IN, + .expected_retval = TC_ACT_SHOT, +}; + +FIXTURE_VARIANT_ADD(test_codegen, drop_by_udp_port_tc) { + .replace_blob = (const struct bpfilter_ipt_replace *)user_defined_chain_blob, + .replace_blob_size = ARRAY_SIZE(user_defined_chain_blob), + .packet = udp_packet_3, + .packet_size = ARRAY_SIZE(udp_packet_3), + .prog_type = BPF_PROG_TYPE_SCHED_CLS, + .hook = BPFILTER_INET_HOOK_LOCAL_IN, + .expected_retval = TC_ACT_SHOT, +}; + +FIXTURE_VARIANT_ADD(test_codegen, accept_tc) { + .replace_blob = (const struct bpfilter_ipt_replace *)user_defined_chain_blob, + .replace_blob_size = ARRAY_SIZE(user_defined_chain_blob), + .packet = udp_packet_4, + .packet_size = ARRAY_SIZE(udp_packet_4), + .prog_type = BPF_PROG_TYPE_SCHED_CLS, + .hook = BPFILTER_INET_HOOK_LOCAL_IN, + .expected_retval = TC_ACT_UNSPEC, +}; + +FIXTURE_SETUP(test_codegen) +{ + logger_set_file(stderr); + ASSERT_EQ(create_context(&self->ctx), 0); + + create_shared_codegen(&self->shared_codegen); + ASSERT_EQ(0, create_codegen(&self->codegen, variant->prog_type)); + + self->codegen.ctx = &self->ctx; + self->codegen.shared_codegen = &self->shared_codegen; + self->codegen.iptables_hook = variant->hook; + + self->table = filter_table_ops.create(&self->ctx, variant->replace_blob); + ASSERT_FALSE(IS_ERR_OR_NULL(self->table)); + + ASSERT_EQ(0, try_codegen(&self->codegen, self->table)); + + self->prog_fd = load_img(&self->codegen); + ASSERT_GT(self->prog_fd, -1) + TH_LOG("load_img(): '%s': %s", STRERR(self->prog_fd), + self->codegen.log_buf); +}; + +FIXTURE_TEARDOWN(test_codegen) +{ + filter_table_ops.free(self->table); + unload_img(&self->codegen); + free_codegen(&self->codegen); + free_context(&self->ctx); + if (self->prog_fd > -1) + close(self->prog_fd); +}; + +TEST_F(test_codegen, test_run) +{ + EXPECT_EQ(0, bpf_prog_test_run(self->prog_fd, variant->packet, + variant->packet_size, &self->retval)) + TH_LOG("cannot bpf_prog_test_run(): '%s'", STRERR(errno)); + EXPECT_EQ(self->retval, variant->expected_retval) + TH_LOG("expected: %d, actual: %d\n", variant->expected_retval, + self->retval); +} + +TEST_HARNESS_MAIN -- 2.38.1