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: 15 additions & 4 deletions doc/usage/bfcli.rst
Original file line number Diff line number Diff line change
Expand Up @@ -340,7 +340,13 @@ With:
- ``BF_HOOK_NF_LOCAL_OUT``: similar to ``nftables`` and ``iptables`` output hook.
- ``BF_HOOK_NF_POST_ROUTING``: similar to ``nftables`` and ``iptables`` postrouting hook.
- ``BF_HOOK_TC_EGRESS``: egress TC 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.
- ``$POLICY``: action taken if no rule matches the packet:

- ``ACCEPT``: forward the packet to the kernel.
- ``DROP``: discard the packet.
- ``NEXT``: pass the packet to the next BPF program. For TC hooks, this maps to ``TCX_NEXT``, deferring the decision to the next program in the TCX link. For NF, XDP, and cgroup_skb hooks, ``NEXT`` behaves identically to ``ACCEPT`` since these hooks do not distinguish between "accept" and "pass to next program."
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Do you want to call out that if there are no other BPF programs then this acts like ACCEPT?


Note: ``CONTINUE`` and ``REDIRECT`` are not valid chain policies.

``$OPTIONS`` are hook-specific comma separated key value pairs. A given hook option can only be specified once:

Expand Down Expand Up @@ -391,13 +397,14 @@ With:
- ``log``: optional. If set, log the requested protocol headers. ``link`` will log the link (layer 2) header, ``internet`` with log the internet (layer 3) header, and ``transport`` will log the transport (layer 4) header. At least one header type is required.
- ``counter``: optional literal. If set, the filter will counter the number of packets and bytes matched by the rule.
- ``mark``: optional, ``$MARK`` must be a valid decimal or hexadecimal 32-bits value. If set, write the packet's marker value. This marker can be used later on in a rule (see ``meta.mark``) or with a TC filter.
- ``$VERDICT``: action taken by the rule if the packet is matched against **all** the criteria: either ``ACCEPT``, ``DROP``, ``CONTINUE``, or ``REDIRECT``.
- ``$VERDICT``: action taken by the rule if the packet is matched against **all** the criteria: either ``ACCEPT``, ``DROP``, ``CONTINUE``, ``NEXT``, or ``REDIRECT``.
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

nit:

Suggested change
- ``$VERDICT``: action taken by the rule if the packet is matched against **all** the criteria: either ``ACCEPT``, ``DROP``, ``CONTINUE``, ``NEXT``, or ``REDIRECT``.
- ``$VERDICT``: action taken by the rule if the packet is matched against **all** the criteria. One of the following:

Not sure why you want to list the verdicts and then list them again with descriptions

- ``ACCEPT``: forward the packet to the kernel.
- ``DROP``: discard the packet.
- ``CONTINUE``: continue processing subsequent rules.
- ``CONTINUE``: continue processing subsequent rules (non-terminal).
- ``NEXT``: stop rule processing and pass the packet to the next BPF program. For TC hooks, this returns ``TCX_NEXT``. For NF, XDP, and cgroup_skb hooks, this is equivalent to ``ACCEPT``.
- ``REDIRECT $IFACE $DIR``: redirect the packet to interface ``$IFACE`` in direction ``$DIR``. ``$IFACE`` can be an interface name (e.g., "eth0") or an interface index (e.g., "2"). ``$DIR`` is either ``in`` for ingress or ``out`` for egress.

In a chain, as soon as a rule matches a packet, its verdict is applied. If the verdict is ``ACCEPT``, ``DROP``, or ``REDIRECT``, the subsequent rules are not processed. Hence, the rules' order matters. If no rule matches the packet, the chain's policy is applied.
In a chain, as soon as a rule matches a packet, its verdict is applied. If the verdict is ``ACCEPT``, ``DROP``, ``NEXT``, or ``REDIRECT``, the subsequent rules are not processed. Hence, the rules' order matters. If no rule matches the packet, the chain's policy is applied.

Note ``CONTINUE`` means a packet can be counted more than once if multiple rules specify ``CONTINUE`` and ``counter``.

Expand All @@ -409,6 +416,10 @@ Note ``CONTINUE`` means a packet can be counted more than once if multiple rules

``REDIRECT`` is **not** supported by Netfilter (``BF_HOOK_NF_*``) or cgroup_skb (``BF_HOOK_CGROUP_SKB_*``) hooks.

