Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
19 changes: 12 additions & 7 deletions doc/usage/bfcli.rst
Original file line number Diff line number Diff line change
Expand Up @@ -360,7 +360,7 @@ With:

.. note::

``BF_HOOK_CGROUP_SOCK_ADDR_*`` hooks operate on socket metadata rather than packet data. Supported matchers: ``meta.l3_proto``, ``meta.l4_proto``, ``meta.probability``, ``meta.dport``, ``ip4.daddr``, ``ip4.dnet``, ``ip4.proto``, ``ip6.daddr``, ``ip6.dnet``, ``udp.dport``. Connect hooks additionally support ``tcp.dport``. Sendmsg hooks additionally support ``ip4.saddr``, ``ip4.snet``, ``ip6.saddr``, and ``ip6.snet``.
``BF_HOOK_CGROUP_SOCK_ADDR_*`` hooks operate on socket metadata rather than packet data. Supported matchers: ``meta.l3_proto``, ``meta.l4_proto``, ``meta.probability``, ``meta.dport``, ``ip4.daddr``, ``ip4.dnet``, ``ip4.proto``, ``ip6.daddr``, ``ip6.dnet``, ``udp.dport``. Connect hooks additionally support ``tcp.dport``. Sendmsg hooks additionally support ``ip4.saddr``, ``ip4.snet``, ``ip6.saddr``, and ``ip6.snet``. Sets are supported with the same per-component restrictions: each set key component must be a matcher supported by the chain's hook.

- ``$POLICY``: action taken if no rule matches the packet, either ``ACCEPT`` forward the packet to the kernel, or ``DROP`` to discard it. Note while ``CONTINUE`` is a valid verdict for rules, it is not supported for chain policy.

Expand Down Expand Up @@ -647,16 +647,21 @@ IPv6
- Operator
- Payload
- Notes
* - :rspan:`1` Source address
- :rspan:`1` ``ip6.saddr``
* - :rspan:`2` Source address
Copy link
Copy Markdown
Contributor Author

@yaakov-stein yaakov-stein Mar 31, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

drive-by fix (document ip6.d/saddr works in sets)

- :rspan:`2` ``ip6.saddr``
- ``eq``
- :rspan:`3` ``$ADDR``
- :rspan:`3` ``$ADDR`` must be an IPv6 address composed of 8 hexadecimal numbers (abbreviations are supported). To filter on an IPv6 network (using an IPv6 address and a subnet mask), see ``ip6.snet`` or ``ip6.dnet``.
- :rspan:`1` ``$ADDR``
- :rspan:`5` ``$ADDR`` must be an IPv6 address composed of 8 hexadecimal numbers (abbreviations are supported). To filter on an IPv6 network (using an IPv6 address and a subnet mask), see ``ip6.snet`` or ``ip6.dnet``.
* - ``not``
* - :rspan:`1` Destination address
- :rspan:`1` ``ip6.daddr``
* - ``in``
- ``{$ADDR[,...]}``
* - :rspan:`2` Destination address
- :rspan:`2` ``ip6.daddr``
- ``eq``
- :rspan:`1` ``$ADDR``
* - ``not``
* - ``in``
- ``{$ADDR[,...]}``
* - :rspan:`2` Source network
- :rspan:`2` ``ip6.snet``
- ``eq``
Expand Down
99 changes: 99 additions & 0 deletions src/libbpfilter/cgen/cgroup_sock_addr.c
Original file line number Diff line number Diff line change
Expand Up @@ -14,15 +14,19 @@
#include <stdint.h>
#include <sys/socket.h>

#include <bpfilter/chain.h>
#include <bpfilter/flavor.h>
#include <bpfilter/logger.h>
#include <bpfilter/matcher.h>
#include <bpfilter/runtime.h>
#include <bpfilter/set.h>
#include <bpfilter/verdict.h>

#include "cgen/matcher/cmp.h"
#include "cgen/matcher/meta.h"
#include "cgen/matcher/set.h"
#include "cgen/program.h"
#include "cgen/stub.h"
#include "cgen/swich.h"
#include "filter.h"

