Source-Changes-HG archive
[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index][Old Index]
[src/trunk]: src/usr.sbin/npf/npfctl NPF: fix BPF byte-code generation for a ...
details: https://anonhg.NetBSD.org/src/rev/c0e7aa8d741a
branches: trunk
changeset: 964606:c0e7aa8d741a
user: rmind <rmind%NetBSD.org@localhost>
date: Thu Aug 08 21:29:15 2019 +0000
description:
NPF: fix BPF byte-code generation for a port-range used in a group.
Resolved PR/52609 and PR/54169.
diffstat:
usr.sbin/npf/npfctl/npf_bpf_comp.c | 109 +++++++++++++++++++++++++++++++-----
usr.sbin/npf/npfctl/npf_build.c | 10 +-
usr.sbin/npf/npfctl/npfctl.h | 4 +-
3 files changed, 100 insertions(+), 23 deletions(-)
diffs (254 lines):
diff -r 43c9bc7b2cd3 -r c0e7aa8d741a usr.sbin/npf/npfctl/npf_bpf_comp.c
--- a/usr.sbin/npf/npfctl/npf_bpf_comp.c Thu Aug 08 21:14:12 2019 +0000
+++ b/usr.sbin/npf/npfctl/npf_bpf_comp.c Thu Aug 08 21:29:15 2019 +0000
@@ -1,5 +1,5 @@
/*-
- * Copyright (c) 2010-2014 The NetBSD Foundation, Inc.
+ * Copyright (c) 2010-2019 The NetBSD Foundation, Inc.
* All rights reserved.
*
* This material is based upon work partially supported by The
@@ -29,10 +29,60 @@
/*
* BPF byte-code generation for NPF rules.
+ *
+ * Overview
+ *
+ * Each NPF rule is compiled into BPF micro-program. There is a
+ * BPF byte-code fragment for each higher-level filtering logic,
+ * e.g. to match L4 protocol, IP/mask, etc. The generation process
+ * combines multiple BPF-byte code fragments into one program.
+ *
+ * Basic case
+ *
+ * Consider a basic case, where all filters should match. They
+ * are expressed as logical conjunction, e.g.:
+ *
+ * A and B and C and D
+ *
+ * Each test (filter) criterion can be evaluated to true (match) or
+ * false (no match) and the logic is as follows:
+ *
+ * - If the value is true, then jump to the "next" test (offset 0).
+ *
+ * - If the value is false, then jump to the JUMP_MAGIC value (0xff).
+ * This "magic" value is used to indicate that it will have to be
+ * patched at a later stage.
+ *
+ * Once all byte-code fragments are combined into one, then there
+ * are two additional steps:
+ *
+ * - Two instructions are appended at the end of the program: return
+ * "success" followed by return "failure".
+ *
+ * - All jumps with the JUMP_MAGIC value are patched to point to the
+ * "return failure" instruction.
+ *
+ * Therefore, if all filter criteria will match, then the first
+ * instruction will be reached, indicating a successful match of the
+ * rule. Otherwise, if any of the criteria will not match, it will
+ * take the failure path and the rule will not matching.
+ *
+ * Grouping
+ *
+ * Filters can have groups, which are have a meaning of logical
+ * disjunction, e.g.:
+ *
+ * A and B and (C or D)
+ *
+ * In such case, the logic inside the group has to be inverted i.e.
+ * the jump values swapped. If the test value is true, then jump
+ * out of the group; if false, then jump "next". At the end of the
+ * group, an addition failure path is appended and the JUMP_MAGIC
+ * uses within the group are patched to jump past the said path.
*/
#include <sys/cdefs.h>
-__RCSID("$NetBSD: npf_bpf_comp.c,v 1.13 2019/07/23 00:52:02 rmind Exp $");
+__RCSID("$NetBSD: npf_bpf_comp.c,v 1.14 2019/08/08 21:29:15 rmind Exp $");
#include <stdlib.h>
#include <stdbool.h>
@@ -75,7 +125,10 @@
sa_family_t af;
uint32_t flags;
- /* The current group offset and block number. */
+ /*
+ * The current group offset (counted in BPF instructions)
+ * and block number at the start of the group.
+ */
bool ingroup;
u_int goff;
u_int gblock;
@@ -120,6 +173,7 @@
for (u_int i = start; i < end; i++) {
struct bpf_insn *insn = &bp->bf_insns[i];
const u_int fail_off = end - i;
+ bool seen_magic = false;
if (fail_off >= JUMP_MAGIC) {
errx(EXIT_FAILURE, "BPF generation error: "
@@ -128,15 +182,37 @@
if (BPF_CLASS(insn->code) != BPF_JMP) {
continue;
}
- if (swap) {
+ if (BPF_OP(insn->code) == BPF_JA) {
+ /*
+ * BPF_JA can be used to jump to the failure path.
+ * If we are swapping i.e. inside the group, then
+ * jump "next"; groups have a failure path appended
+ * at their end.
+ */
+ if (insn->k == JUMP_MAGIC) {
+ insn->k = swap ? 0 : fail_off;
+ }
+ continue;
+ }
+
+ /*
+ * Fixup the "magic" value. Swap only the "magic" jumps.
+ */
+
+ if (insn->jt == JUMP_MAGIC) {
+ insn->jt = fail_off;
+ seen_magic = true;
+ }
+ if (insn->jf == JUMP_MAGIC) {
+ insn->jf = fail_off;
+ seen_magic = true;
+ }
+
+ if (seen_magic && swap) {
uint8_t jt = insn->jt;
insn->jt = insn->jf;
insn->jf = jt;
}
- if (insn->jt == JUMP_MAGIC)
- insn->jt = fail_off;
- if (insn->jf == JUMP_MAGIC)
- insn->jf = fail_off;
}
}
@@ -225,11 +301,11 @@
}
/*
- * npfctl_bpf_group: begin a logical group. It merely uses logical
+ * npfctl_bpf_group_enter: begin a logical group. It merely uses logical
* disjunction (OR) for compares within the group.
*/
void
-npfctl_bpf_group(npf_bpf_t *ctx)
+npfctl_bpf_group_enter(npf_bpf_t *ctx)
{
struct bpf_program *bp = &ctx->prog;
@@ -242,7 +318,7 @@
}
void
-npfctl_bpf_endgroup(npf_bpf_t *ctx, bool invert)
+npfctl_bpf_group_exit(npf_bpf_t *ctx, bool invert)
{
struct bpf_program *bp = &ctx->prog;
const size_t curoff = bp->bf_len;
@@ -255,7 +331,7 @@
/*
* If inverting, then prepend a jump over the statement below.
- * If matching, jump will jump below and the fail will happen.
+ * On match, it will skip-through and the fail path will be taken.
*/
if (invert) {
struct bpf_insn insns_ret[] = {
@@ -318,7 +394,7 @@
*/
if (ingroup) {
assert(ctx->nblocks == ctx->gblock);
- npfctl_bpf_endgroup(ctx, false);
+ npfctl_bpf_group_exit(ctx, false);
}
/*
@@ -338,7 +414,7 @@
done_raw_block(ctx, mwords, sizeof(mwords));
}
if (ingroup) {
- npfctl_bpf_group(ctx);
+ npfctl_bpf_group_enter(ctx);
}
} else if (af && af != ctx->af) {
@@ -508,8 +584,9 @@
} else {
/* Port range case. */
struct bpf_insn insns_range[] = {
- BPF_JUMP(BPF_JMP+BPF_JGE+BPF_K, from, 0, JUMP_MAGIC),
- BPF_JUMP(BPF_JMP+BPF_JGT+BPF_K, to, JUMP_MAGIC, 0),
+ BPF_JUMP(BPF_JMP+BPF_JGE+BPF_K, from, 0, 1),
+ BPF_JUMP(BPF_JMP+BPF_JGT+BPF_K, to, 0, 1),
+ BPF_STMT(BPF_JMP+BPF_JA, JUMP_MAGIC),
};
add_insns(ctx, insns_range, __arraycount(insns_range));
}
diff -r 43c9bc7b2cd3 -r c0e7aa8d741a usr.sbin/npf/npfctl/npf_build.c
--- a/usr.sbin/npf/npfctl/npf_build.c Thu Aug 08 21:14:12 2019 +0000
+++ b/usr.sbin/npf/npfctl/npf_build.c Thu Aug 08 21:29:15 2019 +0000
@@ -32,7 +32,7 @@
*/
#include <sys/cdefs.h>
-__RCSID("$NetBSD: npf_build.c,v 1.50 2019/07/25 00:48:55 rmind Exp $");
+__RCSID("$NetBSD: npf_build.c,v 1.51 2019/08/08 21:29:15 rmind Exp $");
#include <sys/types.h>
#define __FAVOR_BSD
@@ -290,7 +290,7 @@
const int type = npfvar_get_type(vars, 0);
size_t i;
- npfctl_bpf_group(ctx);
+ npfctl_bpf_group_enter(ctx);
for (i = 0; i < npfvar_get_count(vars); i++) {
void *data = npfvar_get_data(vars, type, i);
assert(data != NULL);
@@ -316,7 +316,7 @@
assert(false);
}
}
- npfctl_bpf_endgroup(ctx, (opts & MATCH_INVERT) != 0);
+ npfctl_bpf_group_exit(ctx, (opts & MATCH_INVERT) != 0);
}
static void
@@ -423,10 +423,10 @@
/* Build port-range blocks. */
if (need_tcpudp) {
/* TCP/UDP check for the ports. */
- npfctl_bpf_group(bc);
+ npfctl_bpf_group_enter(bc);
npfctl_bpf_proto(bc, AF_UNSPEC, IPPROTO_TCP);
npfctl_bpf_proto(bc, AF_UNSPEC, IPPROTO_UDP);
- npfctl_bpf_endgroup(bc, false);
+ npfctl_bpf_group_exit(bc, false);
}
npfctl_build_vars(bc, family, apfrom->ap_portrange, MATCH_SRC);
npfctl_build_vars(bc, family, apto->ap_portrange, MATCH_DST);
diff -r 43c9bc7b2cd3 -r c0e7aa8d741a usr.sbin/npf/npfctl/npfctl.h
--- a/usr.sbin/npf/npfctl/npfctl.h Thu Aug 08 21:14:12 2019 +0000
+++ b/usr.sbin/npf/npfctl/npfctl.h Thu Aug 08 21:29:15 2019 +0000
@@ -167,8 +167,8 @@
const void * npfctl_bpf_bmarks(npf_bpf_t *, size_t *);
void npfctl_bpf_destroy(npf_bpf_t *);
-void npfctl_bpf_group(npf_bpf_t *);
-void npfctl_bpf_endgroup(npf_bpf_t *, bool);
+void npfctl_bpf_group_enter(npf_bpf_t *);
+void npfctl_bpf_group_exit(npf_bpf_t *, bool);
void npfctl_bpf_proto(npf_bpf_t *, sa_family_t, int);
void npfctl_bpf_cidr(npf_bpf_t *, u_int, sa_family_t,
Home |
Main Index |
Thread Index |
Old Index