.. note::

``NEXT`` has distinct behavior only for TC hooks (``BF_HOOK_TC_INGRESS``, ``BF_HOOK_TC_EGRESS``), where it maps to ``TCX_NEXT`` and defers to the next BPF program in the TCX link. For all other hooks (Netfilter, XDP, cgroup_skb), ``NEXT`` produces the same return code as ``ACCEPT``.
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

nit

Suggested change
``NEXT`` has distinct behavior only for TC hooks (``BF_HOOK_TC_INGRESS``, ``BF_HOOK_TC_EGRESS``), where it maps to ``TCX_NEXT`` and defers to the next BPF program in the TCX link. For all other hooks (Netfilter, XDP, cgroup_skb), ``NEXT`` produces the same return code as ``ACCEPT``.
``NEXT`` has distinct behavior only for TC hooks (``BF_HOOK_TC_INGRESS``, ``BF_HOOK_TC_EGRESS``), where it maps to ``TCX_NEXT`` and defers to the next BPF program in the TCX link. For all other hooks (Netfilter, XDP, cgroup_skb), ``NEXT`` is equivalent to ``ACCEPT``.


Sets
~~~~

Expand Down
2 changes: 1 addition & 1 deletion src/bfcli/lexer.l
Original file line number Diff line number Diff line change
Expand Up @@ -68,7 +68,7 @@ BF_HOOK_[A-Z0-9_]+ { BEGIN(STATE_HOOK_OPTS); yylval.sval = strdup(yytext); retur
}
}
/* Verdicts */
(ACCEPT|DROP|CONTINUE) { yylval.sval = strdup(yytext); return VERDICT; }
(ACCEPT|DROP|CONTINUE|NEXT) { yylval.sval = strdup(yytext); return VERDICT; }
REDIRECT { BEGIN(STATE_REDIRECT_IFACE); return REDIRECT_TOKEN; }
<STATE_REDIRECT_IFACE>{
{int}|[0-9a-zA-Z_-]+ {
Expand Down
4 changes: 2 additions & 2 deletions src/bfcli/parser.y
Original file line number Diff line number Diff line change
Expand Up @@ -160,8 +160,8 @@ chain : CHAIN STRING hook hookopts verdict sets rules
_free_bf_list_ bf_list *rules = $7;
int r;

if ($5 >= _BF_TERMINAL_VERDICT_MAX)
bf_parse_err("'%s' is not supported for chains\n", bf_verdict_to_str($5));
if (!bf_verdict_is_valid_policy($5))
bf_parse_err("'%s' is not a valid chain policy\n", bf_verdict_to_str($5));

if (bf_chain_new(&chain, name, $3, $5, &ruleset->sets, rules) < 0)
bf_parse_err("failed to create a new bf_chain\n");
Expand Down
13 changes: 9 additions & 4 deletions src/bpfilter/cgen/cgroup_skb.c
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,6 @@
#include <bpfilter/matcher.h>
#include <bpfilter/verdict.h>

#include "cgen/cgen.h"
#include "cgen/matcher/cmp.h"
#include "cgen/matcher/packet.h"
#include "cgen/program.h"
Expand Down Expand Up @@ -141,14 +140,20 @@ static int _bf_cgroup_skb_gen_inline_matcher(struct bf_program *program,
* 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.
* @param ret_code Cgroup return code. Can't be NULL.
* @return 0 on success, or a negative errno value on failure.
*/
static int _bf_cgroup_skb_get_verdict(enum bf_verdict verdict)
static int _bf_cgroup_skb_get_verdict(enum bf_verdict verdict, int *ret_code)
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Since these are internal APIs, I think you could have kept the same return mechanism without adding the ret_code param and just check for -ENOTSUP on the caller. Obviously a matter of style but less changes.

{
assert(ret_code);

switch (verdict) {
case BF_VERDICT_ACCEPT:
return 1;
case BF_VERDICT_NEXT:
*ret_code = 1;
return 0;
case BF_VERDICT_DROP:
*ret_code = 0;
return 0;
default:
return -ENOTSUP;
Expand Down
13 changes: 10 additions & 3 deletions src/bpfilter/cgen/cgroup_sock_addr.c
Original file line number Diff line number Diff line change
Expand Up @@ -86,14 +86,21 @@ _bf_cgroup_sock_addr_gen_inline_matcher(struct bf_program *program,
* @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.
* @param ret_code Cgroup return code. Can't be NULL.
* @return 0 on success, or a negative errno value on failure.
*/
static int _bf_cgroup_sock_addr_get_verdict(enum bf_verdict verdict)
static int _bf_cgroup_sock_addr_get_verdict(enum bf_verdict verdict,
int *ret_code)
{
assert(ret_code);

switch (verdict) {
case BF_VERDICT_ACCEPT:
return 1;
case BF_VERDICT_NEXT:
*ret_code = 1;
return 0;
case BF_VERDICT_DROP:
*ret_code = 0;
return 0;
default:
return -ENOTSUP;
Expand Down
10 changes: 7 additions & 3 deletions src/bpfilter/cgen/jmp.h
Original file line number Diff line number Diff line change
Expand Up @@ -20,10 +20,14 @@
* {
* _clean_bf_jmpctx_ struct bf_jmpctx ctx =
* bf_jmpctx_get(program, BPF_JMP_IMM(BPF_JEQ, BPF_REG_2, 0, 0));
* int ret;
* int r;
*
* EMIT(program,
* BPF_MOV64_IMM(BPF_REG_0, program->runtime.ops->get_verdict(
* BF_VERDICT_ACCEPT)));
* r = program->runtime.ops->get_verdict(BF_VERDICT_ACCEPT, &ret);
* if (r)
* return r;
*
* EMIT(program, BPF_MOV64_IMM(BPF_REG_0, ret));
* EMIT(program, BPF_EXIT_INSN());
* }
* @endcode
Expand Down
14 changes: 10 additions & 4 deletions src/bpfilter/cgen/nf.c
Original file line number Diff line number Diff line change
Expand Up @@ -157,15 +157,21 @@ static int _bf_nf_gen_inline_matcher(struct bf_program *program,
* Convert a standard verdict into a return value.
*
* @param verdict Verdict to convert. Must be valid.
* @return Netfilter return code corresponding to the verdict, as an integer.
* @param ret_code Netfilter return code. Can't be NULL.
* @return 0 on success, or a negative errno value on failure.
*/
static int _bf_nf_get_verdict(enum bf_verdict verdict)
static int _bf_nf_get_verdict(enum bf_verdict verdict, int *ret_code)
{
assert(ret_code);

switch (verdict) {
case BF_VERDICT_ACCEPT:
return NF_ACCEPT;
case BF_VERDICT_NEXT:
*ret_code = NF_ACCEPT;
return 0;
case BF_VERDICT_DROP:
return NF_DROP;
*ret_code = NF_DROP;
return 0;
default:
return -ENOTSUP;
}
Expand Down
15 changes: 9 additions & 6 deletions src/bpfilter/cgen/program.c
Original file line number Diff line number Diff line change
Expand Up @@ -287,6 +287,7 @@ static int _bf_program_generate_rule(struct bf_program *program,
struct bf_rule *rule)
{
uint32_t checked_layers = 0;
int ret_code;
int r;

assert(program);
Expand Down Expand Up @@ -371,10 +372,11 @@ static int _bf_program_generate_rule(struct bf_program *program,
switch (rule->verdict) {
case BF_VERDICT_ACCEPT:
case BF_VERDICT_DROP:
r = program->runtime.ops->get_verdict(rule->verdict);
if (r < 0)
case BF_VERDICT_NEXT:
r = program->runtime.ops->get_verdict(rule->verdict, &ret_code);
if (r)
return r;
EMIT(program, BPF_MOV64_IMM(BPF_REG_0, r));
EMIT(program, BPF_MOV64_IMM(BPF_REG_0, ret_code));
EMIT(program, BPF_EXIT_INSN());
break;
case BF_VERDICT_REDIRECT:
Expand Down Expand Up @@ -576,6 +578,7 @@ int bf_program_emit_fixup_elfstub(struct bf_program *program,
int bf_program_generate(struct bf_program *program)
{
const struct bf_chain *chain = program->runtime.chain;
int ret_code;
int r;

// Save the program's argument into the context.
Expand Down Expand Up @@ -623,10 +626,10 @@ int bf_program_generate(struct bf_program *program)
BPF_MOV32_IMM(BPF_REG_3, bf_program_chain_counter_idx(program)));
EMIT_FIXUP_ELFSTUB(program, BF_ELFSTUB_UPDATE_COUNTERS);

r = program->runtime.ops->get_verdict(chain->policy);
if (r < 0)
r = program->runtime.ops->get_verdict(chain->policy, &ret_code);
if (r)
return r;
EMIT(program, BPF_MOV64_IMM(BPF_REG_0, r));
EMIT(program, BPF_MOV64_IMM(BPF_REG_0, ret_code));
EMIT(program, BPF_EXIT_INSN());

r = _bf_program_generate_elfstubs(program);
Expand Down
32 changes: 20 additions & 12 deletions src/bpfilter/cgen/stub.c
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,7 @@
static int _bf_stub_make_ctx_dynptr(struct bf_program *program, int arg_reg,
const char *kfunc)
{
int ret_code;
int r;

assert(program);
Expand Down Expand Up @@ -76,10 +77,11 @@ static int _bf_stub_make_ctx_dynptr(struct bf_program *program, int arg_reg,
if (bf_ctx_is_verbose(BF_VERBOSE_BPF))
EMIT_PRINT(program, "failed to create a new dynamic pointer");

r = program->runtime.ops->get_verdict(BF_VERDICT_ACCEPT);
if (r < 0)
r = program->runtime.ops->get_verdict(BF_VERDICT_ACCEPT, &ret_code);
if (r)
return r;
EMIT(program, BPF_MOV64_IMM(BPF_REG_0, r));

EMIT(program, BPF_MOV64_IMM(BPF_REG_0, ret_code));
EMIT(program, BPF_EXIT_INSN());
}

Expand All @@ -102,6 +104,7 @@ int bf_stub_make_ctx_skb_dynptr(struct bf_program *program, int skb_reg)

int bf_stub_parse_l2_ethhdr(struct bf_program *program)
{
int ret_code;
int r;

assert(program);
Expand Down Expand Up @@ -135,10 +138,11 @@ int bf_stub_parse_l2_ethhdr(struct bf_program *program)
if (bf_ctx_is_verbose(BF_VERBOSE_BPF))
EMIT_PRINT(program, "failed to create L2 dynamic pointer slice");

r = program->runtime.ops->get_verdict(BF_VERDICT_ACCEPT);
if (r < 0)
r = program->runtime.ops->get_verdict(BF_VERDICT_ACCEPT, &ret_code);
if (r)
return r;
EMIT(program, BPF_MOV64_IMM(BPF_REG_0, r));

EMIT(program, BPF_MOV64_IMM(BPF_REG_0, ret_code));
EMIT(program, BPF_EXIT_INSN());
}

Expand All @@ -160,6 +164,7 @@ int bf_stub_parse_l2_ethhdr(struct bf_program *program)
int bf_stub_parse_l3_hdr(struct bf_program *program)
{
_clean_bf_jmpctx_ struct bf_jmpctx _ = bf_jmpctx_default();
int ret_code;
int r;

assert(program);
Expand Down Expand Up @@ -210,10 +215,11 @@ int bf_stub_parse_l3_hdr(struct bf_program *program)
if (bf_ctx_is_verbose(BF_VERBOSE_BPF))
EMIT_PRINT(program, "failed to create L3 dynamic pointer slice");

r = program->runtime.ops->get_verdict(BF_VERDICT_ACCEPT);
if (r < 0)
r = program->runtime.ops->get_verdict(BF_VERDICT_ACCEPT, &ret_code);
if (r)
return r;
EMIT(program, BPF_MOV64_IMM(BPF_REG_0, r));

EMIT(program, BPF_MOV64_IMM(BPF_REG_0, ret_code));
EMIT(program, BPF_EXIT_INSN());
}

Expand Down Expand Up @@ -316,6 +322,7 @@ int bf_stub_parse_l3_hdr(struct bf_program *program)
int bf_stub_parse_l4_hdr(struct bf_program *program)
{
_clean_bf_jmpctx_ struct bf_jmpctx _ = bf_jmpctx_default();
int ret_code;
int r;

assert(program);
Expand Down Expand Up @@ -369,10 +376,11 @@ int bf_stub_parse_l4_hdr(struct bf_program *program)
if (bf_ctx_is_verbose(BF_VERBOSE_BPF))
EMIT_PRINT(program, "failed to create L4 dynamic pointer slice");

r = program->runtime.ops->get_verdict(BF_VERDICT_ACCEPT);
if (r < 0)
r = program->runtime.ops->get_verdict(BF_VERDICT_ACCEPT, &ret_code);
if (r)
return r;
EMIT(program, BPF_MOV64_IMM(BPF_REG_0, r));

EMIT(program, BPF_MOV64_IMM(BPF_REG_0, ret_code));
EMIT(program, BPF_EXIT_INSN());
}

Expand Down
17 changes: 12 additions & 5 deletions src/bpfilter/cgen/tc.c
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,6 @@
#include <bpfilter/matcher.h>
#include <bpfilter/verdict.h>

#include "cgen/cgen.h"
#include "cgen/matcher/cmp.h"
#include "cgen/matcher/packet.h"
#include "cgen/program.h"
Expand Down Expand Up @@ -154,15 +153,23 @@ static int _bf_tc_gen_inline_redirect(struct bf_program *program,
* Convert a standard verdict into a return value.
*
* @param verdict Verdict to convert. Must be valid.
* @return TC return code corresponding to the verdict, as an integer.
* @param ret_code TC return code. Can't be NULL.
* @return 0 on success, or a negative errno value on failure.
*/
static int _bf_tc_get_verdict(enum bf_verdict verdict)
static int _bf_tc_get_verdict(enum bf_verdict verdict, int *ret_code)
{
assert(ret_code);

switch (verdict) {
case BF_VERDICT_ACCEPT:
return TCX_PASS;
*ret_code = TCX_PASS;
return 0;
case BF_VERDICT_DROP:
return TCX_DROP;
*ret_code = TCX_DROP;
return 0;
case BF_VERDICT_NEXT:
*ret_code = TCX_NEXT;
return 0;
default:
return -ENOTSUP;
}
Expand Down
18 changes: 15 additions & 3 deletions src/bpfilter/cgen/xdp.c
Original file line number Diff line number Diff line change
Expand Up @@ -107,13 +107,25 @@ static int _bf_xdp_gen_inline_redirect(struct bf_program *program,
return 0;
}

static int _bf_xdp_get_verdict(enum bf_verdict verdict)
/**
* Convert a standard verdict into a return value.
*
* @param verdict Verdict to convert. Must be valid.
* @param ret_code XDP return code. Can't be NULL.
* @return 0 on success, or a negative errno value on failure.
*/
static int _bf_xdp_get_verdict(enum bf_verdict verdict, int *ret_code)
{
assert(ret_code);

switch (verdict) {
case BF_VERDICT_ACCEPT:
return XDP_PASS;
case BF_VERDICT_NEXT:
*ret_code = XDP_PASS;
return 0;
case BF_VERDICT_DROP:
return XDP_DROP;
*ret_code = XDP_DROP;
return 0;
default:
return -ENOTSUP;
}
Expand Down
7 changes: 4 additions & 3 deletions src/libbpfilter/chain.c
Original file line number Diff line number Diff line change
Expand Up @@ -185,8 +185,9 @@ int bf_chain_new(struct bf_chain **chain, const char *name, enum bf_hook hook,
assert(chain && name);
if (hook >= _BF_HOOK_MAX)
return bf_err_r(-EINVAL, "unknown hook type");
if (policy >= _BF_TERMINAL_VERDICT_MAX)
return bf_err_r(-EINVAL, "unknown policy type");
if (!bf_verdict_is_valid_policy(policy))
return bf_err_r(-EINVAL, "invalid policy '%s'",
bf_verdict_to_str(policy));

_chain = malloc(sizeof(*_chain));
if (!_chain)
Expand Down Expand Up @@ -240,7 +241,7 @@ int bf_chain_new_from_pack(struct bf_chain **chain, bf_rpack_node_t node)
if (r)
return bf_rpack_key_err(r, "bf_chain.hook");

r = bf_rpack_kv_enum(node, "policy", &policy, 0, _BF_TERMINAL_VERDICT_MAX);
r = bf_rpack_kv_enum(node, "policy", &policy, 0, _BF_VERDICT_MAX);
if (r)
return bf_rpack_key_err(r, "bf_chain.policy");

Expand Down
Loading
Loading