Expand Down Expand Up @@ -204,6 +208,99 @@ static int _bf_cgroup_sock_addr_generate_port(struct bf_program *program,
bf_matcher_payload(matcher), 2, BPF_REG_1);
}
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Claude: nit: _bf_cgroup_sock_addr_ctx_offset is a non-trivial function mapping matcher types to bpf_sock_addr field offsets, with a sentinel return convention ((size_t)-1 for unsupported types). A brief Doxygen comment documenting the purpose, the return convention, and the handled matcher types would help maintainability. Other comparable helpers in this file (e.g., _bf_cgroup_sock_addr_generate_port) have documentation.


static size_t _bf_cgroup_sock_addr_ctx_offset(enum bf_matcher_type type)
{
switch (type) {
case BF_MATCHER_IP4_SADDR:
case BF_MATCHER_IP4_SNET:
return offsetof(struct bpf_sock_addr, msg_src_ip4);
case BF_MATCHER_IP4_DADDR:
case BF_MATCHER_IP4_DNET:
return offsetof(struct bpf_sock_addr, user_ip4);
case BF_MATCHER_IP6_SADDR:
case BF_MATCHER_IP6_SNET:
return offsetof(struct bpf_sock_addr, msg_src_ip6);
case BF_MATCHER_IP6_DADDR:
case BF_MATCHER_IP6_DNET:
return offsetof(struct bpf_sock_addr, user_ip6);
case BF_MATCHER_IP4_PROTO:
return offsetof(struct bpf_sock_addr, protocol);
case BF_MATCHER_TCP_DPORT:
case BF_MATCHER_UDP_DPORT:
return offsetof(struct bpf_sock_addr, user_port);
default:
return (size_t)-1;
}
}

static int _bf_cgroup_sock_addr_generate_set(struct bf_program *program,
const struct bf_matcher *matcher)
{
assert(program);
assert(matcher);

const struct bf_set *set =
bf_chain_get_set_for_matcher(program->runtime.chain, matcher);
size_t offset = 0;
int r;

if (!set) {
return bf_err_r(-ENOENT, "set #%u not found in %s",
*(uint32_t *)bf_matcher_payload(matcher),
program->runtime.chain->name);
}

if (set->use_trie) {
const struct bf_matcher_meta *meta = bf_matcher_get_meta(set->key[0]);
size_t ctx_off = _bf_cgroup_sock_addr_ctx_offset(set->key[0]);

if (!meta) {
return bf_err_r(-EINVAL, "missing meta for set component '%s'",
bf_matcher_type_to_str(set->key[0]));
}

if (ctx_off == (size_t)-1) {
return bf_err_r(
-ENOTSUP,
"set component '%s' not supported for cgroup_sock_addr",
bf_matcher_type_to_str(set->key[0]));
}

return bf_set_generate_trie_lookup(program, matcher, ctx_off,
meta->hdr_payload_size);
}

for (size_t i = 0; i < set->n_comps; ++i) {
enum bf_matcher_type type = set->key[i];
const struct bf_matcher_meta *meta = bf_matcher_get_meta(type);
size_t ctx_off = _bf_cgroup_sock_addr_ctx_offset(type);

if (!meta) {
return bf_err_r(-EINVAL, "missing meta for set component '%s'",
bf_matcher_type_to_str(type));
}

if (ctx_off == (size_t)-1) {
return bf_err_r(
-ENOTSUP,
"set component '%s' not supported for cgroup_sock_addr",
bf_matcher_type_to_str(type));
}

/* For ports, `hdr_payload_size` is 2 but `user_port` is a
* 4-byte `__u32`. The BPF verifier rewrites this 2-byte narrow
* ctx read to extract the correct NBO port value. */
r = bf_stub_load(program, ctx_off, meta->hdr_payload_size,
BF_PROG_SCR_OFF(offset));
if (r)
return r;

offset += meta->hdr_payload_size;
}

return bf_set_generate_map_lookup(program, matcher, BF_PROG_SCR_OFF(0));
}

