From 825fc8d89e17a177dee0d82f96947d97855394d5 Mon Sep 17 00:00:00 2001 From: yaakov-stein Date: Thu, 5 Mar 2026 12:17:01 -0800 Subject: [PATCH 1/2] lib,daemon: add cgroup_sock_addr hooks, flavor, and BPF type constants Add BF_FLAVOR_CGROUP_SOCK_ADDR and BF_HOOK_CGROUP_SOCK_ADDR_CONNECT4/CONNECT6 to support BPF_PROG_TYPE_CGROUP_SOCK_ADDR programs. This is the foundational enum/mapping work for sock_addr filtering. All mapping tables (hook strings, flavor, prog_type, attach_type), BPF type constants, link creation, and hookopts are updated. All existing matchers are blocked on the new hooks via unsupported_hooks. Flavor ops are registered as NULL and codegen is added in a follow-up. --- src/bfcli/lexer.l | 2 +- src/bpfilter/cgen/prog/link.c | 4 +- src/bpfilter/cgen/program.c | 1 + src/libbpfilter/chain.c | 21 +++--- src/libbpfilter/flavor.c | 1 + src/libbpfilter/hook.c | 11 ++- src/libbpfilter/include/bpfilter/bpf_types.h | 3 + src/libbpfilter/include/bpfilter/flavor.h | 9 +++ src/libbpfilter/include/bpfilter/hook.h | 4 +- src/libbpfilter/matcher.c | 72 +++++++++++++++++++- tests/unit/libbpfilter/hook.c | 68 ++++++++++++++++-- 11 files changed, 174 insertions(+), 22 deletions(-) diff --git a/src/bfcli/lexer.l b/src/bfcli/lexer.l index 4380f44a..8a04987a 100644 --- a/src/bfcli/lexer.l +++ b/src/bfcli/lexer.l @@ -58,7 +58,7 @@ set { return SET; } counter { return COUNTER; } /* Hooks */ -BF_HOOK_[A-Z_]+ { BEGIN(STATE_HOOK_OPTS); yylval.sval = strdup(yytext); return HOOK; } +BF_HOOK_[A-Z0-9_]+ { BEGIN(STATE_HOOK_OPTS); yylval.sval = strdup(yytext); return HOOK; } { (\{|,) /* Ignore */ \} { BEGIN(INITIAL); } diff --git a/src/bpfilter/cgen/prog/link.c b/src/bpfilter/cgen/prog/link.c index 2cc682a9..d557c10c 100644 --- a/src/bpfilter/cgen/prog/link.c +++ b/src/bpfilter/cgen/prog/link.c @@ -73,6 +73,7 @@ int bf_link_new(struct bf_link **link, const char *name, enum bf_hook hook, fd = r; break; case BF_FLAVOR_CGROUP_SKB: + case BF_FLAVOR_CGROUP_SOCK_ADDR: cgroup_fd = open(_hookopts->cgpath, O_DIRECTORY | O_RDONLY); if (cgroup_fd < 0) { return bf_err_r(errno, "failed to open cgroup '%s'", @@ -81,7 +82,7 @@ int bf_link_new(struct bf_link **link, const char *name, enum bf_hook hook, r = bf_bpf_link_create(prog_fd, cgroup_fd, hook, 0, 0, 0); if (r < 0) - return bf_err_r(r, "failed to create cgroup_skb BPF link"); + return bf_err_r(r, "failed to create cgroup BPF link"); fd = r; break; @@ -291,6 +292,7 @@ int bf_link_update(struct bf_link *link, int prog_fd) case BF_FLAVOR_XDP: case BF_FLAVOR_TC: case BF_FLAVOR_CGROUP_SKB: + case BF_FLAVOR_CGROUP_SOCK_ADDR: r = bf_bpf_link_update(link->fd, prog_fd); break; case BF_FLAVOR_NF: diff --git a/src/bpfilter/cgen/program.c b/src/bpfilter/cgen/program.c index 9f98f64e..601da81c 100644 --- a/src/bpfilter/cgen/program.c +++ b/src/bpfilter/cgen/program.c @@ -82,6 +82,7 @@ static const struct bf_flavor_ops *bf_flavor_ops_get(enum bf_flavor flavor) [BF_FLAVOR_NF] = &bf_flavor_ops_nf, [BF_FLAVOR_XDP] = &bf_flavor_ops_xdp, [BF_FLAVOR_CGROUP_SKB] = &bf_flavor_ops_cgroup_skb, + [BF_FLAVOR_CGROUP_SOCK_ADDR] = NULL, }; static_assert_enum_mapping(flavor_ops, _BF_FLAVOR_MAX); diff --git a/src/libbpfilter/chain.c b/src/libbpfilter/chain.c index 9213febf..e650ec7d 100644 --- a/src/libbpfilter/chain.c +++ b/src/libbpfilter/chain.c @@ -73,14 +73,17 @@ int _bf_chain_check_rule(struct bf_chain *chain, struct bf_rule *rule) if (rule->log && !rule->disabled) chain->flags |= BF_FLAG(BF_CHAIN_LOG); + if (rule->log && + bf_hook_to_flavor(chain->hook) == BF_FLAVOR_CGROUP_SOCK_ADDR) { + return bf_err_r(-ENOTSUP, "logging is not supported by %s", + bf_hook_to_str(chain->hook)); + } + if (bf_rule_mark_is_set(rule) && - (chain->hook == BF_HOOK_XDP || chain->hook == BF_HOOK_NF_PRE_ROUTING || - chain->hook == BF_HOOK_NF_POST_ROUTING || - chain->hook == BF_HOOK_NF_FORWARD || - chain->hook == BF_HOOK_NF_LOCAL_IN || - chain->hook == BF_HOOK_NF_LOCAL_OUT)) { - return bf_err_r(-EINVAL, - "XDP and Netfilter chains can't set packet mark"); + bf_hook_to_flavor(chain->hook) != BF_FLAVOR_TC && + bf_hook_to_flavor(chain->hook) != BF_FLAVOR_CGROUP_SKB) { + return bf_err_r(-EINVAL, "%s chains can't set packet mark", + bf_hook_to_str(chain->hook)); } bf_list_foreach (&rule->matchers, matcher_node) { @@ -92,10 +95,6 @@ int _bf_chain_check_rule(struct bf_chain *chain, struct bf_rule *rule) !rule->disabled) chain->flags |= BF_FLAG(BF_CHAIN_STORE_NEXTHDR); - // Set matchers are compatible with all hooks. - if (bf_matcher_get_type(matcher) == BF_MATCHER_SET) - continue; - // Ensure the matcher is compatible with the chain's hook. meta = bf_matcher_get_meta(bf_matcher_get_type(matcher)); if (!meta) { diff --git a/src/libbpfilter/flavor.c b/src/libbpfilter/flavor.c index db57a57b..21a97b69 100644 --- a/src/libbpfilter/flavor.c +++ b/src/libbpfilter/flavor.c @@ -14,6 +14,7 @@ const char *bf_flavor_to_str(enum bf_flavor flavor) [BF_FLAVOR_NF] = "BF_FLAVOR_NF", [BF_FLAVOR_XDP] = "BF_FLAVOR_XDP", [BF_FLAVOR_CGROUP_SKB] = "BF_FLAVOR_CGROUP_SKB", + [BF_FLAVOR_CGROUP_SOCK_ADDR] = "BF_FLAVOR_CGROUP_SOCK_ADDR", }; static_assert_enum_mapping(flavor_str, _BF_FLAVOR_MAX); diff --git a/src/libbpfilter/hook.c b/src/libbpfilter/hook.c index 3f5b3b81..769d3cca 100644 --- a/src/libbpfilter/hook.c +++ b/src/libbpfilter/hook.c @@ -35,6 +35,8 @@ static const char *_bf_hook_strs[] = { [BF_HOOK_NF_LOCAL_OUT] = "BF_HOOK_NF_LOCAL_OUT", [BF_HOOK_NF_POST_ROUTING] = "BF_HOOK_NF_POST_ROUTING", [BF_HOOK_TC_EGRESS] = "BF_HOOK_TC_EGRESS", + [BF_HOOK_CGROUP_SOCK_ADDR_CONNECT4] = "BF_HOOK_CGROUP_SOCK_ADDR_CONNECT4", + [BF_HOOK_CGROUP_SOCK_ADDR_CONNECT6] = "BF_HOOK_CGROUP_SOCK_ADDR_CONNECT6", }; static_assert_enum_mapping(_bf_hook_strs, _BF_HOOK_MAX); @@ -73,6 +75,8 @@ enum bf_flavor bf_hook_to_flavor(enum bf_hook hook) [BF_HOOK_NF_LOCAL_OUT] = BF_FLAVOR_NF, [BF_HOOK_NF_POST_ROUTING] = BF_FLAVOR_NF, [BF_HOOK_TC_EGRESS] = BF_FLAVOR_TC, + [BF_HOOK_CGROUP_SOCK_ADDR_CONNECT4] = BF_FLAVOR_CGROUP_SOCK_ADDR, + [BF_HOOK_CGROUP_SOCK_ADDR_CONNECT6] = BF_FLAVOR_CGROUP_SOCK_ADDR, }; static_assert_enum_mapping(flavors, _BF_HOOK_MAX); @@ -93,6 +97,8 @@ enum bf_bpf_attach_type bf_hook_to_bpf_attach_type(enum bf_hook hook) [BF_HOOK_NF_LOCAL_OUT] = BF_BPF_NETFILTER, [BF_HOOK_NF_POST_ROUTING] = BF_BPF_NETFILTER, [BF_HOOK_TC_EGRESS] = BF_BPF_TCX_ENGRESS, + [BF_HOOK_CGROUP_SOCK_ADDR_CONNECT4] = BF_BPF_CGROUP_INET4_CONNECT, + [BF_HOOK_CGROUP_SOCK_ADDR_CONNECT6] = BF_BPF_CGROUP_INET6_CONNECT, }; static_assert_enum_mapping(attach_types, _BF_HOOK_MAX); @@ -113,6 +119,8 @@ enum bf_bpf_prog_type bf_hook_to_bpf_prog_type(enum bf_hook hook) [BF_HOOK_NF_LOCAL_OUT] = BF_BPF_PROG_TYPE_NETFILTER, [BF_HOOK_NF_POST_ROUTING] = BF_BPF_PROG_TYPE_NETFILTER, [BF_HOOK_TC_EGRESS] = BF_BPF_PROG_TYPE_SCHED_CLS, + [BF_HOOK_CGROUP_SOCK_ADDR_CONNECT4] = BF_BPF_PROG_TYPE_CGROUP_SOCK_ADDR, + [BF_HOOK_CGROUP_SOCK_ADDR_CONNECT6] = BF_BPF_PROG_TYPE_CGROUP_SOCK_ADDR, }; static_assert_enum_mapping(prog_types, _BF_HOOK_MAX); @@ -359,7 +367,8 @@ static struct bf_hookopts_ops .dump = _bf_hookopts_ifindex_dump}, [BF_HOOKOPTS_CGPATH] = {.name = "cgpath", .type = BF_HOOKOPTS_CGPATH, - .required_by = BF_FLAGS(BF_FLAVOR_CGROUP_SKB), + .required_by = BF_FLAGS(BF_FLAVOR_CGROUP_SKB, + BF_FLAVOR_CGROUP_SOCK_ADDR), .supported_by = 0, .parse = _bf_hookopts_cgpath_parse, .dump = _bf_hookopts_cgpath_dump}, diff --git a/src/libbpfilter/include/bpfilter/bpf_types.h b/src/libbpfilter/include/bpfilter/bpf_types.h index 31baa173..4d0b4371 100644 --- a/src/libbpfilter/include/bpfilter/bpf_types.h +++ b/src/libbpfilter/include/bpfilter/bpf_types.h @@ -28,6 +28,7 @@ enum bf_bpf_prog_type BF_BPF_PROG_TYPE_XDP = 6, BF_BPF_PROG_TYPE_SCHED_CLS = 3, BF_BPF_PROG_TYPE_CGROUP_SKB = 8, + BF_BPF_PROG_TYPE_CGROUP_SOCK_ADDR = 18, BF_BPF_PROG_TYPE_NETFILTER = 32, }; @@ -39,6 +40,8 @@ enum bf_bpf_attach_type BF_BPF_TCX_ENGRESS = 47, BF_BPF_CGROUP_INET_INGRESS = 0, BF_BPF_CGROUP_INET_EGRESS = 1, + BF_BPF_CGROUP_INET4_CONNECT = 10, + BF_BPF_CGROUP_INET6_CONNECT = 11, }; enum bf_bpf_map_type diff --git a/src/libbpfilter/include/bpfilter/flavor.h b/src/libbpfilter/include/bpfilter/flavor.h index 0605fac9..51c7440d 100644 --- a/src/libbpfilter/include/bpfilter/flavor.h +++ b/src/libbpfilter/include/bpfilter/flavor.h @@ -62,6 +62,15 @@ enum bf_flavor * - Return code: 0 to drop, 1 to accept */ BF_FLAVOR_CGROUP_SKB, + + /** + * cgroup_sock_addr BPF programs, attached to a cgroup to intercept + * socket operations (connect, bind, sendmsg, recvmsg): + * - Input: `struct bpf_sock_addr` + * - No packet data; L3/L4 protocol from socket metadata + * - Return code: 0 to drop, 1 to accept + */ + BF_FLAVOR_CGROUP_SOCK_ADDR, _BF_FLAVOR_MAX, }; diff --git a/src/libbpfilter/include/bpfilter/hook.h b/src/libbpfilter/include/bpfilter/hook.h index 0412e75e..ab31b841 100644 --- a/src/libbpfilter/include/bpfilter/hook.h +++ b/src/libbpfilter/include/bpfilter/hook.h @@ -40,6 +40,8 @@ enum bf_hook BF_HOOK_NF_LOCAL_OUT, BF_HOOK_NF_POST_ROUTING, BF_HOOK_TC_EGRESS, + BF_HOOK_CGROUP_SOCK_ADDR_CONNECT4, + BF_HOOK_CGROUP_SOCK_ADDR_CONNECT6, _BF_HOOK_MAX, }; @@ -139,7 +141,7 @@ struct bf_hookopts // XDP and TC int ifindex; - // cgroup_skb + // cgroup_skb and cgroup_sock_addr const char *cgpath; // Netfilter diff --git a/src/libbpfilter/matcher.c b/src/libbpfilter/matcher.c index dac04ee9..43376d82 100644 --- a/src/libbpfilter/matcher.c +++ b/src/libbpfilter/matcher.c @@ -839,6 +839,8 @@ static struct bf_matcher_meta _bf_matcher_metas[_BF_MATCHER_TYPE_MAX] = { [BF_MATCHER_META_IFACE] = { .layer = BF_MATCHER_NO_LAYER, + .unsupported_hooks = BF_FLAGS(BF_HOOK_CGROUP_SOCK_ADDR_CONNECT4, + BF_HOOK_CGROUP_SOCK_ADDR_CONNECT6), .ops = { BF_MATCHER_OPS(BF_MATCHER_EQ, sizeof(uint32_t), @@ -848,6 +850,8 @@ static struct bf_matcher_meta _bf_matcher_metas[_BF_MATCHER_TYPE_MAX] = { [BF_MATCHER_META_L3_PROTO] = { .layer = BF_MATCHER_NO_LAYER, + .unsupported_hooks = BF_FLAGS(BF_HOOK_CGROUP_SOCK_ADDR_CONNECT4, + BF_HOOK_CGROUP_SOCK_ADDR_CONNECT6), .ops = { BF_MATCHER_OPS(BF_MATCHER_EQ, sizeof(uint16_t), @@ -857,6 +861,8 @@ static struct bf_matcher_meta _bf_matcher_metas[_BF_MATCHER_TYPE_MAX] = { [BF_MATCHER_META_L4_PROTO] = { .layer = BF_MATCHER_NO_LAYER, + .unsupported_hooks = BF_FLAGS(BF_HOOK_CGROUP_SOCK_ADDR_CONNECT4, + BF_HOOK_CGROUP_SOCK_ADDR_CONNECT6), .ops = { BF_MATCHER_OPS(BF_MATCHER_EQ, sizeof(uint16_t), @@ -868,6 +874,8 @@ static struct bf_matcher_meta _bf_matcher_metas[_BF_MATCHER_TYPE_MAX] = { [BF_MATCHER_META_SPORT] = { .layer = BF_MATCHER_NO_LAYER, + .unsupported_hooks = BF_FLAGS(BF_HOOK_CGROUP_SOCK_ADDR_CONNECT4, + BF_HOOK_CGROUP_SOCK_ADDR_CONNECT6), .ops = { BF_MATCHER_OPS(BF_MATCHER_EQ, sizeof(uint16_t), @@ -882,6 +890,8 @@ static struct bf_matcher_meta _bf_matcher_metas[_BF_MATCHER_TYPE_MAX] = { [BF_MATCHER_META_DPORT] = { .layer = BF_MATCHER_NO_LAYER, + .unsupported_hooks = BF_FLAGS(BF_HOOK_CGROUP_SOCK_ADDR_CONNECT4, + BF_HOOK_CGROUP_SOCK_ADDR_CONNECT6), .ops = { BF_MATCHER_OPS(BF_MATCHER_EQ, sizeof(uint16_t), @@ -896,6 +906,8 @@ static struct bf_matcher_meta _bf_matcher_metas[_BF_MATCHER_TYPE_MAX] = { [BF_MATCHER_META_PROBABILITY] = { .layer = BF_MATCHER_NO_LAYER, + .unsupported_hooks = BF_FLAGS(BF_HOOK_CGROUP_SOCK_ADDR_CONNECT4, + BF_HOOK_CGROUP_SOCK_ADDR_CONNECT6), .ops = { BF_MATCHER_OPS(BF_MATCHER_EQ, sizeof(float), @@ -906,7 +918,9 @@ static struct bf_matcher_meta _bf_matcher_metas[_BF_MATCHER_TYPE_MAX] = { [BF_MATCHER_META_MARK] = { .layer = BF_MATCHER_NO_LAYER, - .unsupported_hooks = BF_FLAGS(BF_HOOK_XDP), + .unsupported_hooks = + BF_FLAGS(BF_HOOK_XDP, BF_HOOK_CGROUP_SOCK_ADDR_CONNECT4, + BF_HOOK_CGROUP_SOCK_ADDR_CONNECT6), .ops = { BF_MATCHER_OPS(BF_MATCHER_EQ, sizeof(uint32_t), @@ -922,7 +936,9 @@ static struct bf_matcher_meta _bf_matcher_metas[_BF_MATCHER_TYPE_MAX] = { BF_FLAGS(BF_HOOK_XDP, BF_HOOK_CGROUP_SKB_INGRESS, BF_HOOK_CGROUP_SKB_EGRESS, BF_HOOK_NF_FORWARD, BF_HOOK_NF_LOCAL_IN, BF_HOOK_NF_LOCAL_OUT, - BF_HOOK_NF_POST_ROUTING, BF_HOOK_NF_PRE_ROUTING), + BF_HOOK_NF_POST_ROUTING, BF_HOOK_NF_PRE_ROUTING, + BF_HOOK_CGROUP_SOCK_ADDR_CONNECT4, + BF_HOOK_CGROUP_SOCK_ADDR_CONNECT6), .ops = { BF_MATCHER_OPS(BF_MATCHER_EQ, sizeof(uint32_t), @@ -938,7 +954,9 @@ static struct bf_matcher_meta _bf_matcher_metas[_BF_MATCHER_TYPE_MAX] = { .layer = BF_MATCHER_NO_LAYER, .unsupported_hooks = BF_FLAGS( BF_HOOK_NF_FORWARD, BF_HOOK_NF_LOCAL_IN, BF_HOOK_NF_LOCAL_OUT, - BF_HOOK_NF_POST_ROUTING, BF_HOOK_NF_PRE_ROUTING), + BF_HOOK_NF_POST_ROUTING, BF_HOOK_NF_PRE_ROUTING, + BF_HOOK_CGROUP_SOCK_ADDR_CONNECT4, + BF_HOOK_CGROUP_SOCK_ADDR_CONNECT6), .ops = { BF_MATCHER_OPS(BF_MATCHER_EQ, sizeof(float), @@ -949,6 +967,8 @@ static struct bf_matcher_meta _bf_matcher_metas[_BF_MATCHER_TYPE_MAX] = { [BF_MATCHER_IP4_SADDR] = { .layer = BF_MATCHER_LAYER_3, + .unsupported_hooks = BF_FLAGS(BF_HOOK_CGROUP_SOCK_ADDR_CONNECT4, + BF_HOOK_CGROUP_SOCK_ADDR_CONNECT6), .hdr_id = ETH_P_IP, .hdr_payload_size = sizeof(uint32_t), .hdr_payload_offset = offsetof(struct iphdr, saddr), @@ -965,6 +985,8 @@ static struct bf_matcher_meta _bf_matcher_metas[_BF_MATCHER_TYPE_MAX] = { [BF_MATCHER_IP4_DADDR] = { .layer = BF_MATCHER_LAYER_3, + .unsupported_hooks = BF_FLAGS(BF_HOOK_CGROUP_SOCK_ADDR_CONNECT4, + BF_HOOK_CGROUP_SOCK_ADDR_CONNECT6), .hdr_id = ETH_P_IP, .hdr_payload_size = sizeof(uint32_t), .hdr_payload_offset = offsetof(struct iphdr, daddr), @@ -981,6 +1003,8 @@ static struct bf_matcher_meta _bf_matcher_metas[_BF_MATCHER_TYPE_MAX] = { [BF_MATCHER_IP4_SNET] = { .layer = BF_MATCHER_LAYER_3, + .unsupported_hooks = BF_FLAGS(BF_HOOK_CGROUP_SOCK_ADDR_CONNECT4, + BF_HOOK_CGROUP_SOCK_ADDR_CONNECT6), .hdr_id = ETH_P_IP, .hdr_payload_size = sizeof(uint32_t), .hdr_payload_offset = offsetof(struct iphdr, saddr), @@ -997,6 +1021,8 @@ static struct bf_matcher_meta _bf_matcher_metas[_BF_MATCHER_TYPE_MAX] = { [BF_MATCHER_IP4_DNET] = { .layer = BF_MATCHER_LAYER_3, + .unsupported_hooks = BF_FLAGS(BF_HOOK_CGROUP_SOCK_ADDR_CONNECT4, + BF_HOOK_CGROUP_SOCK_ADDR_CONNECT6), .hdr_id = ETH_P_IP, .hdr_payload_size = sizeof(uint32_t), .hdr_payload_offset = offsetof(struct iphdr, daddr), @@ -1013,6 +1039,8 @@ static struct bf_matcher_meta _bf_matcher_metas[_BF_MATCHER_TYPE_MAX] = { [BF_MATCHER_IP4_PROTO] = { .layer = BF_MATCHER_LAYER_3, + .unsupported_hooks = BF_FLAGS(BF_HOOK_CGROUP_SOCK_ADDR_CONNECT4, + BF_HOOK_CGROUP_SOCK_ADDR_CONNECT6), .hdr_id = ETH_P_IP, .hdr_payload_size = sizeof(uint8_t), .hdr_payload_offset = offsetof(struct iphdr, protocol), @@ -1029,6 +1057,8 @@ static struct bf_matcher_meta _bf_matcher_metas[_BF_MATCHER_TYPE_MAX] = { [BF_MATCHER_IP4_DSCP] = { .layer = BF_MATCHER_LAYER_3, + .unsupported_hooks = BF_FLAGS(BF_HOOK_CGROUP_SOCK_ADDR_CONNECT4, + BF_HOOK_CGROUP_SOCK_ADDR_CONNECT6), .hdr_id = ETH_P_IP, .hdr_payload_size = sizeof(uint8_t), .hdr_payload_offset = offsetof(struct iphdr, tos), @@ -1043,6 +1073,8 @@ static struct bf_matcher_meta _bf_matcher_metas[_BF_MATCHER_TYPE_MAX] = { [BF_MATCHER_IP6_SADDR] = { .layer = BF_MATCHER_LAYER_3, + .unsupported_hooks = BF_FLAGS(BF_HOOK_CGROUP_SOCK_ADDR_CONNECT4, + BF_HOOK_CGROUP_SOCK_ADDR_CONNECT6), .hdr_id = ETH_P_IPV6, .hdr_payload_size = sizeof(struct in6_addr), .hdr_payload_offset = offsetof(struct ipv6hdr, saddr), @@ -1059,6 +1091,8 @@ static struct bf_matcher_meta _bf_matcher_metas[_BF_MATCHER_TYPE_MAX] = { [BF_MATCHER_IP6_DADDR] = { .layer = BF_MATCHER_LAYER_3, + .unsupported_hooks = BF_FLAGS(BF_HOOK_CGROUP_SOCK_ADDR_CONNECT4, + BF_HOOK_CGROUP_SOCK_ADDR_CONNECT6), .hdr_id = ETH_P_IPV6, .hdr_payload_size = sizeof(struct in6_addr), .hdr_payload_offset = offsetof(struct ipv6hdr, daddr), @@ -1075,6 +1109,8 @@ static struct bf_matcher_meta _bf_matcher_metas[_BF_MATCHER_TYPE_MAX] = { [BF_MATCHER_IP6_SNET] = { .layer = BF_MATCHER_LAYER_3, + .unsupported_hooks = BF_FLAGS(BF_HOOK_CGROUP_SOCK_ADDR_CONNECT4, + BF_HOOK_CGROUP_SOCK_ADDR_CONNECT6), .hdr_id = ETH_P_IPV6, .hdr_payload_size = sizeof(struct in6_addr), .hdr_payload_offset = offsetof(struct ipv6hdr, saddr), @@ -1091,6 +1127,8 @@ static struct bf_matcher_meta _bf_matcher_metas[_BF_MATCHER_TYPE_MAX] = { [BF_MATCHER_IP6_DNET] = { .layer = BF_MATCHER_LAYER_3, + .unsupported_hooks = BF_FLAGS(BF_HOOK_CGROUP_SOCK_ADDR_CONNECT4, + BF_HOOK_CGROUP_SOCK_ADDR_CONNECT6), .hdr_id = ETH_P_IPV6, .hdr_payload_size = sizeof(struct in6_addr), .hdr_payload_offset = offsetof(struct ipv6hdr, daddr), @@ -1107,6 +1145,8 @@ static struct bf_matcher_meta _bf_matcher_metas[_BF_MATCHER_TYPE_MAX] = { [BF_MATCHER_IP6_NEXTHDR] = { .layer = BF_MATCHER_LAYER_3, + .unsupported_hooks = BF_FLAGS(BF_HOOK_CGROUP_SOCK_ADDR_CONNECT4, + BF_HOOK_CGROUP_SOCK_ADDR_CONNECT6), .hdr_id = ETH_P_IPV6, .hdr_payload_size = sizeof(uint8_t), .hdr_payload_offset = offsetof(struct ipv6hdr, nexthdr), @@ -1123,6 +1163,8 @@ static struct bf_matcher_meta _bf_matcher_metas[_BF_MATCHER_TYPE_MAX] = { [BF_MATCHER_IP6_DSCP] = { .layer = BF_MATCHER_LAYER_3, + .unsupported_hooks = BF_FLAGS(BF_HOOK_CGROUP_SOCK_ADDR_CONNECT4, + BF_HOOK_CGROUP_SOCK_ADDR_CONNECT6), .hdr_id = ETH_P_IPV6, .hdr_payload_size = sizeof(uint8_t), .hdr_payload_offset = 0, @@ -1137,6 +1179,8 @@ static struct bf_matcher_meta _bf_matcher_metas[_BF_MATCHER_TYPE_MAX] = { [BF_MATCHER_TCP_SPORT] = { .layer = BF_MATCHER_LAYER_4, + .unsupported_hooks = BF_FLAGS(BF_HOOK_CGROUP_SOCK_ADDR_CONNECT4, + BF_HOOK_CGROUP_SOCK_ADDR_CONNECT6), .hdr_id = IPPROTO_TCP, .hdr_payload_size = sizeof(uint16_t), .hdr_payload_offset = offsetof(struct tcphdr, source), @@ -1156,6 +1200,8 @@ static struct bf_matcher_meta _bf_matcher_metas[_BF_MATCHER_TYPE_MAX] = { [BF_MATCHER_TCP_DPORT] = { .layer = BF_MATCHER_LAYER_4, + .unsupported_hooks = BF_FLAGS(BF_HOOK_CGROUP_SOCK_ADDR_CONNECT4, + BF_HOOK_CGROUP_SOCK_ADDR_CONNECT6), .hdr_id = IPPROTO_TCP, .hdr_payload_size = sizeof(uint16_t), .hdr_payload_offset = offsetof(struct tcphdr, dest), @@ -1175,6 +1221,8 @@ static struct bf_matcher_meta _bf_matcher_metas[_BF_MATCHER_TYPE_MAX] = { [BF_MATCHER_TCP_FLAGS] = { .layer = BF_MATCHER_LAYER_4, + .unsupported_hooks = BF_FLAGS(BF_HOOK_CGROUP_SOCK_ADDR_CONNECT4, + BF_HOOK_CGROUP_SOCK_ADDR_CONNECT6), .hdr_id = IPPROTO_TCP, .hdr_payload_size = sizeof(uint8_t), .hdr_payload_offset = _BF_TCP_FLAGS_OFFSET, @@ -1193,6 +1241,8 @@ static struct bf_matcher_meta _bf_matcher_metas[_BF_MATCHER_TYPE_MAX] = { [BF_MATCHER_UDP_SPORT] = { .layer = BF_MATCHER_LAYER_4, + .unsupported_hooks = BF_FLAGS(BF_HOOK_CGROUP_SOCK_ADDR_CONNECT4, + BF_HOOK_CGROUP_SOCK_ADDR_CONNECT6), .hdr_id = IPPROTO_UDP, .hdr_payload_size = sizeof(uint16_t), .hdr_payload_offset = offsetof(struct udphdr, source), @@ -1212,6 +1262,8 @@ static struct bf_matcher_meta _bf_matcher_metas[_BF_MATCHER_TYPE_MAX] = { [BF_MATCHER_UDP_DPORT] = { .layer = BF_MATCHER_LAYER_4, + .unsupported_hooks = BF_FLAGS(BF_HOOK_CGROUP_SOCK_ADDR_CONNECT4, + BF_HOOK_CGROUP_SOCK_ADDR_CONNECT6), .hdr_id = IPPROTO_UDP, .hdr_payload_size = sizeof(uint16_t), .hdr_payload_offset = offsetof(struct udphdr, dest), @@ -1231,6 +1283,8 @@ static struct bf_matcher_meta _bf_matcher_metas[_BF_MATCHER_TYPE_MAX] = { [BF_MATCHER_ICMP_TYPE] = { .layer = BF_MATCHER_LAYER_4, + .unsupported_hooks = BF_FLAGS(BF_HOOK_CGROUP_SOCK_ADDR_CONNECT4, + BF_HOOK_CGROUP_SOCK_ADDR_CONNECT6), .hdr_id = IPPROTO_ICMP, .hdr_payload_size = sizeof(uint8_t), .hdr_payload_offset = offsetof(struct icmphdr, type), @@ -1247,6 +1301,8 @@ static struct bf_matcher_meta _bf_matcher_metas[_BF_MATCHER_TYPE_MAX] = { [BF_MATCHER_ICMP_CODE] = { .layer = BF_MATCHER_LAYER_4, + .unsupported_hooks = BF_FLAGS(BF_HOOK_CGROUP_SOCK_ADDR_CONNECT4, + BF_HOOK_CGROUP_SOCK_ADDR_CONNECT6), .hdr_id = IPPROTO_ICMP, .hdr_payload_size = sizeof(uint8_t), .hdr_payload_offset = offsetof(struct icmphdr, code), @@ -1263,6 +1319,8 @@ static struct bf_matcher_meta _bf_matcher_metas[_BF_MATCHER_TYPE_MAX] = { [BF_MATCHER_ICMPV6_TYPE] = { .layer = BF_MATCHER_LAYER_4, + .unsupported_hooks = BF_FLAGS(BF_HOOK_CGROUP_SOCK_ADDR_CONNECT4, + BF_HOOK_CGROUP_SOCK_ADDR_CONNECT6), .hdr_id = IPPROTO_ICMPV6, .hdr_payload_size = sizeof(uint8_t), .hdr_payload_offset = offsetof(struct icmp6hdr, icmp6_type), @@ -1282,6 +1340,8 @@ static struct bf_matcher_meta _bf_matcher_metas[_BF_MATCHER_TYPE_MAX] = { [BF_MATCHER_ICMPV6_CODE] = { .layer = BF_MATCHER_LAYER_4, + .unsupported_hooks = BF_FLAGS(BF_HOOK_CGROUP_SOCK_ADDR_CONNECT4, + BF_HOOK_CGROUP_SOCK_ADDR_CONNECT6), .hdr_id = IPPROTO_ICMPV6, .hdr_payload_size = sizeof(uint8_t), .hdr_payload_offset = offsetof(struct icmp6hdr, icmp6_code), @@ -1295,6 +1355,12 @@ static struct bf_matcher_meta _bf_matcher_metas[_BF_MATCHER_TYPE_MAX] = { _bf_parse_icmp_code, _bf_print_icmp_code), }, }, + [BF_MATCHER_SET] = + { + .layer = BF_MATCHER_NO_LAYER, + .unsupported_hooks = BF_FLAGS(BF_HOOK_CGROUP_SOCK_ADDR_CONNECT4, + BF_HOOK_CGROUP_SOCK_ADDR_CONNECT6), + }, }; const struct bf_matcher_meta *bf_matcher_get_meta(enum bf_matcher_type type) diff --git a/tests/unit/libbpfilter/hook.c b/tests/unit/libbpfilter/hook.c index d7ac0945..9c257a87 100644 --- a/tests/unit/libbpfilter/hook.c +++ b/tests/unit/libbpfilter/hook.c @@ -29,6 +29,10 @@ static void hook_to_str(void **state) "BF_HOOK_TC_INGRESS"); assert_string_equal(bf_hook_to_str(BF_HOOK_NF_PRE_ROUTING), "BF_HOOK_NF_PRE_ROUTING"); + assert_string_equal(bf_hook_to_str(BF_HOOK_CGROUP_SOCK_ADDR_CONNECT4), + "BF_HOOK_CGROUP_SOCK_ADDR_CONNECT4"); + assert_string_equal(bf_hook_to_str(BF_HOOK_CGROUP_SOCK_ADDR_CONNECT6), + "BF_HOOK_CGROUP_SOCK_ADDR_CONNECT6"); } static void hook_from_str(void **state) @@ -60,6 +64,10 @@ static void hook_to_flavor(void **state) BF_FLAVOR_CGROUP_SKB); assert_int_equal(bf_hook_to_flavor(BF_HOOK_CGROUP_SKB_EGRESS), BF_FLAVOR_CGROUP_SKB); + assert_int_equal(bf_hook_to_flavor(BF_HOOK_CGROUP_SOCK_ADDR_CONNECT4), + BF_FLAVOR_CGROUP_SOCK_ADDR); + assert_int_equal(bf_hook_to_flavor(BF_HOOK_CGROUP_SOCK_ADDR_CONNECT6), + BF_FLAVOR_CGROUP_SOCK_ADDR); } static void hook_to_bpf_attach_type(void **state) @@ -78,6 +86,12 @@ static void hook_to_bpf_attach_type(void **state) BF_BPF_TCX_INGRESS); assert_int_equal(bf_hook_to_bpf_attach_type(BF_HOOK_TC_EGRESS), BF_BPF_TCX_ENGRESS); + assert_int_equal( + bf_hook_to_bpf_attach_type(BF_HOOK_CGROUP_SOCK_ADDR_CONNECT4), + BF_BPF_CGROUP_INET4_CONNECT); + assert_int_equal( + bf_hook_to_bpf_attach_type(BF_HOOK_CGROUP_SOCK_ADDR_CONNECT6), + BF_BPF_CGROUP_INET6_CONNECT); } static void hook_to_bpf_prog_type(void **state) @@ -97,6 +111,12 @@ static void hook_to_bpf_prog_type(void **state) BF_BPF_PROG_TYPE_SCHED_CLS); assert_int_equal(bf_hook_to_bpf_prog_type(BF_HOOK_NF_PRE_ROUTING), BF_BPF_PROG_TYPE_NETFILTER); + assert_int_equal( + bf_hook_to_bpf_prog_type(BF_HOOK_CGROUP_SOCK_ADDR_CONNECT4), + BF_BPF_PROG_TYPE_CGROUP_SOCK_ADDR); + assert_int_equal( + bf_hook_to_bpf_prog_type(BF_HOOK_CGROUP_SOCK_ADDR_CONNECT6), + BF_BPF_PROG_TYPE_CGROUP_SOCK_ADDR); } static void hook_to_nf_hook(void **state) @@ -393,11 +413,11 @@ static void hookopts_validate_tc(void **state) } } -static void hookopts_validate_cgroup(void **state) +static void hookopts_validate_cgroup_skb(void **state) { (void)state; - // Cgroup requires cgpath + // cgroup_skb requires cgpath { _free_bf_hookopts_ struct bf_hookopts *hookopts = NULL; assert_ok(bf_hookopts_new(&hookopts)); @@ -415,7 +435,7 @@ static void hookopts_validate_cgroup(void **state) assert_ok(bf_hookopts_validate(hookopts, BF_HOOK_CGROUP_SKB_EGRESS)); } - // Cgroup doesn't support ifindex + // cgroup_skb doesn't support ifindex { _free_bf_hookopts_ struct bf_hookopts *hookopts = NULL; char opt1[] = "cgpath=/sys/fs/cgroup"; @@ -427,6 +447,45 @@ static void hookopts_validate_cgroup(void **state) } } +static void hookopts_validate_cgroup_sock_addr(void **state) +{ + (void)state; + + // cgroup_sock_addr requires cgpath + { + _free_bf_hookopts_ struct bf_hookopts *hookopts = NULL; + assert_ok(bf_hookopts_new(&hookopts)); + assert_err( + bf_hookopts_validate(hookopts, BF_HOOK_CGROUP_SOCK_ADDR_CONNECT4)); + assert_err( + bf_hookopts_validate(hookopts, BF_HOOK_CGROUP_SOCK_ADDR_CONNECT6)); + } + + // With cgpath, should be valid + { + _free_bf_hookopts_ struct bf_hookopts *hookopts = NULL; + char opt[] = "cgpath=/sys/fs/cgroup"; + assert_ok(bf_hookopts_new(&hookopts)); + assert_ok(bf_hookopts_parse_opt(hookopts, opt)); + assert_ok( + bf_hookopts_validate(hookopts, BF_HOOK_CGROUP_SOCK_ADDR_CONNECT4)); + assert_ok( + bf_hookopts_validate(hookopts, BF_HOOK_CGROUP_SOCK_ADDR_CONNECT6)); + } + + // cgroup_sock_addr doesn't support ifindex + { + _free_bf_hookopts_ struct bf_hookopts *hookopts = NULL; + char opt1[] = "cgpath=/sys/fs/cgroup"; + char opt2[] = "ifindex=1"; + assert_ok(bf_hookopts_new(&hookopts)); + assert_ok(bf_hookopts_parse_opt(hookopts, opt1)); + assert_ok(bf_hookopts_parse_opt(hookopts, opt2)); + assert_err( + bf_hookopts_validate(hookopts, BF_HOOK_CGROUP_SOCK_ADDR_CONNECT4)); + } +} + static void hookopts_validate_nf(void **state) { (void)state; @@ -691,7 +750,8 @@ int main(void) cmocka_unit_test(hookopts_parse_invalid_format), cmocka_unit_test(hookopts_validate_xdp), cmocka_unit_test(hookopts_validate_tc), - cmocka_unit_test(hookopts_validate_cgroup), + cmocka_unit_test(hookopts_validate_cgroup_skb), + cmocka_unit_test(hookopts_validate_cgroup_sock_addr), cmocka_unit_test(hookopts_validate_nf), cmocka_unit_test(hookopts_pack_and_unpack), cmocka_unit_test(hookopts_pack_empty), From d894cd1dbfbfb8d0b811b78be9671a3cc0a15a6b Mon Sep 17 00:00:00 2001 From: yaakov-stein Date: Thu, 5 Mar 2026 17:15:14 -0800 Subject: [PATCH 2/2] daemon: cgen: add cgroup_sock_addr flavor codegen Implement bf_flavor_ops for BF_FLAVOR_CGROUP_SOCK_ADDR so chains with a default policy can be loaded and attached to a cgroup. --- src/bpfilter/CMakeLists.txt | 1 + src/bpfilter/cgen/cgroup_sock_addr.c | 108 +++++++++++++++++++++++++++ src/bpfilter/cgen/cgroup_sock_addr.h | 10 +++ src/bpfilter/cgen/program.c | 3 +- src/bpfilter/cgen/runtime.h | 5 +- 5 files changed, 124 insertions(+), 3 deletions(-) create mode 100644 src/bpfilter/cgen/cgroup_sock_addr.c create mode 100644 src/bpfilter/cgen/cgroup_sock_addr.h diff --git a/src/bpfilter/CMakeLists.txt b/src/bpfilter/CMakeLists.txt index d630c136..8fe18599 100644 --- a/src/bpfilter/CMakeLists.txt +++ b/src/bpfilter/CMakeLists.txt @@ -13,6 +13,7 @@ add_executable(bpfilter ${CMAKE_CURRENT_SOURCE_DIR}/main.c ${CMAKE_CURRENT_SOURCE_DIR}/cgen/cgen.h ${CMAKE_CURRENT_SOURCE_DIR}/cgen/cgen.c ${CMAKE_CURRENT_SOURCE_DIR}/cgen/cgroup_skb.h ${CMAKE_CURRENT_SOURCE_DIR}/cgen/cgroup_skb.c + ${CMAKE_CURRENT_SOURCE_DIR}/cgen/cgroup_sock_addr.h ${CMAKE_CURRENT_SOURCE_DIR}/cgen/cgroup_sock_addr.c ${CMAKE_CURRENT_SOURCE_DIR}/cgen/dump.h ${CMAKE_CURRENT_SOURCE_DIR}/cgen/dump.c ${CMAKE_CURRENT_SOURCE_DIR}/cgen/elfstub.h ${CMAKE_CURRENT_SOURCE_DIR}/cgen/elfstub.c ${CMAKE_CURRENT_SOURCE_DIR}/cgen/fixup.h ${CMAKE_CURRENT_SOURCE_DIR}/cgen/fixup.c diff --git a/src/bpfilter/cgen/cgroup_sock_addr.c b/src/bpfilter/cgen/cgroup_sock_addr.c new file mode 100644 index 00000000..f9e4462a --- /dev/null +++ b/src/bpfilter/cgen/cgroup_sock_addr.c @@ -0,0 +1,108 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ +/* + * Copyright (c) Meta Platforms, Inc. and affiliates. + */ + +#include "cgen/cgroup_sock_addr.h" + +#include +#include +#include + +#include +#include +#include +#include + +#include +#include +#include +#include + +#include "cgen/program.h" +#include "cgen/swich.h" +#include "filter.h" + +// Forward definition to avoid header conflicts. +uint16_t htons(uint16_t hostshort); + +static int _bf_cgroup_sock_addr_gen_inline_prologue(struct bf_program *program) +{ + int r; + + assert(program); + + // The counters stub reads `pkt_size` unconditionally; zero it out. + EMIT(program, BPF_ST_MEM(BPF_DW, BPF_REG_10, BF_PROG_CTX_OFF(pkt_size), 0)); + + /* Convert `bpf_sock_addr.family` to L3 protocol ID in R7, using the same + * `bf_swich` pattern as cgroup_skb. */ + EMIT(program, BPF_LDX_MEM(BPF_W, BPF_REG_2, BPF_REG_1, + offsetof(struct bpf_sock_addr, family))); + + { + _clean_bf_swich_ struct bf_swich swich = + bf_swich_get(program, BPF_REG_2); + + EMIT_SWICH_OPTION(&swich, AF_INET, + BPF_MOV64_IMM(BPF_REG_7, htons(ETH_P_IP))); + EMIT_SWICH_OPTION(&swich, AF_INET6, + BPF_MOV64_IMM(BPF_REG_7, htons(ETH_P_IPV6))); + EMIT_SWICH_DEFAULT(&swich, BPF_MOV64_IMM(BPF_REG_7, 0)); + + r = bf_swich_generate(&swich); + if (r) + return r; + } + + EMIT(program, BPF_LDX_MEM(BPF_W, BPF_REG_8, BPF_REG_1, + offsetof(struct bpf_sock_addr, protocol))); + + return 0; +} + +static int _bf_cgroup_sock_addr_gen_inline_epilogue(struct bf_program *program) +{ + (void)program; + + return 0; +} + +static int +_bf_cgroup_sock_addr_gen_inline_matcher(struct bf_program *program, + const struct bf_matcher *matcher) +{ + assert(program); + assert(matcher); + + (void)program; + + return bf_err_r(-ENOTSUP, + "matcher '%s' not yet supported for cgroup_sock_addr", + bf_matcher_type_to_str(bf_matcher_get_type(matcher))); +} + +/** + * @brief Convert a standard verdict into a return value. + * + * @param verdict Verdict to convert. Must be valid. + * @return Cgroup return code corresponding to the verdict, as an integer. + */ +static int _bf_cgroup_sock_addr_get_verdict(enum bf_verdict verdict) +{ + switch (verdict) { + case BF_VERDICT_ACCEPT: + return 1; + case BF_VERDICT_DROP: + return 0; + default: + return -ENOTSUP; + } +} + +const struct bf_flavor_ops bf_flavor_ops_cgroup_sock_addr = { + .gen_inline_prologue = _bf_cgroup_sock_addr_gen_inline_prologue, + .gen_inline_epilogue = _bf_cgroup_sock_addr_gen_inline_epilogue, + .get_verdict = _bf_cgroup_sock_addr_get_verdict, + .gen_inline_matcher = _bf_cgroup_sock_addr_gen_inline_matcher, +}; diff --git a/src/bpfilter/cgen/cgroup_sock_addr.h b/src/bpfilter/cgen/cgroup_sock_addr.h new file mode 100644 index 00000000..3755472d --- /dev/null +++ b/src/bpfilter/cgen/cgroup_sock_addr.h @@ -0,0 +1,10 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ +/* + * Copyright (c) Meta Platforms, Inc. and affiliates. + */ + +#pragma once + +#include + +extern const struct bf_flavor_ops bf_flavor_ops_cgroup_sock_addr; diff --git a/src/bpfilter/cgen/program.c b/src/bpfilter/cgen/program.c index 601da81c..ac993ebd 100644 --- a/src/bpfilter/cgen/program.c +++ b/src/bpfilter/cgen/program.c @@ -37,6 +37,7 @@ #include #include "cgen/cgroup_skb.h" +#include "cgen/cgroup_sock_addr.h" #include "cgen/dump.h" #include "cgen/fixup.h" #include "cgen/handle.h" @@ -82,7 +83,7 @@ static const struct bf_flavor_ops *bf_flavor_ops_get(enum bf_flavor flavor) [BF_FLAVOR_NF] = &bf_flavor_ops_nf, [BF_FLAVOR_XDP] = &bf_flavor_ops_xdp, [BF_FLAVOR_CGROUP_SKB] = &bf_flavor_ops_cgroup_skb, - [BF_FLAVOR_CGROUP_SOCK_ADDR] = NULL, + [BF_FLAVOR_CGROUP_SOCK_ADDR] = &bf_flavor_ops_cgroup_sock_addr, }; static_assert_enum_mapping(flavor_ops, _BF_FLAVOR_MAX); diff --git a/src/bpfilter/cgen/runtime.h b/src/bpfilter/cgen/runtime.h index 4e657cae..9f224cf4 100644 --- a/src/bpfilter/cgen/runtime.h +++ b/src/bpfilter/cgen/runtime.h @@ -75,7 +75,8 @@ struct bf_runtime * - `BF_FLAVOR_XDP`: `struct xdp_md *` * - `BF_FLAVOR_TC`: `struct struct __sk_buff *` * - `BF_FLAVOR_CGROUP_SKB`: `struct __sk_buff *` - * - `BF_FLAVOR_NF`: `struct bpf_nf_ctx *` */ + * - `BF_FLAVOR_NF`: `struct bpf_nf_ctx *` + * - `BF_FLAVOR_CGROUP_SOCK_ADDR`: `struct bpf_sock_addr *` */ void *arg; /** BPF dynamic pointer to access the packet data. Dynamic pointers are @@ -85,7 +86,7 @@ struct bf_runtime /** Ring buffer map containing the logged packets. */ void *log_map; - /** Total size of the packet. */ + /** Total size of the packet, or 0 for non-packet flavors. */ __u64 pkt_size; /** IPv6 extension header mask */