static int
_bf_cgroup_sock_addr_gen_inline_matcher(struct bf_program *program,
const struct bf_matcher *matcher)
Expand Down Expand Up @@ -248,6 +345,8 @@ _bf_cgroup_sock_addr_gen_inline_matcher(struct bf_program *program,
case BF_MATCHER_TCP_DPORT:
case BF_MATCHER_UDP_DPORT:
return _bf_cgroup_sock_addr_generate_port(program, matcher);
case BF_MATCHER_SET:
return _bf_cgroup_sock_addr_generate_set(program, matcher);
default:
return bf_err_r(-ENOTSUP,
"matcher '%s' not supported for cgroup_sock_addr",
Expand Down
62 changes: 61 additions & 1 deletion src/libbpfilter/cgen/matcher/packet.c
Original file line number Diff line number Diff line change
Expand Up @@ -14,9 +14,11 @@
#include <errno.h>
#include <stdint.h>

#include <bpfilter/chain.h>
#include <bpfilter/helper.h>
#include <bpfilter/logger.h>
#include <bpfilter/matcher.h>
#include <bpfilter/set.h>

#include "cgen/matcher/cmp.h"
#include "cgen/matcher/meta.h"
Expand Down Expand Up @@ -296,6 +298,64 @@ static int _bf_matcher_pkt_generate_ip6_dscp(struct bf_program *program,
return 0;
}

static int _bf_matcher_pkt_generate_set(struct bf_program *program,
const struct bf_matcher *matcher)
{
assert(program);
assert(matcher);

const struct bf_set *set =
bf_chain_get_set_for_matcher(program->runtime.chain, matcher);
size_t offset = 0;
int r;

if (!set) {
return bf_err_r(-ENOENT, "set #%u not found in %s",
*(uint32_t *)bf_matcher_payload(matcher),
program->runtime.chain->name);
}

if (set->use_trie) {
const struct bf_matcher_meta *meta = bf_matcher_get_meta(set->key[0]);

if (!meta) {
return bf_err_r(-EINVAL, "missing meta for '%s'",
bf_matcher_type_to_str(set->key[0]));
}

r = bf_stub_load_header(program, meta, BPF_REG_6);
if (r)
return bf_err_r(r, "failed to load protocol header into BPF_REG_6");

return bf_set_generate_trie_lookup(
program, matcher, meta->hdr_payload_offset, meta->hdr_payload_size);
}

for (size_t i = 0; i < set->n_comps; ++i) {
enum bf_matcher_type type = set->key[i];
const struct bf_matcher_meta *meta = bf_matcher_get_meta(type);

if (!meta) {
return bf_err_r(-EINVAL, "missing meta for '%s'",
bf_matcher_type_to_str(type));
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Claude: suggestion: In the hash-map loop of _bf_matcher_pkt_generate_set, bf_stub_load_header is called for every set component. When multiple components share the same protocol layer (e.g. ip4.saddr and ip4.daddr are both L3), the second call redundantly reloads R6 from the same stack slot, emitting unnecessary BPF instructions.

Consider tracking the loaded layer (similar to how _bf_program_check_proto uses checked_layers) to skip redundant header loads. This reduces the generated BPF instruction count for multi-component sets with same-layer keys.

}

r = bf_stub_load_header(program, meta, BPF_REG_6);
if (r)
return bf_err_r(r, "failed to load protocol header into BPF_REG_6");

r = bf_stub_stx_payload(program, meta, offset);
if (r) {
return bf_err_r(r,
"failed to generate bytecode to load packet data");
}

offset += meta->hdr_payload_size;
}

return bf_set_generate_map_lookup(program, matcher, BF_PROG_SCR_OFF(0));
}

int bf_matcher_generate_packet(struct bf_program *program,
const struct bf_matcher *matcher)
{
Expand Down Expand Up @@ -349,7 +409,7 @@ int bf_matcher_generate_packet(struct bf_program *program,
case BF_MATCHER_IP6_DSCP:
return _bf_matcher_pkt_generate_ip6_dscp(program, matcher, meta);
case BF_MATCHER_SET:
return bf_matcher_generate_set(program, matcher);
return _bf_matcher_pkt_generate_set(program, matcher);
default:
return bf_err_r(-EINVAL, "unknown matcher type %d",
bf_matcher_get_type(matcher));
Expand Down
Loading
Loading