summaryrefslogtreecommitdiff
path: root/usr.sbin
diff options
context:
space:
mode:
authormartin <martin@NetBSD.org>2020-06-20 15:46:47 +0000
committermartin <martin@NetBSD.org>2020-06-20 15:46:47 +0000
commitb19d58aad73c1adb8001858747a19d45bc8be0f9 (patch)
tree9fe3f3431c7c5bed82411881629567d181b19097 /usr.sbin
parent7d79e88db64cfbb1537b720c0a32d0c76aafaaa6 (diff)
Pull up following revision(s) (requested by rmind in ticket #956):
usr.sbin/npf/npf-params.7: revision 1.4 sys/net/npf/npf_worker.c: revision 1.9 usr.sbin/npf/npftest/npftest.h: revision 1.17 usr.sbin/npf/npfctl/npf_bpf_comp.c: revision 1.16 usr.sbin/npf/npf-params.7: revision 1.5 sys/net/npf/npf_state_tcp.c: revision 1.21 usr.sbin/npf/npfctl/npf_build.c: revision 1.55 usr.sbin/npf/npf-params.7: revision 1.6 sys/net/npf/npfkern.h: revision 1.5 lib/libnpf/npf.c: revision 1.49 usr.sbin/npf/npf-params.7: revision 1.7 sys/net/npf/npf_impl.h: revision 1.81 sys/net/npf/npf_ext_log.c: revision 1.17 usr.sbin/npf/npfctl/npfctl.h: revision 1.53 usr.sbin/npf/npftest/libnpftest/npf_mbuf_subr.c: revision 1.11 sys/net/npf/npf_nat.c: revision 1.50 sys/net/npf/npf_mbuf.c: revision 1.24 sys/net/npf/npf_alg.c: revision 1.22 usr.sbin/npf/npftest/libnpftest/npf_nat_test.c: revision 1.14 usr.sbin/npf/npftest/libnpftest/npf_conn_test.c: file removal usr.sbin/npf/npftest/libnpftest/npf_state_test.c: revision 1.10 sys/net/npf/npf.h: revision 1.63 usr.sbin/npf/npftest/libnpftest/npf_test.h: revision 1.21 usr.sbin/npf/npfctl/npf_var.c: revision 1.13 sys/net/npf/files.npf: revision 1.23 usr.sbin/npf/npfctl/npf_show.c: revision 1.32 usr.sbin/npf/npfctl/npf.conf.5: revision 1.91 sys/net/npf/npf_os.c: revision 1.18 sys/net/npf/npf_connkey.c: revision 1.2 sys/net/npf/npf_conf.c: revision 1.17 lib/libnpf/libnpf.3: revision 1.12 usr.sbin/npf/npftest/npftest.c: revision 1.25 usr.sbin/npf/npftest/libnpftest/npf_gc_test.c: revision 1.1 usr.sbin/npf/npfctl/npf_parse.y: revision 1.51 sys/net/npf/npf_tableset.c: revision 1.35 usr.sbin/npf/npftest/npftest.conf: revision 1.9 sys/net/npf/npf_sendpkt.c: revision 1.22 usr.sbin/npf/npfctl/npf_var.h: revision 1.10 sys/net/npf/npf_state.c: revision 1.23 sys/net/npf/npf_conn.h: revision 1.20 usr.sbin/npf/npfctl/npfctl.c: revision 1.64 usr.sbin/npf/npfctl/npf_cmd.c: revision 1.1 sys/net/npf/npf_portmap.c: revision 1.5 sys/net/npf/npf_params.c: revision 1.3 usr.sbin/npf/npfctl/npf_scan.l: revision 1.32 tests/net/npf/t_npf.sh: revision 1.4 sys/net/npf/npf_ext_rndblock.c: revision 1.9 lib/libnpf/npf.h: revision 1.39 sys/net/npf/npf_ruleset.c: revision 1.51 sys/net/npf/npf_alg_icmp.c: revision 1.33 sys/net/npf/npf.c: revision 1.43 usr.sbin/npf/npftest/libnpftest/npf_test_subr.c: revision 1.17 usr.sbin/npf/npfctl/npfctl.8: revision 1.25 sys/net/npf/npf_ctl.c: revision 1.60 usr.sbin/npf/npftest/libnpftest/npf_test_subr.c: revision 1.18 usr.sbin/npf/npftest/libnpftest/Makefile: revision 1.11 sys/net/npf/npf_handler.c: revision 1.49 sys/net/npf/npf_inet.c: revision 1.57 sys/net/npf/npf_ifaddr.c: revision 1.7 sys/net/npf/npf_conndb.c: revision 1.9 sys/net/npf/npf_if.c: revision 1.13 usr.sbin/npf/npfctl/Makefile: revision 1.15 sys/net/npf/npf_conn.c: revision 1.32 sys/net/npf/npf_ext_normalize.c: revision 1.10 sys/net/npf/npf_rproc.c: revision 1.20 sys/net/npf/npf_worker.c: revision 1.8 Major NPF improvements (merge from upstream): - Switch to the C11-style atomic primitives using atomic_loadstore(9). - npfkern: introduce the 'state.key.interface' and 'state.key.direction' settings. Users can now choose whether the connection state should be strictly per-interface or global at the configuration level. Keep NAT logic to be always per-interface, though. - npfkern: rewrite the G/C worker logic and make it self-tuning. - npfkern and libnpf: multiple bug fixes; add param exporting; introduce more parameters. Remove npf_nvlist_{copyin,copyout}() functions and refactor npfctl_load_nvlist() with others; add npfctl_run_op() to have a single entry point for operations. Introduce npf_flow_t and clean up some code. - npfctl: lots of fixes for the 'npfctl show' logic; make 'npfctl list' more informative; misc usability improvements and more user-friendly error messages. - Amend and improve the manual pages. npf_worker_sys{init,fini}: initialize/destroy the exit_cv condvar. npftest -- npf_test_init(): add a workaround for NetBSD. npf-params(7): fix the state.key defaults. npf-params.7: s/filer/filter/ Adjust to "npfctl debug" command line changes, from rmind@. Use more markup.
Diffstat (limited to 'usr.sbin')
-rw-r--r--usr.sbin/npf/npf-params.754
-rw-r--r--usr.sbin/npf/npfctl/Makefile6
-rw-r--r--usr.sbin/npf/npfctl/npf.conf.554
-rw-r--r--usr.sbin/npf/npfctl/npf_bpf_comp.c148
-rw-r--r--usr.sbin/npf/npfctl/npf_build.c269
-rw-r--r--usr.sbin/npf/npfctl/npf_cmd.c540
-rw-r--r--usr.sbin/npf/npfctl/npf_parse.y129
-rw-r--r--usr.sbin/npf/npfctl/npf_scan.l8
-rw-r--r--usr.sbin/npf/npfctl/npf_show.c398
-rw-r--r--usr.sbin/npf/npfctl/npf_var.c120
-rw-r--r--usr.sbin/npf/npfctl/npf_var.h12
-rw-r--r--usr.sbin/npf/npfctl/npfctl.838
-rw-r--r--usr.sbin/npf/npfctl/npfctl.c647
-rw-r--r--usr.sbin/npf/npfctl/npfctl.h38
-rw-r--r--usr.sbin/npf/npftest/libnpftest/Makefile4
-rw-r--r--usr.sbin/npf/npftest/libnpftest/npf_gc_test.c (renamed from usr.sbin/npf/npftest/libnpftest/npf_conn_test.c)104
-rw-r--r--usr.sbin/npf/npftest/libnpftest/npf_mbuf_subr.c6
-rw-r--r--usr.sbin/npf/npftest/libnpftest/npf_nat_test.c9
-rw-r--r--usr.sbin/npf/npftest/libnpftest/npf_state_test.c3
-rw-r--r--usr.sbin/npf/npftest/libnpftest/npf_test.h8
-rw-r--r--usr.sbin/npf/npftest/libnpftest/npf_test_subr.c59
-rw-r--r--usr.sbin/npf/npftest/npftest.c10
-rw-r--r--usr.sbin/npf/npftest/npftest.conf5
-rw-r--r--usr.sbin/npf/npftest/npftest.h6
24 files changed, 1659 insertions, 1016 deletions
diff --git a/usr.sbin/npf/npf-params.7 b/usr.sbin/npf/npf-params.7
index 8e59dc554fe..5ab6be9c0c7 100644
--- a/usr.sbin/npf/npf-params.7
+++ b/usr.sbin/npf/npf-params.7
@@ -1,4 +1,4 @@
-.\" $NetBSD: npf-params.7,v 1.2.2.1 2019/08/13 14:35:55 martin Exp $
+.\" $NetBSD: npf-params.7,v 1.2.2.2 2020/06/20 15:46:47 martin Exp $
.\"
.\" Copyright (c) 2019 Mindaugas Rasiukevicius <rmind at netbsd org>
.\" All rights reserved.
@@ -24,7 +24,7 @@
.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
.\" SUCH DAMAGE.
.\"
-.Dd August 3, 2019
+.Dd May 31, 2020
.Dt NPF-PARAMS 7
.Os
.Sh NAME
@@ -33,7 +33,9 @@
.Sh DESCRIPTION
NPF supports a set of dynamically tunable parameters.
.Pp
-All parameter values are integers and should generally be between and INT_MAX,
+All parameter values are integers and should generally be between
+zero and
+.Dv INT_MAX ,
unless specified otherwise.
Some parameters values can be negative; such values would typically
have a special meaning.
@@ -55,11 +57,49 @@ Default: 1.
.El
.\" ---
.Bl -tag -width "123456"
+.It Li ip4.reassembly
+Perform IPv4 reassembly before inspecting the packet.
+Fragmentation is considered very harmful, so most networks are expected
+to prevent it; therefore, reassembly is disabled by default.
+However, while the packet should generally be reassembled at the receiver,
+reassembly by the packet filter might be necessary in order to perform state
+tracking.
+Default: 0.
+.It Li ip6.reassembly
+Perform IPv6 reassembly before inspecting the packet.
+Discouraged in general but not prohibited by RFC 8200.
+Default: 0.
+.El
+.\" ---
+.Bl -tag -width "123456"
.It Li gc.step
Number of connection state items to process in one garbage collection
(G/C) cycle.
Must be positive number.
Default: 256.
+.It Li gc.interval_min
+The lower bound for the sleep time of the G/C worker.
+The worker is self-tuning and will wake up more frequently if there are
+connections to expire; it will wake up less frequently, diverging towards
+the upper bound, if it does not encounter expired connections.
+Default: 50 (in milliseconds).
+.It Li gc.interval_min
+The upper bound for the sleep time of the G/C worker.
+Default: 5000 (in milliseconds).
+.El
+.\" ---
+.It Li state.key
+The connection state is uniquely identified by an n-tuple.
+The state behavior can be controlled by including (excluding)
+some of the information in (from) the keys.
+.Bl -tag -width "123456"
+.It Li interface
+Include interface identifier into the keys, making the connection
+state strictly per-interface.
+Default: 1.
+.It Li direction
+Include packet direction into the keys.
+Default: 1.
.El
.\" ---
.It Li state.generic
@@ -106,12 +146,12 @@ Default: 240.
.El
.\" ---
.It Li portmap.min_port
-Lower bound of the port range used when selecting the port for dynamic NAT
-with port translation enabled.
-Default: 1024 (also the lowest allowed value).
+Lower bound of the port range used when selecting the port
+for dynamic NAT with port translation enabled.
+Default: 1024 (inclusive; also the lowest allowed value).
.It Li portmap.max_port
Upper bound of the port range as described above.
-Default: 65535 (also the highest allowed value).
+Default: 49151 (inclusive; 65535 is the highest allowed value).
.\" ---
.El
.\" -----
diff --git a/usr.sbin/npf/npfctl/Makefile b/usr.sbin/npf/npfctl/Makefile
index 9a73fef4b10..d34486867a7 100644
--- a/usr.sbin/npf/npfctl/Makefile
+++ b/usr.sbin/npf/npfctl/Makefile
@@ -1,4 +1,4 @@
-# $NetBSD: Makefile,v 1.13.2.1 2019/09/01 13:18:40 martin Exp $
+# $NetBSD: Makefile,v 1.13.2.2 2020/06/20 15:46:48 martin Exp $
.include <bsd.own.mk>
@@ -6,8 +6,8 @@ PROG= npfctl
MAN= npfctl.8 npf.conf.5
BINDIR= /sbin
-SRCS= npfctl.c npf_var.c npf_data.c npf_build.c npf_extmod.c
-SRCS+= npf_bpf_comp.c npf_show.c
+SRCS= npfctl.c npf_cmd.c npf_var.c npf_data.c npf_build.c
+SRCS+= npf_bpf_comp.c npf_show.c npf_extmod.c
CPPFLAGS+= -I${.CURDIR}
SRCS+= npf_scan.l npf_parse.y
diff --git a/usr.sbin/npf/npfctl/npf.conf.5 b/usr.sbin/npf/npfctl/npf.conf.5
index 984126f0d8f..c51a0bd70d4 100644
--- a/usr.sbin/npf/npfctl/npf.conf.5
+++ b/usr.sbin/npf/npfctl/npf.conf.5
@@ -1,6 +1,6 @@
-.\" $NetBSD: npf.conf.5,v 1.88.2.1 2019/10/04 08:06:34 martin Exp $
+.\" $NetBSD: npf.conf.5,v 1.88.2.2 2020/06/20 15:46:48 martin Exp $
.\"
-.\" Copyright (c) 2009-2019 The NetBSD Foundation, Inc.
+.\" Copyright (c) 2009-2020 The NetBSD Foundation, Inc.
.\" All rights reserved.
.\"
.\" This material is based upon work partially supported by The
@@ -27,7 +27,7 @@
.\" ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
.\" POSSIBILITY OF SUCH DAMAGE.
.\"
-.Dd August 25, 2019
+.Dd May 19, 2020
.Dt NPF.CONF 5
.Os
.Sh NAME
@@ -271,6 +271,17 @@ Return an ICMP UNREACHABLE message, when the packet being blocked is a UDP packe
Applies to IPv4 and IPv6.
.El
.Pp
+The
+.Cm from
+and
+.Cm to
+keywords are provided to filter by source or destination IP addresses.
+They can be used in conjunction with the
+.Cm port
+keyword.
+Negation (the exclamation mark) can be used in front of the address
+filter criteria.
+.Pp
Further packet specification at present is limited to TCP and UDP
understanding source and destination ports, and ICMP and IPv6-ICMP
understanding icmp-type.
@@ -296,15 +307,32 @@ syntax, for example:
Fragments are not selectable since NPF always reassembles packets
before further processing.
.Ss Stateful
+NPF supports stateful packet inspection which can be used to bypass
+unnecessary rule processing as well as to complement NAT.
+The connection state is uniquely identified by an n-tuple: IP version,
+layer 4 protocol, source and destination IP addresses and port numbers.
+Each state is represented by two keys: one for the original flow and
+one for the reverse flow, so that the reverse lookup on the returning
+packets would succeed.
+The packets are matched against the connection direction respectively.
+.Pp
+Depending on the settings (see the section on
+.Li state.key
+in the
+.Xr npf-params 7
+manual), the connection identifier (keys) may also include the interface ID,
+making the states per-interface.
+.Pp
Stateful packet inspection is enabled using the
.Cm stateful
or
.Cm stateful-all
keywords.
-The former creates a state which is uniquely identified by a 5-tuple (source
-and destination IP addresses, port numbers and an interface identifier).
-The latter excludes the interface identifier, i.e. making the state global,
-and must be used with caution.
+The former matches the interface after the state lookup, while the latter
+avoids matching the interface (assuming the
+.Li state.key.interface
+parameter is disabled),
+i.e. making the state global, and must be used with caution.
In both cases, a full TCP state tracking is performed for TCP connections
and a limited tracking for message-based protocols (UDP and ICMP).
.Pp
@@ -486,10 +514,15 @@ procedure "someproc" {
.Pp
In this case, the procedure calls the logging and normalization modules.
.Ss Parameter settings
-NPF supports a set of dynamically tunable parameters.
+NPF supports a set of dynamically tunable configuration-wide parameters.
+For example:
+.Bd -literal -offset indent
+set state.tcp.timeout.time_wait 0 # destroy the state immediately
+.Ed
+.Pp
See
.Xr npf-params 7
-for specific details.
+for the list of parameters and their details.
.Ss Misc
Text after a hash
.Pq Sq #
@@ -580,8 +613,7 @@ proto-opts = "flags" tcp-flags [ "/" tcp-flag-mask ] |
"icmp-type" type [ "code" icmp-code ]
proto = "proto" protocol [ proto-opts ]
-filt-opts = "from" filt-addr [ port-opts ] "to" filt-addr
- [ port-opts ]
+filt-opts = "from" filt-addr [ port-opts ] "to" filt-addr [ port-opts ]
filt-addr = [ "!" ] [ interface | addr-mask | table-id | "any" ]
port-opts = "port" ( port-num | port-from "-" port-to | var-name )
diff --git a/usr.sbin/npf/npfctl/npf_bpf_comp.c b/usr.sbin/npf/npfctl/npf_bpf_comp.c
index 297b87ec0b6..37a78908f83 100644
--- a/usr.sbin/npf/npfctl/npf_bpf_comp.c
+++ b/usr.sbin/npf/npfctl/npf_bpf_comp.c
@@ -1,5 +1,5 @@
/*-
- * Copyright (c) 2010-2019 The NetBSD Foundation, Inc.
+ * Copyright (c) 2010-2020 The NetBSD Foundation, Inc.
* All rights reserved.
*
* This material is based upon work partially supported by The
@@ -69,7 +69,7 @@
*
* Grouping
*
- * Filters can have groups, which have a meaning of logical
+ * Filters can have groups, which have an effect of logical
* disjunction, e.g.:
*
* A and B and (C or D)
@@ -82,7 +82,7 @@
*/
#include <sys/cdefs.h>
-__RCSID("$NetBSD: npf_bpf_comp.c,v 1.13.2.2 2019/09/01 13:21:39 martin Exp $");
+__RCSID("$NetBSD: npf_bpf_comp.c,v 1.13.2.3 2020/06/20 15:46:48 martin Exp $");
#include <stdlib.h>
#include <stdbool.h>
@@ -111,7 +111,7 @@ __RCSID("$NetBSD: npf_bpf_comp.c,v 1.13.2.2 2019/09/01 13:21:39 martin Exp $");
* something other than L4 header offset. Generally, when BPF_LDX is used.
*/
#define FETCHED_L3 0x01
-#define CHECKED_L4 0x02
+#define CHECKED_L4_PROTO 0x02
#define X_EQ_L4OFF 0x04
struct npf_bpf {
@@ -121,17 +121,24 @@ struct npf_bpf {
*/
struct bpf_program prog;
size_t alen;
- u_int nblocks;
+ unsigned nblocks;
sa_family_t af;
uint32_t flags;
/*
+ * Indicators whether we are inside the group and whether this
+ * group is implementing inverted logic.
+ *
* 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;
+ unsigned ingroup;
+ bool invert;
+ unsigned goff;
+ unsigned gblock;
+
+ /* Track inversion (excl. mark). */
+ uint32_t invflags;
/* BPF marks, allocated length and the real length. */
uint32_t * marks;
@@ -239,7 +246,7 @@ add_insns(npf_bpf_t *ctx, struct bpf_insn *insns, size_t count)
}
static void
-done_raw_block(npf_bpf_t *ctx, const uint32_t *m, size_t len)
+add_bmarks(npf_bpf_t *ctx, const uint32_t *m, size_t len)
{
size_t reqlen, nargs = m[1];
@@ -258,7 +265,7 @@ done_raw_block(npf_bpf_t *ctx, const uint32_t *m, size_t len)
static void
done_block(npf_bpf_t *ctx, const uint32_t *m, size_t len)
{
- done_raw_block(ctx, m, len);
+ add_bmarks(ctx, m, len);
ctx->nblocks++;
}
@@ -302,10 +309,10 @@ npfctl_bpf_destroy(npf_bpf_t *ctx)
/*
* npfctl_bpf_group_enter: begin a logical group. It merely uses logical
- * disjunction (OR) for compares within the group.
+ * disjunction (OR) for comparisons within the group.
*/
void
-npfctl_bpf_group_enter(npf_bpf_t *ctx)
+npfctl_bpf_group_enter(npf_bpf_t *ctx, bool invert)
{
struct bpf_program *bp = &ctx->prog;
@@ -314,17 +321,21 @@ npfctl_bpf_group_enter(npf_bpf_t *ctx)
ctx->goff = bp->bf_len;
ctx->gblock = ctx->nblocks;
- ctx->ingroup = true;
+ ctx->invert = invert;
+ ctx->ingroup++;
}
void
-npfctl_bpf_group_exit(npf_bpf_t *ctx, bool invert)
+npfctl_bpf_group_exit(npf_bpf_t *ctx)
{
struct bpf_program *bp = &ctx->prog;
const size_t curoff = bp->bf_len;
+ assert(ctx->ingroup);
+ ctx->ingroup--;
+
/* If there are no blocks or only one - nothing to do. */
- if (!invert && (ctx->nblocks - ctx->gblock) <= 1) {
+ if (!ctx->invert && (ctx->nblocks - ctx->gblock) <= 1) {
ctx->goff = ctx->gblock = 0;
return;
}
@@ -333,7 +344,7 @@ npfctl_bpf_group_exit(npf_bpf_t *ctx, bool invert)
* If inverting, then prepend a jump over the statement below.
* On match, it will skip-through and the fail path will be taken.
*/
- if (invert) {
+ if (ctx->invert) {
struct bpf_insn insns_ret[] = {
BPF_STMT(BPF_JMP+BPF_JA, 1),
};
@@ -359,9 +370,9 @@ npfctl_bpf_group_exit(npf_bpf_t *ctx, bool invert)
}
static void
-fetch_l3(npf_bpf_t *ctx, sa_family_t af, u_int flags)
+fetch_l3(npf_bpf_t *ctx, sa_family_t af, unsigned flags)
{
- u_int ver;
+ unsigned ver;
switch (af) {
case AF_INET:
@@ -386,7 +397,8 @@ fetch_l3(npf_bpf_t *ctx, sa_family_t af, u_int flags)
if ((ctx->flags & FETCHED_L3) == 0 || (af && ctx->af == 0)) {
const uint8_t jt = ver ? 0 : JUMP_MAGIC;
const uint8_t jf = ver ? JUMP_MAGIC : 0;
- bool ingroup = ctx->ingroup;
+ const bool ingroup = ctx->ingroup != 0;
+ const bool invert = ctx->invert;
/*
* L3 block cannot be inserted in the middle of a group.
@@ -394,7 +406,7 @@ fetch_l3(npf_bpf_t *ctx, sa_family_t af, u_int flags)
*/
if (ingroup) {
assert(ctx->nblocks == ctx->gblock);
- npfctl_bpf_group_exit(ctx, false);
+ npfctl_bpf_group_exit(ctx);
}
/*
@@ -411,10 +423,10 @@ fetch_l3(npf_bpf_t *ctx, sa_family_t af, u_int flags)
if (af) {
uint32_t mwords[] = { BM_IPVER, 1, af };
- done_raw_block(ctx, mwords, sizeof(mwords));
+ add_bmarks(ctx, mwords, sizeof(mwords));
}
if (ingroup) {
- npfctl_bpf_group_enter(ctx);
+ npfctl_bpf_group_enter(ctx, invert);
}
} else if (af && af != ctx->af) {
@@ -431,20 +443,43 @@ fetch_l3(npf_bpf_t *ctx, sa_family_t af, u_int flags)
}
}
+static void
+bm_invert_checkpoint(npf_bpf_t *ctx, const unsigned opts)
+{
+ uint32_t bm = 0;
+
+ if (ctx->ingroup && ctx->invert) {
+ const unsigned seen = ctx->invflags;
+
+ if ((opts & MATCH_SRC) != 0 && (seen & MATCH_SRC) == 0) {
+ bm = BM_SRC_NEG;
+ }
+ if ((opts & MATCH_DST) != 0 && (seen & MATCH_DST) == 0) {
+ bm = BM_DST_NEG;
+ }
+ ctx->invflags |= opts & (MATCH_SRC | MATCH_DST);
+ }
+ if (bm) {
+ uint32_t mwords[] = { bm, 0 };
+ add_bmarks(ctx, mwords, sizeof(mwords));
+ }
+}
+
/*
- * npfctl_bpf_proto: code block to match IP version and L4 protocol.
+ * npfctl_bpf_ipver: match the IP version.
*/
void
-npfctl_bpf_proto(npf_bpf_t *ctx, sa_family_t af, int proto)
+npfctl_bpf_ipver(npf_bpf_t *ctx, sa_family_t af)
{
- assert(af != AF_UNSPEC || proto != -1);
-
- /* Note: fails if IP version does not match. */
fetch_l3(ctx, af, 0);
- if (proto == -1) {
- return;
- }
+}
+/*
+ * npfctl_bpf_proto: code block to match IP version and L4 protocol.
+ */
+void
+npfctl_bpf_proto(npf_bpf_t *ctx, unsigned proto)
+{
struct bpf_insn insns_proto[] = {
/* A <- L4 protocol; A == expected-protocol? */
BPF_STMT(BPF_LD+BPF_W+BPF_MEM, BPF_MW_L4PROTO),
@@ -454,7 +489,7 @@ npfctl_bpf_proto(npf_bpf_t *ctx, sa_family_t af, int proto)
uint32_t mwords[] = { BM_PROTO, 1, proto };
done_block(ctx, mwords, sizeof(mwords));
- ctx->flags |= CHECKED_L4;
+ ctx->flags |= CHECKED_L4_PROTO;
}
/*
@@ -463,11 +498,11 @@ npfctl_bpf_proto(npf_bpf_t *ctx, sa_family_t af, int proto)
* => IP address shall be in the network byte order.
*/
void
-npfctl_bpf_cidr(npf_bpf_t *ctx, u_int opts, sa_family_t af,
+npfctl_bpf_cidr(npf_bpf_t *ctx, unsigned opts, sa_family_t af,
const npf_addr_t *addr, const npf_netmask_t mask)
{
const uint32_t *awords = (const uint32_t *)addr;
- u_int nwords, length, maxmask, off;
+ unsigned nwords, length, maxmask, off;
assert(((opts & MATCH_SRC) != 0) ^ ((opts & MATCH_DST) != 0));
assert((mask && mask <= NPF_MAX_NETMASK) || mask == NPF_NO_NETMASK);
@@ -497,8 +532,8 @@ npfctl_bpf_cidr(npf_bpf_t *ctx, u_int opts, sa_family_t af,
length = (mask == NPF_NO_NETMASK) ? maxmask : mask;
/* CAUTION: BPF operates in host byte-order. */
- for (u_int i = 0; i < nwords; i++) {
- const u_int woff = i * sizeof(uint32_t);
+ for (unsigned i = 0; i < nwords; i++) {
+ const unsigned woff = i * sizeof(uint32_t);
uint32_t word = ntohl(awords[i]);
uint32_t wordmask;
@@ -539,6 +574,7 @@ npfctl_bpf_cidr(npf_bpf_t *ctx, u_int opts, sa_family_t af,
(opts & MATCH_SRC) ? BM_SRC_CIDR: BM_DST_CIDR, 6,
af, mask, awords[0], awords[1], awords[2], awords[3],
};
+ bm_invert_checkpoint(ctx, opts);
done_block(ctx, mwords, sizeof(mwords));
}
@@ -548,16 +584,16 @@ npfctl_bpf_cidr(npf_bpf_t *ctx, u_int opts, sa_family_t af,
* => Port numbers shall be in the network byte order.
*/
void
-npfctl_bpf_ports(npf_bpf_t *ctx, u_int opts, in_port_t from, in_port_t to)
+npfctl_bpf_ports(npf_bpf_t *ctx, unsigned opts, in_port_t from, in_port_t to)
{
- const u_int sport_off = offsetof(struct udphdr, uh_sport);
- const u_int dport_off = offsetof(struct udphdr, uh_dport);
- u_int off;
+ const unsigned sport_off = offsetof(struct udphdr, uh_sport);
+ const unsigned dport_off = offsetof(struct udphdr, uh_dport);
+ unsigned off;
/* TCP and UDP port offsets are the same. */
assert(sport_off == offsetof(struct tcphdr, th_sport));
assert(dport_off == offsetof(struct tcphdr, th_dport));
- assert(ctx->flags & CHECKED_L4);
+ assert(ctx->flags & CHECKED_L4_PROTO);
assert(((opts & MATCH_SRC) != 0) ^ ((opts & MATCH_DST) != 0));
off = (opts & MATCH_SRC) ? sport_off : dport_off;
@@ -592,7 +628,7 @@ npfctl_bpf_ports(npf_bpf_t *ctx, u_int opts, in_port_t from, in_port_t to)
}
uint32_t mwords[] = {
- opts & MATCH_SRC ? BM_SRC_PORTS : BM_DST_PORTS, 2, from, to
+ (opts & MATCH_SRC) ? BM_SRC_PORTS : BM_DST_PORTS, 2, from, to
};
done_block(ctx, mwords, sizeof(mwords));
}
@@ -601,25 +637,30 @@ npfctl_bpf_ports(npf_bpf_t *ctx, u_int opts, in_port_t from, in_port_t to)
* npfctl_bpf_tcpfl: code block to match TCP flags.
*/
void
-npfctl_bpf_tcpfl(npf_bpf_t *ctx, uint8_t tf, uint8_t tf_mask, bool checktcp)
+npfctl_bpf_tcpfl(npf_bpf_t *ctx, uint8_t tf, uint8_t tf_mask)
{
- const u_int tcpfl_off = offsetof(struct tcphdr, th_flags);
+ const unsigned tcpfl_off = offsetof(struct tcphdr, th_flags);
const bool usingmask = tf_mask != tf;
/* X <- IP header length */
fetch_l3(ctx, AF_UNSPEC, X_EQ_L4OFF);
- if (checktcp) {
- const u_int jf = usingmask ? 3 : 2;
- assert(ctx->ingroup == false);
- /* A <- L4 protocol; A == TCP? If not, jump out. */
+ if ((ctx->flags & CHECKED_L4_PROTO) == 0) {
+ const unsigned jf = usingmask ? 3 : 2;
+ assert(ctx->ingroup == 0);
+
+ /*
+ * A <- L4 protocol; A == TCP? If not, jump out.
+ *
+ * Note: the TCP flag matching might be without 'proto tcp'
+ * when using a plain 'stateful' rule. In such case it also
+ * handles other protocols, thus no strict TCP check.
+ */
struct bpf_insn insns_tcp[] = {
BPF_STMT(BPF_LD+BPF_W+BPF_MEM, BPF_MW_L4PROTO),
BPF_JUMP(BPF_JMP+BPF_JEQ+BPF_K, IPPROTO_TCP, 0, jf),
};
add_insns(ctx, insns_tcp, __arraycount(insns_tcp));
- } else {
- assert(ctx->flags & CHECKED_L4);
}
struct bpf_insn insns_tf[] = {
@@ -642,13 +683,13 @@ npfctl_bpf_tcpfl(npf_bpf_t *ctx, uint8_t tf, uint8_t tf_mask, bool checktcp)
};
add_insns(ctx, insns_cmp, __arraycount(insns_cmp));
- uint32_t mwords[] = { BM_TCPFL, 2, tf, tf_mask};
+ uint32_t mwords[] = { BM_TCPFL, 2, tf, tf_mask };
done_block(ctx, mwords, sizeof(mwords));
}
/*
* npfctl_bpf_icmp: code block to match ICMP type and/or code.
- * Note: suitable both for the ICMPv4 and ICMPv6.
+ * Note: suitable for both the ICMPv4 and ICMPv6.
*/
void
npfctl_bpf_icmp(npf_bpf_t *ctx, int type, int code)
@@ -656,7 +697,7 @@ npfctl_bpf_icmp(npf_bpf_t *ctx, int type, int code)
const u_int type_off = offsetof(struct icmp, icmp_type);
const u_int code_off = offsetof(struct icmp, icmp_code);
- assert(ctx->flags & CHECKED_L4);
+ assert(ctx->flags & CHECKED_L4_PROTO);
assert(offsetof(struct icmp6_hdr, icmp6_type) == type_off);
assert(offsetof(struct icmp6_hdr, icmp6_code) == code_off);
assert(type != -1 || code != -1);
@@ -694,7 +735,7 @@ npfctl_bpf_icmp(npf_bpf_t *ctx, int type, int code)
* against NPF table specified by ID.
*/
void
-npfctl_bpf_table(npf_bpf_t *ctx, u_int opts, u_int tid)
+npfctl_bpf_table(npf_bpf_t *ctx, unsigned opts, unsigned tid)
{
const bool src = (opts & MATCH_SRC) != 0;
@@ -706,5 +747,6 @@ npfctl_bpf_table(npf_bpf_t *ctx, u_int opts, u_int tid)
add_insns(ctx, insns_table, __arraycount(insns_table));
uint32_t mwords[] = { src ? BM_SRC_TABLE: BM_DST_TABLE, 1, tid };
+ bm_invert_checkpoint(ctx, opts);
done_block(ctx, mwords, sizeof(mwords));
}
diff --git a/usr.sbin/npf/npfctl/npf_build.c b/usr.sbin/npf/npfctl/npf_build.c
index 8bef46053ec..fe15f80028e 100644
--- a/usr.sbin/npf/npfctl/npf_build.c
+++ b/usr.sbin/npf/npfctl/npf_build.c
@@ -1,5 +1,5 @@
/*-
- * Copyright (c) 2011-2019 The NetBSD Foundation, Inc.
+ * Copyright (c) 2011-2020 The NetBSD Foundation, Inc.
* All rights reserved.
*
* This material is based upon work partially supported by The
@@ -32,7 +32,7 @@
*/
#include <sys/cdefs.h>
-__RCSID("$NetBSD: npf_build.c,v 1.50.2.3 2020/05/25 17:25:28 martin Exp $");
+__RCSID("$NetBSD: npf_build.c,v 1.50.2.4 2020/06/20 15:46:48 martin Exp $");
#include <sys/types.h>
#define __FAVOR_BSD
@@ -70,13 +70,25 @@ npfctl_config_init(bool debug)
{
npf_conf = npf_config_create();
if (npf_conf == NULL) {
- errx(EXIT_FAILURE, "npf_config_create failed");
+ errx(EXIT_FAILURE, "npf_config_create() failed");
}
memset(current_group, 0, sizeof(current_group));
npf_debug = debug;
npf_conf_built = false;
}
+nl_config_t *
+npfctl_config_ref(void)
+{
+ return npf_conf;
+}
+
+nl_rule_t *
+npfctl_rule_ref(void)
+{
+ return the_rule;
+}
+
void
npfctl_config_build(void)
{
@@ -107,10 +119,6 @@ npfctl_config_send(int fd)
npfctl_config_build();
error = npf_config_submit(npf_conf, fd, &errinfo);
- if (error == EEXIST) { /* XXX */
- errx(EXIT_FAILURE, "(re)load failed: "
- "some table has a duplicate entry?");
- }
if (error) {
npfctl_print_error(&errinfo);
}
@@ -126,10 +134,12 @@ npfctl_config_save(nl_config_t *ncf, const char *outfile)
int fd;
blob = npf_config_export(ncf, &len);
- if (!blob)
+ if (!blob) {
err(EXIT_FAILURE, "npf_config_export");
- if ((fd = open(outfile, O_CREAT | O_TRUNC | O_WRONLY, 0644)) == -1)
+ }
+ if ((fd = open(outfile, O_CREAT | O_TRUNC | O_WRONLY, 0644)) == -1) {
err(EXIT_FAILURE, "could not open %s", outfile);
+ }
if (write(fd, blob, len) != (ssize_t)len) {
err(EXIT_FAILURE, "write to %s failed", outfile);
}
@@ -137,31 +147,6 @@ npfctl_config_save(nl_config_t *ncf, const char *outfile)
close(fd);
}
-void
-npfctl_config_debug(const char *outfile)
-{
- npfctl_config_build();
-
- printf("\nConfiguration:\n\n");
- _npf_config_dump(npf_conf, STDOUT_FILENO);
-
- printf("\nSaving binary to %s\n", outfile);
- npfctl_config_save(npf_conf, outfile);
- npf_config_destroy(npf_conf);
-}
-
-nl_config_t *
-npfctl_config_ref(void)
-{
- return npf_conf;
-}
-
-nl_rule_t *
-npfctl_rule_ref(void)
-{
- return the_rule;
-}
-
bool
npfctl_debug_addif(const char *ifname)
{
@@ -269,7 +254,7 @@ npfctl_get_singletable(const npfvar_t *vp)
unsigned *tid;
if (npfvar_get_count(vp) > 1) {
- yyerror("multiple tables are not valid");
+ yyerror("invalid use of multiple tables");
}
tid = npfvar_get_data(vp, NPFVAR_TABLE, 0);
assert(tid != NULL);
@@ -278,7 +263,7 @@ npfctl_get_singletable(const npfvar_t *vp)
static bool
npfctl_build_fam(npf_bpf_t *ctx, sa_family_t family,
- fam_addr_mask_t *fam, int opts)
+ fam_addr_mask_t *fam, unsigned opts)
{
/*
* If family is specified, address does not match it and the
@@ -303,9 +288,7 @@ npfctl_build_fam(npf_bpf_t *ctx, sa_family_t family,
* zero mask would never match and therefore is not valid.
*/
if (fam->fam_mask == 0) {
- static const npf_addr_t zero; /* must be static */
-
- if (memcmp(&fam->fam_addr, &zero, sizeof(npf_addr_t))) {
+ if (!npfctl_addr_iszero(&fam->fam_addr)) {
yyerror("filter criterion would never match");
}
return false;
@@ -318,12 +301,11 @@ npfctl_build_fam(npf_bpf_t *ctx, sa_family_t family,
static void
npfctl_build_vars(npf_bpf_t *ctx, sa_family_t family, npfvar_t *vars, int opts)
{
- const int type = npfvar_get_type(vars, 0);
- size_t i;
-
- npfctl_bpf_group_enter(ctx);
- for (i = 0; i < npfvar_get_count(vars); i++) {
+ npfctl_bpf_group_enter(ctx, (opts & MATCH_INVERT) != 0);
+ for (unsigned i = 0; i < npfvar_get_count(vars); i++) {
+ const unsigned type = npfvar_get_type(vars, i);
void *data = npfvar_get_data(vars, type, i);
+
assert(data != NULL);
switch (type) {
@@ -338,29 +320,32 @@ npfctl_build_vars(npf_bpf_t *ctx, sa_family_t family, npfvar_t *vars, int opts)
break;
}
case NPFVAR_TABLE: {
- u_int tid;
- memcpy(&tid, data, sizeof(u_int));
+ unsigned tid;
+ memcpy(&tid, data, sizeof(unsigned));
npfctl_bpf_table(ctx, opts, tid);
break;
}
default:
- assert(false);
+ yyerror("unexpected %s", npfvar_type(type));
}
}
- npfctl_bpf_group_exit(ctx, (opts & MATCH_INVERT) != 0);
+ npfctl_bpf_group_exit(ctx);
}
static void
-npfctl_build_proto(npf_bpf_t *ctx, sa_family_t family, const opt_proto_t *op)
+npfctl_build_proto_block(npf_bpf_t *ctx, const opt_proto_t *op, bool multiple)
{
- const npfvar_t *popts = op->op_opts;
- const int proto = op->op_proto;
+ const unsigned proto = op->op_proto;
+ npfvar_t *popts = op->op_opts;
- /* IP version and/or L4 protocol matching. */
- if (family != AF_UNSPEC || proto != -1) {
- npfctl_bpf_proto(ctx, family, proto);
+ if (multiple && popts) {
+ yyerror("multiple protocol options with protocol filters "
+ "are not yet supported");
}
+ /* Build the protocol filter. */
+ npfctl_bpf_proto(ctx, proto);
+
switch (proto) {
case IPPROTO_TCP:
/* Build TCP flags matching (optional). */
@@ -370,7 +355,7 @@ npfctl_build_proto(npf_bpf_t *ctx, sa_family_t family, const opt_proto_t *op)
assert(npfvar_get_count(popts) == 2);
tf = npfvar_get_data(popts, NPFVAR_TCPFLAG, 0);
tf_mask = npfvar_get_data(popts, NPFVAR_TCPFLAG, 1);
- npfctl_bpf_tcpfl(ctx, *tf, *tf_mask, false);
+ npfctl_bpf_tcpfl(ctx, *tf, *tf_mask);
}
break;
case IPPROTO_ICMP:
@@ -391,58 +376,110 @@ npfctl_build_proto(npf_bpf_t *ctx, sa_family_t family, const opt_proto_t *op)
}
}
+static void
+npfctl_build_proto(npf_bpf_t *ctx, const npfvar_t *vars)
+{
+ const unsigned count = npfvar_get_count(vars);
+
+ /*
+ * XXX: For now, just do not support multiple protocol
+ * blocks with options; this is because npfctl_bpf_tcpfl()
+ * and npfctl_bpf_icmp() will not work correctly in a group.
+ */
+ if (count == 1) {
+ const opt_proto_t *op = npfvar_get_data(vars, NPFVAR_PROTO, 0);
+ npfctl_build_proto_block(ctx, op, false);
+ return;
+ }
+
+ npfctl_bpf_group_enter(ctx, false);
+ for (unsigned i = 0; i < count; i++) {
+ const opt_proto_t *op = npfvar_get_data(vars, NPFVAR_PROTO, i);
+ npfctl_build_proto_block(ctx, op, true);
+ }
+ npfctl_bpf_group_exit(ctx);
+}
+
+static bool
+npfctl_check_proto(const npfvar_t *vars, bool *non_tcpudp, bool *tcp_with_nofl)
+{
+ unsigned count;
+
+ *non_tcpudp = false;
+ *tcp_with_nofl = false;
+
+ if (vars == NULL) {
+ return false;
+ }
+
+ count = npfvar_get_count(vars);
+ for (unsigned i = 0; i < count; i++) {
+ const opt_proto_t *op = npfvar_get_data(vars, NPFVAR_PROTO, i);
+
+ switch (op->op_proto) {
+ case IPPROTO_TCP:
+ *tcp_with_nofl = op->op_opts == NULL;
+ break;
+ case IPPROTO_UDP:
+ case -1:
+ break;
+ default:
+ *non_tcpudp = true;
+ break;
+ }
+ }
+ return count != 0;
+}
+
static bool
-npfctl_build_code(nl_rule_t *rl, sa_family_t family, const opt_proto_t *op,
+npfctl_build_code(nl_rule_t *rl, sa_family_t family, const npfvar_t *popts,
const filt_opts_t *fopts)
{
- bool noproto, noaddrs, noports, nostate, need_tcpudp = false;
const addr_port_t *apfrom = &fopts->fo_from;
const addr_port_t *apto = &fopts->fo_to;
- const int proto = op->op_proto;
+ bool any_proto, any_addrs, any_ports, stateful;
+ bool any_l4proto, non_tcpudp, tcp_with_nofl;
npf_bpf_t *bc;
unsigned opts;
size_t len;
- /* If none specified, then no byte-code. */
- noproto = family == AF_UNSPEC && proto == -1 && !op->op_opts;
- noaddrs = !apfrom->ap_netaddr && !apto->ap_netaddr;
- noports = !apfrom->ap_portrange && !apto->ap_portrange;
- nostate = !(npf_rule_getattr(rl) & NPF_RULE_STATEFUL);
- if (noproto && noaddrs && noports && nostate) {
+ /*
+ * Gather some information about the protocol options, if any.
+ * Check the filter criteria in general -- if none specified,
+ * then no byte-code.
+ */
+ any_l4proto = npfctl_check_proto(popts, &non_tcpudp, &tcp_with_nofl);
+ any_proto = (family != AF_UNSPEC) || any_l4proto;
+ any_addrs = apfrom->ap_netaddr || apto->ap_netaddr;
+ any_ports = apfrom->ap_portrange || apto->ap_portrange;
+ stateful = (npf_rule_getattr(rl) & NPF_RULE_STATEFUL) != 0;
+ if (!any_proto && !any_addrs && !any_ports && !stateful) {
return false;
}
/*
* Sanity check: ports can only be used with TCP or UDP protocol.
- * No filter options are supported for other protocols, only the
- * IP addresses are allowed.
*/
- if (!noports) {
- switch (proto) {
- case IPPROTO_TCP:
- case IPPROTO_UDP:
- break;
- case -1:
- need_tcpudp = true;
- break;
- default:
- yyerror("invalid filter options for protocol %d", proto);
- }
+ if (any_ports && non_tcpudp) {
+ yyerror("invalid filter options for given the protocol(s)");
}
bc = npfctl_bpf_create();
- /* Build layer 4 protocol blocks. */
- npfctl_build_proto(bc, family, op);
+ /* Build layer 3 and 4 protocol blocks. */
+ if (family != AF_UNSPEC) {
+ npfctl_bpf_ipver(bc, family);
+ }
+ if (any_l4proto) {
+ npfctl_build_proto(bc, popts);
+ }
/*
* If this is a stateful rule and TCP flags are not specified,
* then add "flags S/SAFR" filter for TCP protocol case.
*/
- if ((npf_rule_getattr(rl) & NPF_RULE_STATEFUL) != 0 &&
- (proto == -1 || (proto == IPPROTO_TCP && !op->op_opts))) {
- npfctl_bpf_tcpfl(bc, TH_SYN,
- TH_SYN | TH_ACK | TH_FIN | TH_RST, proto == -1);
+ if (stateful && (!any_l4proto || tcp_with_nofl)) {
+ npfctl_bpf_tcpfl(bc, TH_SYN, TH_SYN | TH_ACK | TH_FIN | TH_RST);
}
/* Build IP address blocks. */
@@ -451,21 +488,23 @@ npfctl_build_code(nl_rule_t *rl, sa_family_t family, const opt_proto_t *op,
opts = MATCH_DST | (fopts->fo_tinvert ? MATCH_INVERT : 0);
npfctl_build_vars(bc, family, apto->ap_netaddr, opts);
- /* Build port-range blocks. */
- if (need_tcpudp) {
- /* TCP/UDP check for the ports. */
- npfctl_bpf_group_enter(bc);
- npfctl_bpf_proto(bc, AF_UNSPEC, IPPROTO_TCP);
- npfctl_bpf_proto(bc, AF_UNSPEC, IPPROTO_UDP);
- npfctl_bpf_group_exit(bc, false);
+ /*
+ * Build the port-range blocks. If no protocol is specified,
+ * then we implicitly filter for the TCP / UDP protocols.
+ */
+ if (any_ports && !any_l4proto) {
+ npfctl_bpf_group_enter(bc, false);
+ npfctl_bpf_proto(bc, IPPROTO_TCP);
+ npfctl_bpf_proto(bc, IPPROTO_UDP);
+ npfctl_bpf_group_exit(bc);
}
npfctl_build_vars(bc, family, apfrom->ap_portrange, MATCH_SRC);
npfctl_build_vars(bc, family, apto->ap_portrange, MATCH_DST);
/* Set the byte-code marks, if any. */
const void *bmarks = npfctl_bpf_bmarks(bc, &len);
- if (npf_rule_setinfo(rl, bmarks, len) == -1) {
- errx(EXIT_FAILURE, "npf_rule_setinfo failed");
+ if (bmarks && npf_rule_setinfo(rl, bmarks, len) != 0) {
+ errx(EXIT_FAILURE, "npf_rule_setinfo");
}
/* Complete BPF byte-code and pass to the rule. */
@@ -477,7 +516,7 @@ npfctl_build_code(nl_rule_t *rl, sa_family_t family, const opt_proto_t *op,
len = bf->bf_len * sizeof(struct bpf_insn);
if (npf_rule_setcode(rl, NPF_CODE_BPF, bf->bf_insns, len) != 0) {
- errx(EXIT_FAILURE, "npf_rule_setcode failed");
+ errx(EXIT_FAILURE, "npf_rule_setcode");
}
npfctl_dump_bpf(bf);
npfctl_bpf_destroy(bc);
@@ -655,7 +694,7 @@ npfctl_build_group_end(void)
*/
void
npfctl_build_rule(uint32_t attr, const char *ifname, sa_family_t family,
- const opt_proto_t *op, const filt_opts_t *fopts,
+ const npfvar_t *popts, const filt_opts_t *fopts,
const char *pcap_filter, const char *rproc)
{
nl_rule_t *rl;
@@ -666,7 +705,7 @@ npfctl_build_rule(uint32_t attr, const char *ifname, sa_family_t family,
if (pcap_filter) {
npfctl_build_pcap(rl, pcap_filter);
} else {
- npfctl_build_code(rl, family, op, fopts);
+ npfctl_build_code(rl, family, popts, fopts);
}
if (rproc) {
@@ -694,9 +733,8 @@ npfctl_build_rule(uint32_t attr, const char *ifname, sa_family_t family,
*/
static nl_nat_t *
npfctl_build_nat(int type, const char *ifname, const addr_port_t *ap,
- const opt_proto_t *op, const filt_opts_t *fopts, unsigned flags)
+ const npfvar_t *popts, const filt_opts_t *fopts, unsigned flags)
{
- const opt_proto_t def_op = { .op_proto = -1, .op_opts = NULL };
fam_addr_mask_t *am;
sa_family_t family;
in_port_t port;
@@ -714,9 +752,6 @@ npfctl_build_nat(int type, const char *ifname, const addr_port_t *ap,
} else {
port = 0;
}
- if (!op) {
- op = &def_op;
- }
nat = npf_nat_create(type, flags, ifname);
@@ -738,14 +773,14 @@ npfctl_build_nat(int type, const char *ifname, const addr_port_t *ap,
abort();
}
npf_nat_setport(nat, port);
- npfctl_build_code(nat, family, op, fopts);
+ npfctl_build_code(nat, family, popts, fopts);
return nat;
}
static void
npfctl_dnat_check(const addr_port_t *ap, const unsigned algo)
{
- int type = npfvar_get_type(ap->ap_netaddr, 0);
+ const unsigned type = npfvar_get_type(ap->ap_netaddr, 0);
fam_addr_mask_t *am;
switch (algo) {
@@ -779,7 +814,7 @@ npfctl_dnat_check(const addr_port_t *ap, const unsigned algo)
*/
void
npfctl_build_natseg(int sd, int type, unsigned mflags, const char *ifname,
- const addr_port_t *ap1, const addr_port_t *ap2, const opt_proto_t *op,
+ const addr_port_t *ap1, const addr_port_t *ap2, const npfvar_t *popts,
const filt_opts_t *fopts, unsigned algo)
{
fam_addr_mask_t *am1 = NULL, *am2 = NULL;
@@ -829,27 +864,31 @@ npfctl_build_natseg(int sd, int type, unsigned mflags, const char *ifname,
flags = NPF_NAT_STATIC;
/* Note: translation address/network cannot be a table. */
- am1 = npfctl_get_singlefam(ap1->ap_netaddr);
- am2 = npfctl_get_singlefam(ap2->ap_netaddr);
+ if (type & NPF_NATIN) {
+ am1 = npfctl_get_singlefam(ap1->ap_netaddr);
+ }
+ if (type & NPF_NATOUT) {
+ am2 = npfctl_get_singlefam(ap2->ap_netaddr);
+ }
/* Validate the algorithm. */
switch (algo) {
case NPF_ALGO_NPT66:
- if (am1->fam_mask != am2->fam_mask) {
+ if (!binat || am1->fam_mask != am2->fam_mask) {
yyerror("asymmetric NPTv6 is not supported");
}
adj = npfctl_npt66_calcadj(am1->fam_mask,
&am1->fam_addr, &am2->fam_addr);
break;
case NPF_ALGO_NETMAP:
- if (am1->fam_mask != am2->fam_mask) {
+ if (binat && am1->fam_mask != am2->fam_mask) {
yyerror("net-to-net mapping using the "
"NETMAP algorithm must be 1:1");
}
break;
case NPF_ALGO_NONE:
- if (am1->fam_mask != NPF_NO_NETMASK ||
- am2->fam_mask != NPF_NO_NETMASK) {
+ if ((am1 && am1->fam_mask != NPF_NO_NETMASK) ||
+ (am2 && am2->fam_mask != NPF_NO_NETMASK)) {
yyerror("static net-to-net translation "
"must have an algorithm specified");
}
@@ -882,12 +921,14 @@ npfctl_build_natseg(int sd, int type, unsigned mflags, const char *ifname,
if (type & NPF_NATIN) {
memset(&imfopts, 0, sizeof(filt_opts_t));
memcpy(&imfopts.fo_to, ap2, sizeof(addr_port_t));
- nt1 = npfctl_build_nat(NPF_NATIN, ifname, ap1, op, fopts, flags);
+ nt1 = npfctl_build_nat(NPF_NATIN, ifname,
+ ap1, popts, fopts, flags);
}
if (type & NPF_NATOUT) {
memset(&imfopts, 0, sizeof(filt_opts_t));
memcpy(&imfopts.fo_from, ap1, sizeof(addr_port_t));
- nt2 = npfctl_build_nat(NPF_NATOUT, ifname, ap2, op, fopts, flags);
+ nt2 = npfctl_build_nat(NPF_NATOUT, ifname,
+ ap2, popts, fopts, flags);
}
switch (algo) {
@@ -937,7 +978,7 @@ npfctl_build_natseg(int sd, int type, unsigned mflags, const char *ifname,
* npfctl_fill_table: fill NPF table with entries from a specified file.
*/
static void
-npfctl_fill_table(nl_table_t *tl, u_int type, const char *fname, FILE *fp)
+npfctl_fill_table(nl_table_t *tl, unsigned type, const char *fname, FILE *fp)
{
char *buf = NULL;
int l = 0;
@@ -973,7 +1014,7 @@ npfctl_fill_table(nl_table_t *tl, u_int type, const char *fname, FILE *fp)
* npfctl_load_table: create an NPF table and fill with contents from a file.
*/
nl_table_t *
-npfctl_load_table(const char *tname, int tid, u_int type,
+npfctl_load_table(const char *tname, int tid, unsigned type,
const char *fname, FILE *fp)
{
nl_table_t *tl;
@@ -991,7 +1032,7 @@ npfctl_load_table(const char *tname, int tid, u_int type,
* if required, fill with contents from a file.
*/
void
-npfctl_build_table(const char *tname, u_int type, const char *fname)
+npfctl_build_table(const char *tname, unsigned type, const char *fname)
{
nl_table_t *tl;
diff --git a/usr.sbin/npf/npfctl/npf_cmd.c b/usr.sbin/npf/npfctl/npf_cmd.c
new file mode 100644
index 00000000000..80020b8b593
--- /dev/null
+++ b/usr.sbin/npf/npfctl/npf_cmd.c
@@ -0,0 +1,540 @@
+/*-
+ * Copyright (c) 2009-2020 The NetBSD Foundation, Inc.
+ * All rights reserved.
+ *
+ * This material is based upon work partially supported by The
+ * NetBSD Foundation under a contract with Mindaugas Rasiukevicius.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
+ * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
+ * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include <sys/cdefs.h>
+__RCSID("$NetBSD: npf_cmd.c,v 1.1.2.2 2020/06/20 15:46:48 martin Exp $");
+
+#include <stdio.h>
+#include <string.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <errno.h>
+#include <err.h>
+
+#ifdef __NetBSD__
+#include <sha1.h>
+#define SHA_DIGEST_LENGTH SHA1_DIGEST_LENGTH
+#else
+#include <openssl/sha.h>
+#endif
+
+#include "npfctl.h"
+
+////////////////////////////////////////////////////////////////////////////
+//
+// NPFCTL RULE COMMANDS
+//
+
+#ifdef __NetBSD__
+static unsigned char *
+SHA1(const unsigned char *d, size_t l, unsigned char *md)
+{
+ SHA1_CTX c;
+
+ SHA1Init(&c);
+ SHA1Update(&c, d, l);
+ SHA1Final(md, &c);
+ return md;
+}
+#endif
+
+static void
+npfctl_generate_key(nl_rule_t *rl, void *key)
+{
+ void *meta;
+ size_t len;
+
+ if ((meta = npf_rule_export(rl, &len)) == NULL) {
+ errx(EXIT_FAILURE, "error generating rule key");
+ }
+ __CTASSERT(NPF_RULE_MAXKEYLEN >= SHA_DIGEST_LENGTH);
+ memset(key, 0, NPF_RULE_MAXKEYLEN);
+ SHA1(meta, len, key);
+ free(meta);
+}
+
+int
+npfctl_nat_ruleset_p(const char *name, bool *natset)
+{
+ const size_t preflen = sizeof(NPF_RULESET_MAP_PREF) - 1;
+ *natset = strncmp(name, NPF_RULESET_MAP_PREF, preflen) == 0;
+ return (*natset && strlen(name) <= preflen) ? -1 : 0;
+}
+
+static nl_rule_t *
+npfctl_parse_rule(int argc, char **argv, parse_entry_t entry)
+{
+ char rule_string[1024];
+ nl_rule_t *rl;
+
+ /* Get the rule string and parse it. */
+ if (!join(rule_string, sizeof(rule_string), argc, argv, " ")) {
+ errx(EXIT_FAILURE, "command too long");
+ }
+ npfctl_parse_string(rule_string, entry);
+ if ((rl = npfctl_rule_ref()) == NULL) {
+ errx(EXIT_FAILURE, "could not parse the rule");
+ }
+ return rl;
+}
+
+void
+npfctl_rule(int fd, int argc, char **argv)
+{
+ static const struct ruleops_s {
+ const char * cmd;
+ int action;
+ bool extra_arg;
+ } ruleops[] = {
+ { "add", NPF_CMD_RULE_ADD, true },
+ { "rem", NPF_CMD_RULE_REMKEY, true },
+ { "del", NPF_CMD_RULE_REMKEY, true },
+ { "rem-id", NPF_CMD_RULE_REMOVE, true },
+ { "list", NPF_CMD_RULE_LIST, false },
+ { "flush", NPF_CMD_RULE_FLUSH, false },
+ { NULL, 0, 0 }
+ };
+ uint8_t key[NPF_RULE_MAXKEYLEN];
+ const char *ruleset_name = argv[0];
+ const char *cmd = argv[1];
+ int error, action = 0;
+ bool extra_arg, natset;
+ parse_entry_t entry;
+ uint64_t rule_id;
+ nl_rule_t *rl;
+
+ for (unsigned n = 0; ruleops[n].cmd != NULL; n++) {
+ if (strcmp(cmd, ruleops[n].cmd) == 0) {
+ action = ruleops[n].action;
+ extra_arg = ruleops[n].extra_arg;
+ break;
+ }
+ }
+ argc -= 2;
+ argv += 2;
+
+ if (!action || (extra_arg && argc == 0)) {
+ usage();
+ }
+
+ if (npfctl_nat_ruleset_p(ruleset_name, &natset) != 0) {
+ errx(EXIT_FAILURE,
+ "invalid NAT ruleset name (note: the name must be "
+ "prefixed with `" NPF_RULESET_MAP_PREF "`)");
+ }
+ entry = natset ? NPFCTL_PARSE_MAP : NPFCTL_PARSE_RULE;
+
+ switch (action) {
+ case NPF_CMD_RULE_ADD:
+ rl = npfctl_parse_rule(argc, argv, entry);
+ npfctl_generate_key(rl, key);
+ npf_rule_setkey(rl, key, sizeof(key));
+ error = npf_ruleset_add(fd, ruleset_name, rl, &rule_id);
+ break;
+ case NPF_CMD_RULE_REMKEY:
+ rl = npfctl_parse_rule(argc, argv, entry);
+ npfctl_generate_key(rl, key);
+ error = npf_ruleset_remkey(fd, ruleset_name, key, sizeof(key));
+ break;
+ case NPF_CMD_RULE_REMOVE:
+ rule_id = strtoull(argv[0], NULL, 16);
+ error = npf_ruleset_remove(fd, ruleset_name, rule_id);
+ break;
+ case NPF_CMD_RULE_LIST:
+ error = npfctl_ruleset_show(fd, ruleset_name);
+ break;
+ case NPF_CMD_RULE_FLUSH:
+ error = npf_ruleset_flush(fd, ruleset_name);
+ break;
+ default:
+ abort();
+ }
+
+ switch (error) {
+ case 0:
+ /* Success. */
+ break;
+ case ESRCH:
+ errx(EXIT_FAILURE, "ruleset \"%s\" not found", ruleset_name);
+ case ENOENT:
+ errx(EXIT_FAILURE, "rule was not found");
+ default:
+ errx(EXIT_FAILURE, "rule operation: %s", strerror(error));
+ }
+ if (action == NPF_CMD_RULE_ADD) {
+ printf("OK %" PRIx64 "\n", rule_id);
+ }
+}
+
+////////////////////////////////////////////////////////////////////////////
+//
+// NPFCTL TABLE COMMANDS
+//
+
+static int
+npfctl_table_type(const char *typename)
+{
+ static const struct tbltype_s {
+ const char * name;
+ unsigned type;
+ } tbltypes[] = {
+ { "ipset", NPF_TABLE_IPSET },
+ { "lpm", NPF_TABLE_LPM },
+ { "const", NPF_TABLE_CONST },
+ { NULL, 0 }
+ };
+
+ for (unsigned i = 0; tbltypes[i].name != NULL; i++) {
+ if (strcmp(typename, tbltypes[i].name) == 0) {
+ return tbltypes[i].type;
+ }
+ }
+ return 0;
+}
+
+void
+npfctl_table_replace(int fd, int argc, char **argv)
+{
+ const char *name, *newname, *path, *typename = NULL;
+ nl_config_t *ncf;
+ nl_table_t *t;
+ unsigned type = 0;
+ int c, tid = -1;
+ FILE *fp;
+
+ name = newname = argv[0];
+ optind = 2;
+ while ((c = getopt(argc, argv, "n:t:")) != -1) {
+ switch (c) {
+ case 't':
+ typename = optarg;
+ break;
+ case 'n':
+ newname = optarg;
+ break;
+ default:
+ errx(EXIT_FAILURE,
+ "Usage: %s table \"table-name\" replace "
+ "[-n \"name\"] [-t <type>] <table-file>\n",
+ getprogname());
+ }
+ }
+ argc -= optind;
+ argv += optind;
+
+ if (typename && (type = npfctl_table_type(typename)) == 0) {
+ errx(EXIT_FAILURE, "unsupported table type '%s'", typename);
+ }
+
+ if (argc != 1) {
+ usage();
+ }
+
+ path = argv[0];
+ if (strcmp(path, "-") == 0) {
+ path = "stdin";
+ fp = stdin;
+ } else if ((fp = fopen(path, "r")) == NULL) {
+ err(EXIT_FAILURE, "open '%s'", path);
+ }
+
+ /* Get existing config to lookup ID of existing table */
+ if ((ncf = npf_config_retrieve(fd)) == NULL) {
+ err(EXIT_FAILURE, "npf_config_retrieve()");
+ }
+ if ((t = npfctl_table_getbyname(ncf, name)) == NULL) {
+ errx(EXIT_FAILURE,
+ "table '%s' not found in the active configuration", name);
+ }
+ tid = npf_table_getid(t);
+ if (!type) {
+ type = npf_table_gettype(t);
+ }
+ npf_config_destroy(ncf);
+
+ if ((t = npfctl_load_table(newname, tid, type, path, fp)) == NULL) {
+ err(EXIT_FAILURE, "table load failed");
+ }
+
+ if (npf_table_replace(fd, t, NULL)) {
+ err(EXIT_FAILURE, "npf_table_replace(<%s>)", name);
+ }
+}
+
+void
+npfctl_table(int fd, int argc, char **argv)
+{
+ static const struct tblops_s {
+ const char * cmd;
+ int action;
+ } tblops[] = {
+ { "add", NPF_CMD_TABLE_ADD },
+ { "rem", NPF_CMD_TABLE_REMOVE },
+ { "del", NPF_CMD_TABLE_REMOVE },
+ { "test", NPF_CMD_TABLE_LOOKUP },
+ { "list", NPF_CMD_TABLE_LIST },
+ { "flush", NPF_CMD_TABLE_FLUSH },
+ { NULL, 0 }
+ };
+ npf_ioctl_table_t nct;
+ fam_addr_mask_t fam;
+ size_t buflen = 512;
+ char *cmd, *arg;
+ int n, alen;
+
+ /* Default action is list. */
+ memset(&nct, 0, sizeof(npf_ioctl_table_t));
+ nct.nct_name = argv[0];
+ cmd = argv[1];
+
+ for (n = 0; tblops[n].cmd != NULL; n++) {
+ if (strcmp(cmd, tblops[n].cmd) != 0) {
+ continue;
+ }
+ nct.nct_cmd = tblops[n].action;
+ break;
+ }
+ if (tblops[n].cmd == NULL) {
+ errx(EXIT_FAILURE, "invalid command '%s'", cmd);
+ }
+
+ switch (nct.nct_cmd) {
+ case NPF_CMD_TABLE_LIST:
+ case NPF_CMD_TABLE_FLUSH:
+ arg = NULL;
+ break;
+ default:
+ if (argc < 3) {
+ usage();
+ }
+ arg = argv[2];
+ }
+
+again:
+ switch (nct.nct_cmd) {
+ case NPF_CMD_TABLE_LIST:
+ nct.nct_data.buf.buf = ecalloc(1, buflen);
+ nct.nct_data.buf.len = buflen;
+ break;
+ case NPF_CMD_TABLE_FLUSH:
+ break;
+ default:
+ if (!npfctl_parse_cidr(arg, &fam, &alen)) {
+ errx(EXIT_FAILURE, "invalid CIDR '%s'", arg);
+ }
+ nct.nct_data.ent.alen = alen;
+ memcpy(&nct.nct_data.ent.addr, &fam.fam_addr, alen);
+ nct.nct_data.ent.mask = fam.fam_mask;
+ }
+
+ if (ioctl(fd, IOC_NPF_TABLE, &nct) != -1) {
+ errno = 0;
+ }
+ switch (errno) {
+ case 0:
+ break;
+ case EEXIST:
+ errx(EXIT_FAILURE, "entry already exists or is conflicting");
+ case ENOENT:
+ errx(EXIT_FAILURE, "not found");
+ case EINVAL:
+ errx(EXIT_FAILURE, "invalid address, mask or table ID");
+ case ENOMEM:
+ if (nct.nct_cmd == NPF_CMD_TABLE_LIST) {
+ /* XXX */
+ free(nct.nct_data.buf.buf);
+ buflen <<= 1;
+ goto again;
+ }
+ /* FALLTHROUGH */
+ default:
+ err(EXIT_FAILURE, "ioctl(IOC_NPF_TABLE)");
+ }
+
+ if (nct.nct_cmd == NPF_CMD_TABLE_LIST) {
+ npf_ioctl_ent_t *ent = nct.nct_data.buf.buf;
+ char *buf;
+
+ while (nct.nct_data.buf.len--) {
+ if (!ent->alen)
+ break;
+ buf = npfctl_print_addrmask(ent->alen, "%a",
+ &ent->addr, ent->mask);
+ puts(buf);
+ ent++;
+ }
+ free(nct.nct_data.buf.buf);
+ } else {
+ printf("%s: %s\n", getprogname(),
+ nct.nct_cmd == NPF_CMD_TABLE_LOOKUP ?
+ "match" : "success");
+ }
+}
+
+////////////////////////////////////////////////////////////////////////////
+//
+// NPFCTL CONNECTION COMMANDS
+//
+
+typedef struct {
+ FILE * fp;
+ unsigned alen;
+ const char * ifname;
+ bool nat;
+ bool nowide;
+ bool name;
+
+ bool v4;
+ unsigned pwidth;
+} npf_conn_filter_t;
+
+static int
+npfctl_conn_print(unsigned alen, const npf_addr_t *a, const in_port_t *p,
+ const char *ifname, void *arg)
+{
+ const npf_conn_filter_t *fil = arg;
+ char *addrstr, *src, *dst;
+ const char *fmt;
+ FILE *fp = fil->fp;
+ bool nat_conn;
+
+ /*
+ * Filter connection entries by IP version, interface and/or
+ * applicability of NAT.
+ */
+ if (alen != fil->alen) {
+ return 0;
+ }
+ if (fil->ifname && (!ifname || strcmp(ifname, fil->ifname) != 0)) {
+ return 0;
+ }
+ nat_conn = !npfctl_addr_iszero(&a[2]) || p[2] != 0;
+ if (fil->nat && !nat_conn) {
+ return 0;
+ }
+
+ fmt = fil->name ? "%A" : (fil->v4 ? "%a" : "[%a]");
+
+ addrstr = npfctl_print_addrmask(alen, fmt, &a[0], NPF_NO_NETMASK);
+ easprintf(&src, "%s:%d", addrstr, p[0]);
+ free(addrstr);
+
+ addrstr = npfctl_print_addrmask(alen, fmt, &a[1], NPF_NO_NETMASK);
+ easprintf(&dst, "%s:%d", addrstr, p[1]);
+ free(addrstr);
+
+ fprintf(fp, "%-*s %-*s ", fil->pwidth, src, fil->pwidth, dst);
+ free(src);
+ free(dst);
+
+ fprintf(fp, "%-10s ", ifname ? ifname : "-");
+ if (nat_conn) {
+ addrstr = npfctl_print_addrmask(alen, fmt, &a[2], NPF_NO_NETMASK);
+ fprintf(fp, "%s", addrstr);
+ free(addrstr);
+ if (p[2]) {
+ fprintf(fp, ":%d", p[2]);
+ }
+ }
+ fputc('\n', fp);
+ return 1;
+}
+
+static void
+npf_conn_list_v(int fd, unsigned alen, npf_conn_filter_t *f)
+{
+ f->alen = alen;
+ f->v4 = alen == sizeof(struct in_addr);
+ f->pwidth = f->nowide ? 0 : ((f->v4 ? 15 : 40) + 1 + 5);
+ if (npf_conn_list(fd, npfctl_conn_print, f) != 0) {
+ err(EXIT_FAILURE, "npf_conn_list");
+ }
+}
+
+int
+npfctl_conn_list(int fd, int argc, char **argv)
+{
+ npf_conn_filter_t f;
+ bool header = true;
+ unsigned alen = 0;
+ int c;
+
+ argc--;
+ argv++;
+
+ memset(&f, 0, sizeof(f));
+ f.fp = stdout;
+
+ while ((c = getopt(argc, argv, "46hi:nNW")) != -1) {
+ switch (c) {
+ case '4':
+ alen = sizeof(struct in_addr);
+ break;
+ case '6':
+ alen = sizeof(struct in6_addr);
+ break;
+ case 'h':
+ header = false;
+ break;
+ case 'i':
+ f.ifname = optarg;
+ break;
+ case 'n':
+ f.nat = true;
+ break;
+ case 'N':
+ f.name = true;
+ break;
+ case 'W':
+ f.nowide = true;
+ break;
+ default:
+ errx(EXIT_FAILURE,
+ "Usage: %s list [-46hnNW] [-i <ifname>]\n",
+ getprogname());
+ }
+ }
+
+ if (header) {
+ fprintf(f.fp, "# %-*s %-*s %-*s %s\n",
+ 21 - 2, "src-addr:port",
+ 21, "dst-addr:port",
+ 10, "interface",
+ "nat-addr:port");
+ }
+
+ if (!alen || alen == sizeof(struct in_addr)) {
+ npf_conn_list_v(fd, sizeof(struct in_addr), &f);
+ }
+ if (!alen || alen == sizeof(struct in6_addr)) {
+ npf_conn_list_v(fd, sizeof(struct in6_addr), &f);
+ }
+
+ return 0;
+}
diff --git a/usr.sbin/npf/npfctl/npf_parse.y b/usr.sbin/npf/npfctl/npf_parse.y
index b070f07c327..d77f462cd8c 100644
--- a/usr.sbin/npf/npfctl/npf_parse.y
+++ b/usr.sbin/npf/npfctl/npf_parse.y
@@ -1,5 +1,5 @@
/*-
- * Copyright (c) 2011-2019 The NetBSD Foundation, Inc.
+ * Copyright (c) 2011-2020 The NetBSD Foundation, Inc.
* All rights reserved.
*
* This code is derived from software contributed to The NetBSD Foundation
@@ -130,6 +130,7 @@ yyerror(const char *fmt, ...)
%token INET4
%token INET6
%token INTERFACE
+%token INVALID
%token IPHASH
%token IPSET
%token LPM
@@ -188,14 +189,15 @@ yyerror(const char *fmt, ...)
%type <num> maybe_not opt_stateful icmp_type table_type
%type <num> map_sd map_algo map_flags map_type
%type <num> param_val
-%type <var> static_ifaddrs addr_or_ifaddr
-%type <var> port_range icmp_type_and_code
+%type <var> static_ifaddrs filt_addr_element
+%type <var> filt_port filt_port_list port_range icmp_type_and_code
%type <var> filt_addr addr_and_mask tcp_flags tcp_flags_and_mask
%type <var> procs proc_call proc_param_list proc_param
-%type <var> element list_elems list value
+%type <var> element list_elems list value filt_addr_list
+%type <var> opt_proto proto proto_elems
%type <addrport> mapseg
%type <filtopts> filt_opts all_or_filt_opts
-%type <optproto> proto opt_proto
+%type <optproto> rawproto
%type <rulegroup> group_opts
%union {
@@ -389,7 +391,7 @@ map_type
;
mapseg
- : filt_addr port_range
+ : filt_addr filt_port
{
$$.ap_netaddr = $1;
$$.ap_portrange = $2;
@@ -400,7 +402,7 @@ map
: MAP ifref map_sd map_algo map_flags mapseg map_type mapseg
PASS opt_family opt_proto all_or_filt_opts
{
- npfctl_build_natseg($3, $7, $5, $2, &$6, &$8, &$11, &$12, $4);
+ npfctl_build_natseg($3, $7, $5, $2, &$6, &$8, $11, &$12, $4);
}
| MAP ifref map_sd map_algo map_flags mapseg map_type mapseg
{
@@ -408,7 +410,7 @@ map
}
| MAP ifref map_sd map_algo map_flags proto mapseg map_type mapseg
{
- npfctl_build_natseg($3, $8, $5, $2, &$7, &$9, &$6, NULL, $4);
+ npfctl_build_natseg($3, $8, $5, $2, &$7, &$9, $6, NULL, $4);
}
| MAP RULESET group_opts
{
@@ -549,7 +551,7 @@ rule
opt_family opt_proto all_or_filt_opts opt_apply
{
npfctl_build_rule($1 | $2 | $3 | $4, $5,
- $6, &$7, &$8, NULL, $9);
+ $6, $7, &$8, NULL, $9);
}
| block_or_pass opt_stateful rule_dir opt_final on_ifname
PCAP_FILTER STRING opt_apply
@@ -595,43 +597,63 @@ opt_family
| { $$ = AF_UNSPEC; }
;
-proto
- : PROTO TCP tcp_flags_and_mask
+rawproto
+ : TCP tcp_flags_and_mask
{
$$.op_proto = IPPROTO_TCP;
- $$.op_opts = $3;
+ $$.op_opts = $2;
}
- | PROTO ICMP icmp_type_and_code
+ | ICMP icmp_type_and_code
{
$$.op_proto = IPPROTO_ICMP;
- $$.op_opts = $3;
+ $$.op_opts = $2;
}
- | PROTO ICMP6 icmp_type_and_code
+ | ICMP6 icmp_type_and_code
{
$$.op_proto = IPPROTO_ICMPV6;
- $$.op_opts = $3;
+ $$.op_opts = $2;
}
- | PROTO some_name
+ | some_name
{
- $$.op_proto = npfctl_protono($2);
+ $$.op_proto = npfctl_protono($1);
$$.op_opts = NULL;
}
- | PROTO number
+ | number
{
- $$.op_proto = $2;
+ $$.op_proto = $1;
$$.op_opts = NULL;
}
;
-opt_proto
- : proto { $$ = $1; }
- |
+proto_elems
+ : proto_elems COMMA rawproto
{
- $$.op_proto = -1;
- $$.op_opts = NULL;
+ npfvar_t *pvar = npfvar_create_element(
+ NPFVAR_PROTO, &$3, sizeof($3));
+ $$ = npfvar_add_elements($1, pvar);
+ }
+ | rawproto
+ {
+ $$ = npfvar_create_element(NPFVAR_PROTO, &$1, sizeof($1));
+ }
+ ;
+
+proto
+ : PROTO rawproto
+ {
+ $$ = npfvar_create_element(NPFVAR_PROTO, &$2, sizeof($2));
+ }
+ | PROTO CURLY_OPEN proto_elems CURLY_CLOSE
+ {
+ $$ = $3;
}
;
+opt_proto
+ : proto { $$ = $1; }
+ | { $$ = NULL; }
+ ;
+
all_or_filt_opts
: ALL
{
@@ -664,7 +686,7 @@ block_opts
;
filt_opts
- : FROM maybe_not filt_addr port_range TO maybe_not filt_addr port_range
+ : FROM maybe_not filt_addr filt_port TO maybe_not filt_addr filt_port
{
$$.fo_finvert = $2;
$$.fo_from.ap_netaddr = $3;
@@ -673,7 +695,7 @@ filt_opts
$$.fo_to.ap_netaddr = $7;
$$.fo_to.ap_portrange = $8;
}
- | FROM maybe_not filt_addr port_range
+ | FROM maybe_not filt_addr filt_port
{
$$.fo_finvert = $2;
$$.fo_from.ap_netaddr = $3;
@@ -682,7 +704,7 @@ filt_opts
$$.fo_to.ap_netaddr = NULL;
$$.fo_to.ap_portrange = NULL;
}
- | TO maybe_not filt_addr port_range
+ | TO maybe_not filt_addr filt_port
{
$$.fo_finvert = false;
$$.fo_from.ap_netaddr = NULL;
@@ -693,9 +715,20 @@ filt_opts
}
;
+filt_addr_list
+ : filt_addr_list COMMA filt_addr_element
+ {
+ npfvar_add_elements($1, $3);
+ }
+ | filt_addr_element
+ ;
+
filt_addr
- : list { $$ = $1; }
- | addr_or_ifaddr { $$ = $1; }
+ : CURLY_OPEN filt_addr_list CURLY_CLOSE
+ {
+ $$ = $2;
+ }
+ | filt_addr_element { $$ = $1; }
| ANY { $$ = NULL; }
;
@@ -714,7 +747,7 @@ addr_and_mask
}
;
-addr_or_ifaddr
+filt_addr_element
: addr_and_mask { assert($1 != NULL); $$ = $1; }
| static_ifaddrs
{
@@ -765,25 +798,37 @@ addr
| IPV6ADDR { $$ = $1; }
;
-port_range
- : PORT port /* just port */
+filt_port
+ : PORT CURLY_OPEN filt_port_list CURLY_CLOSE
{
- $$ = npfctl_parse_port_range($2, $2);
+ $$ = npfctl_parse_port_range_variable(NULL, $3);
}
- | PORT port MINUS port /* port from-to */
+ | PORT port_range { $$ = $2; }
+ | { $$ = NULL; }
+ ;
+
+filt_port_list
+ : filt_port_list COMMA port_range
{
- $$ = npfctl_parse_port_range($2, $4);
+ npfvar_add_elements($1, $3);
+ }
+ | port_range
+ ;
+
+port_range
+ : port /* just port */
+ {
+ $$ = npfctl_parse_port_range($1, $1);
}
- | PORT VAR_ID
+ | port MINUS port /* port from-to */
{
- npfvar_t *vp = npfvar_lookup($2);
- $$ = npfctl_parse_port_range_variable($2, vp);
+ $$ = npfctl_parse_port_range($1, $3);
}
- | PORT list
+ | VAR_ID
{
- $$ = npfctl_parse_port_range_variable(NULL, $2);
+ npfvar_t *vp = npfvar_lookup($1);
+ $$ = npfctl_parse_port_range_variable($1, vp);
}
- | { $$ = NULL; }
;
port
diff --git a/usr.sbin/npf/npfctl/npf_scan.l b/usr.sbin/npf/npfctl/npf_scan.l
index 95d1340de94..28bc5e96657 100644
--- a/usr.sbin/npf/npfctl/npf_scan.l
+++ b/usr.sbin/npf/npfctl/npf_scan.l
@@ -30,6 +30,7 @@
%{
#include <stdio.h>
#include <stdlib.h>
+#include <unistd.h>
#include <err.h>
#include "npfctl.h"
@@ -48,9 +49,10 @@ extern void yyrestart(FILE *);
void
npfctl_parse_file(const char *name)
{
+ const bool use_stdin = strcmp(name, "-") == 0;
FILE *fp;
- fp = fopen(name, "r");
+ fp = use_stdin ? fdopen(STDIN_FILENO, "r") : fopen(name, "r");
if (fp == NULL) {
err(EXIT_FAILURE, "open '%s'", name);
}
@@ -58,7 +60,7 @@ npfctl_parse_file(const char *name)
yyrestart(fp);
yylineno = 1;
yycolumn = 0;
- yyfilename = name;
+ yyfilename = use_stdin ? "stdin" : name;
yyparse();
fclose(fp);
}
@@ -247,3 +249,5 @@ any return ANY;
[ \t] /* eat whitespace */
: return COLON;
+
+. return INVALID;
diff --git a/usr.sbin/npf/npfctl/npf_show.c b/usr.sbin/npf/npfctl/npf_show.c
index 0c30cbb26c4..62ac509998b 100644
--- a/usr.sbin/npf/npfctl/npf_show.c
+++ b/usr.sbin/npf/npfctl/npf_show.c
@@ -1,5 +1,5 @@
/*-
- * Copyright (c) 2013-2019 The NetBSD Foundation, Inc.
+ * Copyright (c) 2013-2020 The NetBSD Foundation, Inc.
* All rights reserved.
*
* This code is derived from software contributed to The NetBSD Foundation
@@ -34,7 +34,7 @@
*/
#include <sys/cdefs.h>
-__RCSID("$NetBSD: npf_show.c,v 1.28.2.3 2020/05/25 17:27:19 martin Exp $");
+__RCSID("$NetBSD: npf_show.c,v 1.28.2.4 2020/06/20 15:46:48 martin Exp $");
#include <sys/socket.h>
#define __FAVOR_BSD
@@ -52,29 +52,80 @@ __RCSID("$NetBSD: npf_show.c,v 1.28.2.3 2020/05/25 17:27:19 martin Exp $");
#include "npfctl.h"
-#define SEEN_SRC 0x01
-#define SEEN_DST 0x02
+#define SEEN_PROTO 0x01
+
+typedef struct {
+ char ** values;
+ unsigned count;
+} elem_list_t;
+
+enum {
+ LIST_PROTO = 0, LIST_SADDR, LIST_DADDR, LIST_SPORT, LIST_DPORT,
+ LIST_COUNT,
+};
typedef struct {
nl_config_t * conf;
+ bool validating;
+
FILE * fp;
long fpos;
- u_int flags;
+ long fposln;
+ int glevel;
+
+ unsigned flags;
uint32_t curmark;
- unsigned level;
-} npf_conf_info_t;
+ uint64_t seen_marks;
+ elem_list_t list[LIST_COUNT];
-static npf_conf_info_t stdout_ctx;
+} npf_conf_info_t;
static void print_linesep(npf_conf_info_t *);
-void
+static npf_conf_info_t *
npfctl_show_init(void)
{
+ static npf_conf_info_t stdout_ctx;
+ memset(&stdout_ctx, 0, sizeof(npf_conf_info_t));
+ stdout_ctx.glevel = -1;
stdout_ctx.fp = stdout;
- stdout_ctx.fpos = 0;
- stdout_ctx.flags = 0;
- stdout_ctx.level = 0;
+ return &stdout_ctx;
+}
+
+static void
+list_push(elem_list_t *list, char *val)
+{
+ const unsigned n = list->count;
+ char **values;
+
+ if ((values = calloc(n + 1, sizeof(char *))) == NULL) {
+ err(EXIT_FAILURE, "calloc");
+ }
+ for (unsigned i = 0; i < n; i++) {
+ values[i] = list->values[i];
+ }
+ values[n] = val;
+ free(list->values);
+ list->values = values;
+ list->count++;
+}
+
+static char *
+list_join_free(elem_list_t *list, const bool use_br, const char *sep)
+{
+ char *s, buf[2048];
+
+ if (!join(buf, sizeof(buf), list->count, list->values, sep)) {
+ errx(EXIT_FAILURE, "out of memory while parsing the rule");
+ }
+ easprintf(&s, (use_br && list->count > 1) ? "{ %s }" : "%s", buf);
+ for (unsigned i = 0; i < list->count; i++) {
+ free(list->values[i]);
+ }
+ free(list->values);
+ list->values = NULL;
+ list->count = 0;
+ return s;
}
/*
@@ -84,39 +135,32 @@ npfctl_show_init(void)
static void
print_indent(npf_conf_info_t *ctx, unsigned level)
{
- if (level < ctx->level) {
+ if (ctx->glevel >= 0 && level <= (unsigned)ctx->glevel) {
/*
* Level decrease -- end of the group.
* Print the group closing curly bracket.
*/
- fputs("}\n", ctx->fp);
+ ctx->fpos += fprintf(ctx->fp, "}\n\n");
+ ctx->glevel = -1;
}
- if (level == 0) {
- /*
- * Group level -- separate groups by a trailing new line.
- */
- print_linesep(ctx);
- }
- ctx->level = level;
-
while (level--) {
- fprintf(ctx->fp, "\t");
+ ctx->fpos += fprintf(ctx->fp, "\t");
}
}
static void
print_linesep(npf_conf_info_t *ctx)
{
- if (ftell(ctx->fp) != ctx->fpos) {
- fputs("\n", ctx->fp);
- ctx->fpos = ftell(ctx->fp);
+ if (ctx->fpos != ctx->fposln) {
+ ctx->fpos += fprintf(ctx->fp, "\n");
+ ctx->fposln = ctx->fpos;
}
}
static size_t
-tcpflags2string(char *buf, u_int tfl)
+tcpflags2string(char *buf, unsigned tfl)
{
- u_int i = 0;
+ unsigned i = 0;
if (tfl & TH_FIN) buf[i++] = 'F';
if (tfl & TH_SYN) buf[i++] = 'S';
@@ -150,7 +194,7 @@ static char *
print_address(npf_conf_info_t *ctx __unused, const uint32_t *words)
{
const int af = *words++;
- const u_int mask = *words++;
+ const unsigned mask = *words++;
const npf_addr_t *addr;
int alen = 0;
@@ -192,6 +236,7 @@ print_table(npf_conf_info_t *ctx, const uint32_t *words)
static char *
print_proto(npf_conf_info_t *ctx, const uint32_t *words)
{
+ ctx->flags |= SEEN_PROTO;
switch (words[0]) {
case IPPROTO_TCP:
return estrdup("tcp");
@@ -208,10 +253,19 @@ print_proto(npf_conf_info_t *ctx, const uint32_t *words)
static char *
print_tcpflags(npf_conf_info_t *ctx __unused, const uint32_t *words)
{
- const u_int tf = words[0], tf_mask = words[1];
- char buf[20];
+ const unsigned tf = words[0], tf_mask = words[1];
+ char buf[32];
+ size_t n;
- size_t n = tcpflags2string(buf, tf);
+ if ((ctx->flags & SEEN_PROTO) == 0) {
+ /*
+ * Note: the TCP flag matching might be without 'proto tcp'
+ * when using a plain 'stateful' rule. In such case, just
+ * skip showing of the flags as they are implicit.
+ */
+ return NULL;
+ }
+ n = tcpflags2string(buf, tf);
if (tf != tf_mask) {
buf[n++] = '/';
tcpflags2string(buf + n, tf_mask);
@@ -220,23 +274,9 @@ print_tcpflags(npf_conf_info_t *ctx __unused, const uint32_t *words)
}
static char *
-print_pbarrier(npf_conf_info_t *ctx, const uint32_t *words __unused)
-{
- if (ctx->curmark == BM_SRC_PORTS && (ctx->flags & SEEN_SRC) == 0) {
- ctx->flags |= SEEN_SRC;
- return estrdup("from any");
- }
- if (ctx->curmark == BM_DST_PORTS && (ctx->flags & SEEN_DST) == 0) {
- ctx->flags |= SEEN_DST;
- return estrdup("to any");
- }
- return NULL;
-}
-
-static char *
print_portrange(npf_conf_info_t *ctx __unused, const uint32_t *words)
{
- u_int fport = words[0], tport = words[1];
+ unsigned fport = words[0], tport = words[1];
char *p;
if (fport != tport) {
@@ -263,7 +303,7 @@ static const struct attr_keyword_mapent {
const char * val;
} attr_keyword_map[] = {
{ F(GROUP)|F(DYNAMIC), F(GROUP), "group" },
- { F(DYNAMIC), F(DYNAMIC), "ruleset" },
+ { F(GROUP)|F(DYNAMIC), F(GROUP)|F(DYNAMIC), "ruleset" },
{ F(GROUP)|F(PASS), 0, "block" },
{ F(GROUP)|F(PASS), F(PASS), "pass" },
{ F(RETRST)|F(RETICMP), F(RETRST)|F(RETICMP), "return" },
@@ -277,28 +317,27 @@ static const struct attr_keyword_mapent {
};
static const struct mark_keyword_mapent {
- u_int mark;
- const char * token;
- const char * sep;
- u_int set_flags;
+ unsigned mark;
+ const char * format;
+ int list_id;
char * (*printfn)(npf_conf_info_t *, const uint32_t *);
- u_int fwords;
+ unsigned fwords;
} mark_keyword_map[] = {
- { BM_IPVER, "family %s", NULL, 0, print_family, 1 },
- { BM_PROTO, "proto %s", ", ", 0, print_proto, 1 },
- { BM_TCPFL, "flags %s", NULL, 0, print_tcpflags, 2 },
- { BM_ICMP_TYPE, "icmp-type %s", NULL, 0, print_number, 1 },
- { BM_ICMP_CODE, "code %s", NULL, 0, print_number, 1 },
-
- { BM_SRC_CIDR, "from %s", ", ", SEEN_SRC, print_address, 6 },
- { BM_SRC_TABLE, "from %s", ", ", SEEN_SRC, print_table, 1 },
- { BM_SRC_PORTS, "%s", NULL, 0, print_pbarrier, 2 },
- { BM_SRC_PORTS, "port %s", ", ", 0, print_portrange,2 },
-
- { BM_DST_CIDR, "to %s", ", ", SEEN_DST, print_address, 6 },
- { BM_DST_TABLE, "to %s", ", ", SEEN_DST, print_table, 1 },
- { BM_DST_PORTS, "%s", NULL, 0, print_pbarrier, 2 },
- { BM_DST_PORTS, "port %s", ", ", 0, print_portrange,2 },
+ { BM_IPVER, "family %s", LIST_PROTO, print_family, 1 },
+ { BM_PROTO, "proto %s", LIST_PROTO, print_proto, 1 },
+ { BM_TCPFL, "flags %s", LIST_PROTO, print_tcpflags, 2 },
+ { BM_ICMP_TYPE, "icmp-type %s", LIST_PROTO, print_number, 1 },
+ { BM_ICMP_CODE, "code %s", LIST_PROTO, print_number, 1 },
+
+ { BM_SRC_NEG, NULL, -1, NULL, 0 },
+ { BM_SRC_CIDR, NULL, LIST_SADDR, print_address, 6 },
+ { BM_SRC_TABLE, NULL, LIST_SADDR, print_table, 1 },
+ { BM_SRC_PORTS, NULL, LIST_SPORT, print_portrange,2 },
+
+ { BM_DST_NEG, NULL, -1, NULL, 0 },
+ { BM_DST_CIDR, NULL, LIST_DADDR, print_address, 6 },
+ { BM_DST_TABLE, NULL, LIST_DADDR, print_table, 1 },
+ { BM_DST_PORTS, NULL, LIST_DPORT, print_portrange,2 },
};
static const char * __attribute__((format_arg(2)))
@@ -307,71 +346,141 @@ verified_fmt(const char *fmt, const char *t __unused)
return fmt;
}
-static char *
+static void
scan_marks(npf_conf_info_t *ctx, const struct mark_keyword_mapent *mk,
const uint32_t *marks, size_t mlen)
{
- char buf[2048], *vals[256], *p;
- size_t nvals = 0;
+ elem_list_t sublist, *target_list;
+
+ /*
+ * If format is used for this mark, then collect multiple elements
+ * in into the list, merge and re-push the set into the target list.
+ *
+ * Currently, this is applicable only for 'proto { tcp, udp }'.
+ */
+ memset(&sublist, 0, sizeof(elem_list_t));
+ target_list = mk->format ? &sublist : &ctx->list[mk->list_id];
/* Scan for the marks and extract the values. */
mlen /= sizeof(uint32_t);
while (mlen > 2) {
const uint32_t m = *marks++;
- const u_int nwords = *marks++;
+ const unsigned nwords = *marks++;
if ((mlen -= 2) < nwords) {
errx(EXIT_FAILURE, "byte-code marking inconsistency");
}
if (m == mk->mark) {
- char *val;
-
- /* Set the current mark and the flags. */
- ctx->flags |= mk->set_flags;
+ /*
+ * Set the current mark and note it as seen.
+ * Value is processed by the print function,
+ * otherwise we just need to note the mark.
+ */
ctx->curmark = m;
-
- /* Value is processed by the print function. */
+ assert(BM_COUNT < (sizeof(uint64_t) * CHAR_BIT));
+ ctx->seen_marks = UINT64_C(1) << m;
assert(mk->fwords == nwords);
- if ((val = mk->printfn(ctx, marks)) != NULL) {
- vals[nvals++] = val;
+
+ if (mk->printfn) {
+ char *val;
+
+ if ((val = mk->printfn(ctx, marks)) != NULL) {
+ list_push(target_list, val);
+ }
}
}
marks += nwords;
mlen -= nwords;
}
- if (nvals == 0) {
- return NULL;
- }
- assert(nvals == 1 || mk->sep != NULL);
- /*
- * Join all the values and print. Add curly brackets if there
- * is more than value and it can be a set.
- */
- if (!join(buf, sizeof(buf), nvals, vals, mk->sep ? mk->sep : "")) {
- errx(EXIT_FAILURE, "out of memory while parsing the rule");
- }
- easprintf(&p, nvals > 1 ? "{ %s }" : "%s", buf);
+ if (sublist.count) {
+ char *val, *elements;
- for (u_int i = 0; i < nvals; i++) {
- free(vals[i]);
+ elements = list_join_free(&sublist, true, ", ");
+ easprintf(&val, verified_fmt(mk->format, "%s"), elements );
+ list_push(&ctx->list[mk->list_id], val);
+ free(elements);
}
- return p;
}
static void
npfctl_print_id(npf_conf_info_t *ctx, nl_rule_t *rl)
{
- uint64_t id = id = npf_rule_getid(rl);
- fprintf(ctx->fp, "# id=\"%" PRIx64 "\" ", id);
+ const uint64_t id = npf_rule_getid(rl);
+
+ if (id) {
+ ctx->fpos += fprintf(ctx->fp, "# id=\"%" PRIx64 "\" ", id);
+ }
}
static void
+npfctl_print_filter_generic(npf_conf_info_t *ctx)
+{
+ elem_list_t *list = &ctx->list[LIST_PROTO];
+
+ if (list->count) {
+ char *elements = list_join_free(list, false, " ");
+ ctx->fpos += fprintf(ctx->fp, "%s ", elements);
+ free(elements);
+ }
+}
+
+static bool
+npfctl_print_filter_seg(npf_conf_info_t *ctx, unsigned which)
+{
+ static const struct {
+ const char * keyword;
+ unsigned alist;
+ unsigned plist;
+ unsigned negbm;
+ } refs[] = {
+ [NPF_SRC] = {
+ .keyword = "from",
+ .alist = LIST_SADDR,
+ .plist = LIST_SPORT,
+ .negbm = UINT64_C(1) << BM_SRC_NEG,
+ },
+ [NPF_DST] = {
+ .keyword = "to",
+ .alist = LIST_DADDR,
+ .plist = LIST_DPORT,
+ .negbm = UINT64_C(1) << BM_DST_NEG,
+ }
+ };
+ const char *neg = !!(ctx->seen_marks & refs[which].negbm) ? "! " : "";
+ const char *kwd = refs[which].keyword;
+ bool seen_filter = false;
+ elem_list_t *list;
+ char *elements;
+
+ list = &ctx->list[refs[which].alist];
+ if (list->count != 0) {
+ seen_filter = true;
+ elements = list_join_free(list, true, ", ");
+ ctx->fpos += fprintf(ctx->fp, "%s %s%s ", kwd, neg, elements);
+ free(elements);
+ }
+
+ list = &ctx->list[refs[which].plist];
+ if (list->count != 0) {
+ if (!seen_filter) {
+ ctx->fpos += fprintf(ctx->fp, "%s any ", kwd);
+ seen_filter = true;
+ }
+ elements = list_join_free(list, true, ", ");
+ ctx->fpos += fprintf(ctx->fp, "port %s ", elements);
+ free(elements);
+ }
+ return seen_filter;
+}
+
+static bool
npfctl_print_filter(npf_conf_info_t *ctx, nl_rule_t *rl)
{
const void *marks;
size_t mlen, len;
const void *code;
+ bool seenf = false;
int type;
marks = npf_rule_getinfo(rl, &mlen);
@@ -381,70 +490,70 @@ npfctl_print_filter(npf_conf_info_t *ctx, nl_rule_t *rl)
* have been filled by libpcap(3) or possibly an unknown
* to us byte-code.
*/
- fprintf(ctx->fp, "%s ", type == NPF_CODE_BPF ?
+ ctx->fpos += fprintf(ctx->fp, "%s ", type == NPF_CODE_BPF ?
"pcap-filter \"...\"" : "unrecognized-bytecode");
- return;
+ return true;
}
ctx->flags = 0;
/*
* BPF filter criteria described by the byte-code marks.
*/
- for (u_int i = 0; i < __arraycount(mark_keyword_map); i++) {
+ for (unsigned i = 0; i < __arraycount(mark_keyword_map); i++) {
const struct mark_keyword_mapent *mk = &mark_keyword_map[i];
- char *val;
-
- if ((val = scan_marks(ctx, mk, marks, mlen)) != NULL) {
- fprintf(ctx->fp, verified_fmt(mk->token, "%s"), val);
- fputs(" ", ctx->fp);
- free(val);
- }
- }
- if (!mlen) {
- fputs("all ", ctx->fp);
+ scan_marks(ctx, mk, marks, mlen);
}
+ npfctl_print_filter_generic(ctx);
+ seenf |= npfctl_print_filter_seg(ctx, NPF_SRC);
+ seenf |= npfctl_print_filter_seg(ctx, NPF_DST);
+ return seenf;
}
static void
-npfctl_print_rule(npf_conf_info_t *ctx, nl_rule_t *rl)
+npfctl_print_rule(npf_conf_info_t *ctx, nl_rule_t *rl, unsigned level)
{
const uint32_t attr = npf_rule_getattr(rl);
const char *rproc, *ifname, *name;
+ bool dyn_ruleset;
/* Rule attributes/flags. */
- for (u_int i = 0; i < __arraycount(attr_keyword_map); i++) {
+ for (unsigned i = 0; i < __arraycount(attr_keyword_map); i++) {
const struct attr_keyword_mapent *ak = &attr_keyword_map[i];
if (i == NAME_AT && (name = npf_rule_getname(rl)) != NULL) {
- fprintf(ctx->fp, "\"%s\" ", name);
+ ctx->fpos += fprintf(ctx->fp, "\"%s\" ", name);
}
if ((attr & ak->mask) == ak->flags) {
- fprintf(ctx->fp, "%s ", ak->val);
+ ctx->fpos += fprintf(ctx->fp, "%s ", ak->val);
}
}
if ((ifname = npf_rule_getinterface(rl)) != NULL) {
- fprintf(ctx->fp, "on %s ", ifname);
+ ctx->fpos += fprintf(ctx->fp, "on %s ", ifname);
}
if (attr == (NPF_RULE_GROUP | NPF_RULE_IN | NPF_RULE_OUT) && !ifname) {
/* The default group is a special case. */
- fprintf(ctx->fp, "default ");
+ ctx->fpos += fprintf(ctx->fp, "default ");
}
if ((attr & NPF_DYNAMIC_GROUP) == NPF_RULE_GROUP) {
/* Group; done. */
- fprintf(ctx->fp, "{ ");
+ ctx->fpos += fprintf(ctx->fp, "{ ");
+ ctx->glevel = level;
goto out;
}
/* Print filter criteria. */
- npfctl_print_filter(ctx, rl);
+ dyn_ruleset = (attr & NPF_DYNAMIC_GROUP) == NPF_DYNAMIC_GROUP;
+ if (!npfctl_print_filter(ctx, rl) && !dyn_ruleset) {
+ ctx->fpos += fprintf(ctx->fp, "all ");
+ }
/* Rule procedure. */
if ((rproc = npf_rule_getproc(rl)) != NULL) {
- fprintf(ctx->fp, "apply \"%s\" ", rproc);
+ ctx->fpos += fprintf(ctx->fp, "apply \"%s\" ", rproc);
}
out:
npfctl_print_id(ctx, rl);
- fputs("\n", ctx->fp);
+ ctx->fpos += fprintf(ctx->fp, "\n");
}
static void
@@ -467,7 +576,8 @@ npfctl_print_nat(npf_conf_info_t *ctx, nl_nat_t *nt)
if ((npf_rule_getattr(rl) & dynamic_natset) == dynamic_natset) {
const char *name = npf_rule_getname(rl);
- fprintf(ctx->fp, "map ruleset \"%s\" on %s\n", name, ifname);
+ ctx->fpos += fprintf(ctx->fp,
+ "map ruleset \"%s\" on %s\n", name, ifname);
return;
}
@@ -517,23 +627,23 @@ npfctl_print_nat(npf_conf_info_t *ctx, nl_nat_t *nt)
algo = "algo round-robin ";
break;
case NPF_ALGO_NPT66:
- algo = "algo npt66";
+ algo = "algo npt66 ";
break;
default:
algo = "";
break;
}
- /* FIXME also handle "any" */
+ /* XXX also handle "any" */
/* Print out the NAT policy with the filter criteria. */
- fprintf(ctx->fp, "map %s %s %s%s%s %s %s pass ",
+ ctx->fpos += fprintf(ctx->fp, "map %s %s %s%s%s %s %s pass ",
ifname, (flags & NPF_NAT_STATIC) ? "static" : "dynamic",
algo, (flags & NPF_NAT_PORTS) ? "" : "no-ports ",
seg1, arrow, seg2);
npfctl_print_filter(ctx, rl);
npfctl_print_id(ctx, rl);
- fputs("\n", ctx->fp);
+ ctx->fpos += fprintf(ctx->fp, "\n");
free(seg);
}
@@ -553,13 +663,31 @@ npfctl_print_table(npf_conf_info_t *ctx, nl_table_t *tl)
return;
}
assert(type < __arraycount(table_types));
- fprintf(ctx->fp, "table <%s> type %s\n", name, table_types[type]);
+ ctx->fpos += fprintf(ctx->fp,
+ "table <%s> type %s\n", name, table_types[type]);
+}
+
+static void
+npfctl_print_params(npf_conf_info_t *ctx, nl_config_t *ncf)
+{
+ nl_iter_t i = NPF_ITER_BEGIN;
+ int val, defval, *dval;
+ const char *name;
+
+ dval = ctx->validating ? NULL : &defval;
+ while ((name = npf_param_iterate(ncf, &i, &val, dval)) != NULL) {
+ if (dval && val == *dval) {
+ continue;
+ }
+ ctx->fpos += fprintf(ctx->fp, "set %s %d\n", name, val);
+ }
+ print_linesep(ctx);
}
int
npfctl_config_show(int fd)
{
- npf_conf_info_t *ctx = &stdout_ctx;
+ npf_conf_info_t *ctx = npfctl_show_init();
nl_config_t *ncf;
bool loaded;
@@ -569,13 +697,16 @@ npfctl_config_show(int fd)
return errno;
}
loaded = npf_config_loaded_p(ncf);
- fprintf(ctx->fp, "# filtering:\t%s\n# config:\t%s\n",
+ ctx->validating = false;
+ ctx->fpos += fprintf(ctx->fp,
+ "# filtering:\t%s\n# config:\t%s\n",
npf_config_active_p(ncf) ? "active" : "inactive",
loaded ? "loaded" : "empty");
print_linesep(ctx);
} else {
ncf = npfctl_config_ref();
npfctl_config_build();
+ ctx->validating = true;
loaded = true;
}
ctx->conf = ncf;
@@ -588,6 +719,8 @@ npfctl_config_show(int fd)
nl_iter_t i;
unsigned level;
+ npfctl_print_params(ctx, ncf);
+
i = NPF_ITER_BEGIN;
while ((tl = npf_table_iterate(ncf, &i)) != NULL) {
npfctl_print_table(ctx, tl);
@@ -597,7 +730,8 @@ npfctl_config_show(int fd)
i = NPF_ITER_BEGIN;
while ((rp = npf_rproc_iterate(ncf, &i)) != NULL) {
const char *rpname = npf_rproc_getname(rp);
- fprintf(ctx->fp, "procedure \"%s\"\n", rpname);
+ ctx->fpos += fprintf(ctx->fp,
+ "procedure \"%s\"\n", rpname);
}
print_linesep(ctx);
@@ -610,7 +744,7 @@ npfctl_config_show(int fd)
i = NPF_ITER_BEGIN;
while ((rl = npf_rule_iterate(ncf, &i, &level)) != NULL) {
print_indent(ctx, level);
- npfctl_print_rule(ctx, rl);
+ npfctl_print_rule(ctx, rl, level);
}
print_indent(ctx, 0);
}
@@ -621,7 +755,7 @@ npfctl_config_show(int fd)
int
npfctl_ruleset_show(int fd, const char *ruleset_name)
{
- npf_conf_info_t *ctx = &stdout_ctx;
+ npf_conf_info_t *ctx = npfctl_show_init();
nl_config_t *ncf;
nl_rule_t *rl;
unsigned level;
@@ -636,7 +770,7 @@ npfctl_ruleset_show(int fd, const char *ruleset_name)
}
i = NPF_ITER_BEGIN;
while ((rl = npf_rule_iterate(ncf, &i, &level)) != NULL) {
- npfctl_print_rule(ctx, rl);
+ npfctl_print_rule(ctx, rl, 0);
}
npf_config_destroy(ncf);
return error;
diff --git a/usr.sbin/npf/npfctl/npf_var.c b/usr.sbin/npf/npfctl/npf_var.c
index 4f18abe1add..afb2e808e6e 100644
--- a/usr.sbin/npf/npfctl/npf_var.c
+++ b/usr.sbin/npf/npfctl/npf_var.c
@@ -27,8 +27,14 @@
* POSSIBILITY OF SUCH DAMAGE.
*/
+/*
+ * NPF variables are used to build the intermediate representation (IR)
+ * of the configuration grammar. They represent primitive types (strings,
+ * numbers, etc) as well as complex types (address and mask, table, etc).
+ */
+
#include <sys/cdefs.h>
-__RCSID("$NetBSD: npf_var.c,v 1.12 2019/01/19 21:19:32 rmind Exp $");
+__RCSID("$NetBSD: npf_var.c,v 1.12.2.1 2020/06/20 15:46:48 martin Exp $");
#include <stdlib.h>
#include <string.h>
@@ -39,7 +45,7 @@ __RCSID("$NetBSD: npf_var.c,v 1.12 2019/01/19 21:19:32 rmind Exp $");
typedef struct npf_element {
void * e_data;
- int e_type;
+ unsigned e_type;
struct npf_element *e_next;
} npf_element_t;
@@ -47,7 +53,6 @@ struct npfvar {
char * v_key;
npf_element_t * v_elements;
npf_element_t * v_last;
- int v_type;
size_t v_count;
void * v_next;
};
@@ -90,43 +95,36 @@ npfvar_add(npfvar_t *vp, const char *name)
}
npfvar_t *
-npfvar_create_element(int type, const void *data, size_t len)
+npfvar_create_element(unsigned type, const void *data, size_t len)
{
npfvar_t *vp = npfvar_create();
return npfvar_add_element(vp, type, data, len);
}
npfvar_t *
-npfvar_create_from_string(int type, const char *string)
+npfvar_create_from_string(unsigned type, const char *string)
{
return npfvar_create_element(type, string, strlen(string) + 1);
}
npfvar_t *
-npfvar_add_element(npfvar_t *vp, int type, const void *data, size_t len)
+npfvar_add_element(npfvar_t *vp, unsigned type, const void *data, size_t len)
{
npf_element_t *el;
- if (vp->v_count == 0) {
- vp->v_type = type;
- } else if (NPFVAR_TYPE(vp->v_type) != NPFVAR_TYPE(type)) {
- yyerror("element type '%s' does not match variable type '%s'",
- npfvar_type(type), npfvar_type(vp->v_type));
- return NULL;
- }
- vp->v_count++;
el = ecalloc(1, sizeof(*el));
el->e_data = ecalloc(1, len);
el->e_type = type;
memcpy(el->e_data, data, len);
- /* Preserve order of insertion. */
+ /* Preserve the order of insertion. */
if (vp->v_elements == NULL) {
vp->v_elements = el;
} else {
vp->v_last->e_next = el;
}
vp->v_last = el;
+ vp->v_count++;
return vp;
}
@@ -140,17 +138,9 @@ npfvar_add_elements(npfvar_t *vp, npfvar_t *vp2)
if (vp->v_elements == NULL) {
if (vp2->v_elements) {
- vp->v_type = vp2->v_type;
vp->v_elements = vp2->v_elements;
}
} else if (vp2->v_elements) {
- if (NPFVAR_TYPE(vp->v_type) != NPFVAR_TYPE(vp2->v_type)) {
- yyerror("variable '%s' type '%s' does not match "
- "variable '%s' type '%s'", vp->v_key,
- npfvar_type(vp->v_type),
- vp2->v_key, npfvar_type(vp2->v_type));
- return NULL;
- }
vp->v_last->e_next = vp2->v_elements;
}
if (vp2->v_elements) {
@@ -187,8 +177,8 @@ char *
npfvar_expand_string(const npfvar_t *vp)
{
if (npfvar_get_count(vp) != 1) {
- yyerror("variable '%s' type '%s' has %zu elements", vp->v_key,
- npfvar_type(vp->v_type), npfvar_get_count(vp));
+ yyerror("variable '%s' has multiple elements", vp->v_key);
+ return NULL;
}
return npfvar_get_data(vp, NPFVAR_STRING, 0);
}
@@ -199,82 +189,62 @@ npfvar_get_count(const npfvar_t *vp)
return vp ? vp->v_count : 0;
}
-static void *
-npfvar_get_data1(const npfvar_t *vp, int type, size_t idx, size_t level)
+static npf_element_t *
+npfvar_get_element(const npfvar_t *vp, size_t idx, size_t level)
{
npf_element_t *el;
- if (level >= var_num) {
- yyerror("variable loop for '%s'", vp->v_key);
+ /*
+ * Verify the parameters.
+ */
+ if (vp == NULL) {
return NULL;
}
-
- if (vp == NULL)
- return NULL;
-
- if (NPFVAR_TYPE(vp->v_type) != NPFVAR_TYPE(type)) {
- yyerror("variable '%s' is of type '%s' not '%s'", vp->v_key,
- npfvar_type(vp->v_type), npfvar_type(type));
+ if (level >= var_num) {
+ yyerror("circular dependency for variable '%s'", vp->v_key);
return NULL;
}
-
if (vp->v_count <= idx) {
yyerror("variable '%s' has only %zu elements, requested %zu",
vp->v_key, vp->v_count, idx);
return NULL;
}
+ /*
+ * Get the element at the given index.
+ */
el = vp->v_elements;
while (idx--) {
el = el->e_next;
}
- if (vp->v_type == NPFVAR_VAR_ID) {
- npfvar_t *rvp = npfvar_lookup(el->e_data);
- return npfvar_get_data1(rvp, type, 0, level + 1);
+ /*
+ * Resolve if it is a reference to another variable.
+ */
+ if (el->e_type == NPFVAR_VAR_ID) {
+ const npfvar_t *rvp = npfvar_lookup(el->e_data);
+ return npfvar_get_element(rvp, 0, level + 1);
}
- return el->e_data;
-}
-
-static int
-npfvar_get_type1(const npfvar_t *vp, size_t idx, size_t level)
-{
- npf_element_t *el;
-
- if (vp == NULL)
- return -1;
-
- if (level >= var_num) {
- yyerror("variable loop for '%s'", vp->v_key);
- return -1;
- }
-
- if (vp->v_count <= idx) {
- yyerror("variable '%s' has only %zu elements, requested %zu",
- vp->v_key, vp->v_count, idx);
- return -1;
- }
-
- el = vp->v_elements;
- while (idx--) {
- el = el->e_next;
- }
-
- if (vp->v_type == NPFVAR_VAR_ID) {
- npfvar_t *rvp = npfvar_lookup(el->e_data);
- return npfvar_get_type1(rvp, 0, level + 1);
- }
- return el->e_type;
+ return el;
}
int
npfvar_get_type(const npfvar_t *vp, size_t idx)
{
- return npfvar_get_type1(vp, idx, 0);
+ npf_element_t *el = npfvar_get_element(vp, idx, 0);
+ return el ? (int)el->e_type : -1;
}
void *
-npfvar_get_data(const npfvar_t *vp, int type, size_t idx)
+npfvar_get_data(const npfvar_t *vp, unsigned type, size_t idx)
{
- return npfvar_get_data1(vp, type, idx, 0);
+ npf_element_t *el = npfvar_get_element(vp, idx, 0);
+
+ if (el && NPFVAR_TYPE(el->e_type) != NPFVAR_TYPE(type)) {
+ yyerror("variable '%s' element %zu "
+ "is of type '%s' rather than '%s'", vp->v_key,
+ idx, npfvar_type(el->e_type), npfvar_type(type));
+ return NULL;
+ }
+ return el->e_data;
}
diff --git a/usr.sbin/npf/npfctl/npf_var.h b/usr.sbin/npf/npfctl/npf_var.h
index 0968e1381de..cf92e7d4273 100644
--- a/usr.sbin/npf/npfctl/npf_var.h
+++ b/usr.sbin/npf/npfctl/npf_var.h
@@ -47,6 +47,7 @@
#define NPFVAR_TCPFLAG 9
#define NPFVAR_ICMP 10
#define NPFVAR_INTERFACE 11
+#define NPFVAR_PROTO 12
#ifdef _NPFVAR_PRIVATE
static const char *npfvar_types[ ] = {
@@ -61,7 +62,8 @@ static const char *npfvar_types[ ] = {
[NPFVAR_PROC_PARAM] = "procedure-parameter",
[NPFVAR_TCPFLAG] = "tcp-flag",
[NPFVAR_ICMP] = "icmp",
- [NPFVAR_INTERFACE] = "interface-address"
+ [NPFVAR_INTERFACE] = "interface-address",
+ [NPFVAR_PROTO] = "proto",
};
#endif
@@ -69,18 +71,18 @@ struct npfvar;
typedef struct npfvar npfvar_t;
npfvar_t * npfvar_create(void);
-npfvar_t * npfvar_create_element(int, const void *, size_t);
-npfvar_t * npfvar_create_from_string(int, const char *);
+npfvar_t * npfvar_create_element(unsigned, const void *, size_t);
+npfvar_t * npfvar_create_from_string(unsigned, const char *);
npfvar_t * npfvar_lookup(const char *);
const char * npfvar_type(size_t);
void npfvar_add(npfvar_t *, const char *);
-npfvar_t * npfvar_add_element(npfvar_t *, int, const void *, size_t);
+npfvar_t * npfvar_add_element(npfvar_t *, unsigned, const void *, size_t);
npfvar_t * npfvar_add_elements(npfvar_t *, npfvar_t *);
void npfvar_destroy(npfvar_t *);
char * npfvar_expand_string(const npfvar_t *);
size_t npfvar_get_count(const npfvar_t *);
int npfvar_get_type(const npfvar_t *, size_t);
-void * npfvar_get_data(const npfvar_t *, int, size_t);
+void * npfvar_get_data(const npfvar_t *, unsigned, size_t);
#endif
diff --git a/usr.sbin/npf/npfctl/npfctl.8 b/usr.sbin/npf/npfctl/npfctl.8
index 71f0f9970d9..d2c41c715ae 100644
--- a/usr.sbin/npf/npfctl/npfctl.8
+++ b/usr.sbin/npf/npfctl/npfctl.8
@@ -1,4 +1,4 @@
-.\" $NetBSD: npfctl.8,v 1.21.2.1 2019/10/04 08:06:34 martin Exp $
+.\" $NetBSD: npfctl.8,v 1.21.2.2 2020/06/20 15:46:48 martin Exp $
.\"
.\" Copyright (c) 2009-2014 The NetBSD Foundation, Inc.
.\" All rights reserved.
@@ -27,7 +27,7 @@
.\" ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
.\" POSSIBILITY OF SUCH DAMAGE.
.\"
-.Dd August 26, 2019
+.Dd May 12, 2020
.Dt NPFCTL 8
.Os
.Sh NAME
@@ -85,6 +85,10 @@ The configuration file at
.Pa /etc/npf.conf
will be used unless a file is specified by
.Ar path .
+The
+.Ar path
+may also be set to "-" for reading from
+.Em stdin .
.\" ---
.It Ic rule Ar name Ic add Aq rule-syntax
Add a rule to a dynamic ruleset specified by
@@ -159,11 +163,12 @@ or
If not specified, the type of the table being replaced will be used.
.El
.\" ---
-.It Ic save
-Save the active configuration and a snapshot of the current connections.
+.It Ic save Op Ar path
+Save the active configuration with a snapshot of the current connections.
The data will be stored in the
.Pa /var/db/npf.db
-file.
+file unless a file is specified by
+.Ar path .
Administrator may want to stop the packet inspection before saving.
.It Ic load
Load the saved configuration file and the connections from the file.
@@ -171,11 +176,22 @@ Note that any existing connections will be destroyed.
Administrator may want to start packet inspection after the load.
.It Ic stats
Print various statistics.
-.It Ic debug
-Process the configuration file, print the byte-code of each rule
-and dump the raw configuration.
+.It Ic debug ( Fl a | Fl b Ar binary-config | Fl c Ar config ) \
+Oo Fl o Ar outfile Oc
+Process the active configuration (if
+.Fl a
+is set), the given binary configuration (if
+.Fl b
+is set) or the given the plain configuration (if
+.Fl c
+is set).
+Print the byte-code of each rule and the encoded configuration data.
+Also, if
+.Fl o
+is set, write the binary configuration data into the given file.
+.Pp
This is primarily for developer use.
-.It Ic list Oo Fl 46hNnw Oc Op Fl i Ar ifname
+.It Ic list Oo Fl 46hNnW Oc Op Fl i Ar ifname
Display a list of tracked connections:
.Bl -tag -width xxxxxxxxx -compact -offset 3n
.It Fl 4
@@ -188,8 +204,8 @@ Don't display a header.
Try to resolve addresses.
.It Fl n
Only show NAT connections.
-.It Fl w
-Don't restrict display width.
+.It Fl W
+Restrict the display width.
.It Fl i Ar ifname
Display only connections through the named interface.
.El
diff --git a/usr.sbin/npf/npfctl/npfctl.c b/usr.sbin/npf/npfctl/npfctl.c
index 4f90cfb9d10..71c7722fca0 100644
--- a/usr.sbin/npf/npfctl/npfctl.c
+++ b/usr.sbin/npf/npfctl/npfctl.c
@@ -28,29 +28,24 @@
*/
#include <sys/cdefs.h>
-__RCSID("$NetBSD: npfctl.c,v 1.60.2.2 2019/10/04 08:06:34 martin Exp $");
+__RCSID("$NetBSD: npfctl.c,v 1.60.2.3 2020/06/20 15:46:48 martin Exp $");
-#include <sys/stat.h>
#include <sys/types.h>
+#include <sys/stat.h>
+#include <sys/socket.h>
#include <sys/mman.h>
+#include <sys/un.h>
#ifdef __NetBSD__
-#include <sha1.h>
-#include <sys/ioctl.h>
#include <sys/module.h>
-#define SHA_DIGEST_LENGTH SHA1_DIGEST_LENGTH
-#else
-#include <openssl/sha.h>
#endif
#include <stdio.h>
-#include <stdlib.h>
#include <string.h>
-#include <err.h>
-#include <fcntl.h>
+#include <stdlib.h>
#include <unistd.h>
+#include <fcntl.h>
#include <errno.h>
-
-#include <arpa/inet.h>
+#include <err.h>
#include "npfctl.h"
@@ -70,37 +65,10 @@ enum {
NPFCTL_CONN_LIST,
};
-static const struct operations_s {
- const char * cmd;
- int action;
-} operations[] = {
- /* Start, stop, reload */
- { "start", NPFCTL_START },
- { "stop", NPFCTL_STOP },
- { "reload", NPFCTL_RELOAD },
- { "show", NPFCTL_SHOWCONF, },
- { "flush", NPFCTL_FLUSH },
- /* Table */
- { "table", NPFCTL_TABLE },
- /* Rule */
- { "rule", NPFCTL_RULE },
- /* Stats */
- { "stats", NPFCTL_STATS },
- /* Full state save/load */
- { "save", NPFCTL_SAVE },
- { "load", NPFCTL_LOAD },
- { "list", NPFCTL_CONN_LIST },
- /* Misc. */
- { "valid", NPFCTL_VALIDATE },
- { "debug", NPFCTL_DEBUG },
- /* --- */
- { NULL, 0 }
-};
-
bool
join(char *buf, size_t buflen, int count, char **args, const char *sep)
{
- const u_int seplen = strlen(sep);
+ const unsigned seplen = strlen(sep);
char *s = buf, *p = NULL;
for (int i = 0; i < count; i++) {
@@ -119,7 +87,7 @@ join(char *buf, size_t buflen, int count, char **args, const char *sep)
return true;
}
-__dead static void
+__dead void
usage(void)
{
const char *progname = getprogname();
@@ -156,7 +124,8 @@ usage(void)
"\t%s list [-46hNnw] [-i <ifname>]\n",
progname);
fprintf(stderr,
- "\t%s debug [<rule-file>] [<raw-output>]\n",
+ "\t%s debug { -a | -b <binary-config> | -c <config> } "
+ "[ -o <outfile> ]\n",
progname);
exit(EXIT_FAILURE);
}
@@ -267,7 +236,7 @@ npfctl_print_addrmask(int alen, const char *fmt, const npf_addr_t *addr,
break;
}
default:
- assert(false);
+ abort();
}
sockaddr_snprintf(buf, buflen, fmt, (const void *)&ss);
if (mask && mask != NPF_NO_NETMASK) {
@@ -277,346 +246,11 @@ npfctl_print_addrmask(int alen, const char *fmt, const npf_addr_t *addr,
return buf;
}
-static int
-npfctl_table_type(const char *typename)
-{
- static const struct tbltype_s {
- const char * name;
- unsigned type;
- } tbltypes[] = {
- { "ipset", NPF_TABLE_IPSET },
- { "lpm", NPF_TABLE_LPM },
- { "const", NPF_TABLE_CONST },
- { NULL, 0 }
- };
-
- for (unsigned i = 0; tbltypes[i].name != NULL; i++) {
- if (strcmp(typename, tbltypes[i].name) == 0) {
- return tbltypes[i].type;
- }
- }
- return 0;
-}
-
-static void
-npfctl_table_replace(int fd, int argc, char **argv)
-{
- const char *name, *newname, *path, *typename = NULL;
- int c, tid = -1;
- FILE *fp;
- nl_config_t *ncf;
- nl_table_t *t;
- u_int type = 0;
-
- name = newname = argv[0];
- optind = 2;
- while ((c = getopt(argc, argv, "n:t:")) != -1) {
- switch (c) {
- case 't':
- typename = optarg;
- break;
- case 'n':
- newname = optarg;
- break;
- default:
- fprintf(stderr,
- "Usage: %s table \"table-name\" replace "
- "[-n \"name\"] [-t <type>] <table-file>\n",
- getprogname());
- exit(EXIT_FAILURE);
- }
- }
- argc -= optind;
- argv += optind;
-
- if (typename && (type = npfctl_table_type(typename)) == 0) {
- errx(EXIT_FAILURE, "unsupported table type '%s'", typename);
- }
-
- if (argc != 1) {
- usage();
- }
-
- path = argv[0];
- if (strcmp(path, "-") == 0) {
- path = "stdin";
- fp = stdin;
- } else if ((fp = fopen(path, "r")) == NULL) {
- err(EXIT_FAILURE, "open '%s'", path);
- }
-
- /* Get existing config to lookup ID of existing table */
- if ((ncf = npf_config_retrieve(fd)) == NULL) {
- err(EXIT_FAILURE, "npf_config_retrieve()");
- }
- if ((t = npfctl_table_getbyname(ncf, name)) == NULL) {
- errx(EXIT_FAILURE,
- "table '%s' not found in the active configuration", name);
- }
- tid = npf_table_getid(t);
- if (!type) {
- type = npf_table_gettype(t);
- }
- npf_config_destroy(ncf);
-
- if ((t = npfctl_load_table(newname, tid, type, path, fp)) == NULL) {
- err(EXIT_FAILURE, "table load failed");
- }
-
- if (npf_table_replace(fd, t, NULL)) {
- err(EXIT_FAILURE, "npf_table_replace(<%s>)", name);
- }
-}
-
-static void
-npfctl_table(int fd, int argc, char **argv)
-{
- static const struct tblops_s {
- const char * cmd;
- int action;
- } tblops[] = {
- { "add", NPF_CMD_TABLE_ADD },
- { "rem", NPF_CMD_TABLE_REMOVE },
- { "del", NPF_CMD_TABLE_REMOVE },
- { "test", NPF_CMD_TABLE_LOOKUP },
- { "list", NPF_CMD_TABLE_LIST },
- { "flush", NPF_CMD_TABLE_FLUSH },
- { NULL, 0 }
- };
- npf_ioctl_table_t nct;
- fam_addr_mask_t fam;
- size_t buflen = 512;
- char *cmd, *arg;
- int n, alen;
-
- /* Default action is list. */
- memset(&nct, 0, sizeof(npf_ioctl_table_t));
- nct.nct_name = argv[0];
- cmd = argv[1];
-
- for (n = 0; tblops[n].cmd != NULL; n++) {
- if (strcmp(cmd, tblops[n].cmd) != 0) {
- continue;
- }
- nct.nct_cmd = tblops[n].action;
- break;
- }
- if (tblops[n].cmd == NULL) {
- errx(EXIT_FAILURE, "invalid command '%s'", cmd);
- }
-
- switch (nct.nct_cmd) {
- case NPF_CMD_TABLE_LIST:
- case NPF_CMD_TABLE_FLUSH:
- arg = NULL;
- break;
- default:
- if (argc < 3) {
- usage();
- }
- arg = argv[2];
- }
-
-again:
- switch (nct.nct_cmd) {
- case NPF_CMD_TABLE_LIST:
- nct.nct_data.buf.buf = ecalloc(1, buflen);
- nct.nct_data.buf.len = buflen;
- break;
- case NPF_CMD_TABLE_FLUSH:
- break;
- default:
- if (!npfctl_parse_cidr(arg, &fam, &alen)) {
- errx(EXIT_FAILURE, "invalid CIDR '%s'", arg);
- }
- nct.nct_data.ent.alen = alen;
- memcpy(&nct.nct_data.ent.addr, &fam.fam_addr, alen);
- nct.nct_data.ent.mask = fam.fam_mask;
- }
-
- if (ioctl(fd, IOC_NPF_TABLE, &nct) != -1) {
- errno = 0;
- }
- switch (errno) {
- case 0:
- break;
- case EEXIST:
- errx(EXIT_FAILURE, "entry already exists or is conflicting");
- case ENOENT:
- errx(EXIT_FAILURE, "not found");
- case EINVAL:
- errx(EXIT_FAILURE, "invalid address, mask or table ID");
- case ENOMEM:
- if (nct.nct_cmd == NPF_CMD_TABLE_LIST) {
- /* XXX */
- free(nct.nct_data.buf.buf);
- buflen <<= 1;
- goto again;
- }
- /* FALLTHROUGH */
- default:
- err(EXIT_FAILURE, "ioctl(IOC_NPF_TABLE)");
- }
-
- if (nct.nct_cmd == NPF_CMD_TABLE_LIST) {
- npf_ioctl_ent_t *ent = nct.nct_data.buf.buf;
- char *buf;
-
- while (nct.nct_data.buf.len--) {
- if (!ent->alen)
- break;
- buf = npfctl_print_addrmask(ent->alen, "%a",
- &ent->addr, ent->mask);
- puts(buf);
- ent++;
- }
- free(nct.nct_data.buf.buf);
- } else {
- printf("%s: %s\n", getprogname(),
- nct.nct_cmd == NPF_CMD_TABLE_LOOKUP ?
- "match" : "success");
- }
-}
-
-static nl_rule_t *
-npfctl_parse_rule(int argc, char **argv, parse_entry_t entry)
-{
- char rule_string[1024];
- nl_rule_t *rl;
-
- /* Get the rule string and parse it. */
- if (!join(rule_string, sizeof(rule_string), argc, argv, " ")) {
- errx(EXIT_FAILURE, "command too long");
- }
- npfctl_parse_string(rule_string, entry);
- if ((rl = npfctl_rule_ref()) == NULL) {
- errx(EXIT_FAILURE, "could not parse the rule");
- }
- return rl;
-}
-
-#ifdef __NetBSD__
-static unsigned char *
-SHA1(const unsigned char *d, size_t l, unsigned char *md)
-{
- SHA1_CTX c;
-
- SHA1Init(&c);
- SHA1Update(&c, d, l);
- SHA1Final(md, &c);
- return md;
-}
-#endif
-
-static void
-npfctl_generate_key(nl_rule_t *rl, void *key)
-{
- void *meta;
- size_t len;
-
- if ((meta = npf_rule_export(rl, &len)) == NULL) {
- errx(EXIT_FAILURE, "error generating rule key");
- }
- __CTASSERT(NPF_RULE_MAXKEYLEN >= SHA_DIGEST_LENGTH);
- memset(key, 0, NPF_RULE_MAXKEYLEN);
- SHA1(meta, len, key);
- free(meta);
-}
-
-int
-npfctl_nat_ruleset_p(const char *name, bool *natset)
+bool
+npfctl_addr_iszero(const npf_addr_t *addr)
{
- const size_t preflen = sizeof(NPF_RULESET_MAP_PREF) - 1;
- *natset = strncmp(name, NPF_RULESET_MAP_PREF, preflen) == 0;
- return (*natset && strlen(name) <= preflen) ? -1 : 0;
-}
-
-static void
-npfctl_rule(int fd, int argc, char **argv)
-{
- static const struct ruleops_s {
- const char * cmd;
- int action;
- bool extra_arg;
- } ruleops[] = {
- { "add", NPF_CMD_RULE_ADD, true },
- { "rem", NPF_CMD_RULE_REMKEY, true },
- { "del", NPF_CMD_RULE_REMKEY, true },
- { "rem-id", NPF_CMD_RULE_REMOVE, true },
- { "list", NPF_CMD_RULE_LIST, false },
- { "flush", NPF_CMD_RULE_FLUSH, false },
- { NULL, 0, 0 }
- };
- uint8_t key[NPF_RULE_MAXKEYLEN];
- const char *ruleset_name = argv[0];
- const char *cmd = argv[1];
- int error, action = 0;
- bool extra_arg, natset;
- parse_entry_t entry;
- uint64_t rule_id;
- nl_rule_t *rl;
-
- for (unsigned n = 0; ruleops[n].cmd != NULL; n++) {
- if (strcmp(cmd, ruleops[n].cmd) == 0) {
- action = ruleops[n].action;
- extra_arg = ruleops[n].extra_arg;
- break;
- }
- }
- argc -= 2;
- argv += 2;
-
- if (!action || (extra_arg && argc == 0)) {
- usage();
- }
-
- if (npfctl_nat_ruleset_p(ruleset_name, &natset) != 0) {
- errx(EXIT_FAILURE,
- "invalid NAT ruleset name (note: the name must be "
- "prefixed with `" NPF_RULESET_MAP_PREF "`)");
- }
- entry = natset ? NPFCTL_PARSE_MAP : NPFCTL_PARSE_RULE;
-
- switch (action) {
- case NPF_CMD_RULE_ADD:
- rl = npfctl_parse_rule(argc, argv, entry);
- npfctl_generate_key(rl, key);
- npf_rule_setkey(rl, key, sizeof(key));
- error = npf_ruleset_add(fd, ruleset_name, rl, &rule_id);
- break;
- case NPF_CMD_RULE_REMKEY:
- rl = npfctl_parse_rule(argc, argv, entry);
- npfctl_generate_key(rl, key);
- error = npf_ruleset_remkey(fd, ruleset_name, key, sizeof(key));
- break;
- case NPF_CMD_RULE_REMOVE:
- rule_id = strtoull(argv[0], NULL, 16);
- error = npf_ruleset_remove(fd, ruleset_name, rule_id);
- break;
- case NPF_CMD_RULE_LIST:
- error = npfctl_ruleset_show(fd, ruleset_name);
- break;
- case NPF_CMD_RULE_FLUSH:
- error = npf_ruleset_flush(fd, ruleset_name);
- break;
- default:
- abort();
- }
-
- switch (error) {
- case 0:
- /* Success. */
- break;
- case ESRCH:
- errx(EXIT_FAILURE, "ruleset \"%s\" not found", ruleset_name);
- case ENOENT:
- errx(EXIT_FAILURE, "rule was not found");
- default:
- errx(EXIT_FAILURE, "rule operation: %s", strerror(error));
- }
- if (action == NPF_CMD_RULE_ADD) {
- printf("OK %" PRIx64 "\n", rule_id);
- }
+ static const npf_addr_t zero; /* must be static */
+ return memcmp(addr, &zero, sizeof(npf_addr_t)) == 0;
}
static bool bpfjit = true;
@@ -653,165 +287,158 @@ npfctl_preload_bpfjit(void)
#endif
}
-static int
-npfctl_load(int fd)
+static nl_config_t *
+npfctl_import(const char *path)
{
nl_config_t *ncf;
- npf_error_t errinfo;
struct stat sb;
size_t blen;
void *blob;
- int error;
+ int fd;
/*
* The file may change while reading - we are not handling this,
- * leaving this responsibility for the caller.
+ * just leaving this responsibility for the caller.
*/
- if (stat(NPF_DB_PATH, &sb) == -1) {
+ if ((fd = open(path, O_RDONLY)) == -1) {
+ err(EXIT_FAILURE, "could not open `%s'", path);
+ }
+ if (fstat(fd, &sb) == -1) {
err(EXIT_FAILURE, "stat");
}
if ((blen = sb.st_size) == 0) {
- err(EXIT_FAILURE, "saved configuration file is empty");
+ err(EXIT_FAILURE, "the binary configuration file is empty");
}
- if ((blob = mmap(NULL, blen, PROT_READ,
- MAP_FILE | MAP_PRIVATE, fd, 0)) == MAP_FAILED) {
+ blob = mmap(NULL, blen, PROT_READ, MAP_FILE | MAP_PRIVATE, fd, 0);
+ if (blob == MAP_FAILED) {
err(EXIT_FAILURE, "mmap");
}
ncf = npf_config_import(blob, blen);
munmap(blob, blen);
- if (ncf == NULL) {
- return errno;
- }
+ return ncf;
+}
+
+static int
+npfctl_load(int fd)
+{
+ nl_config_t *ncf;
+ npf_error_t errinfo;
/*
- * Configuration imported - submit it now.
- **/
- errno = error = npf_config_submit(ncf, fd, &errinfo);
- if (error) {
+ * Import the configuration, submit it and destroy.
+ */
+ ncf = npfctl_import(NPF_DB_PATH);
+ if (ncf == NULL) {
+ err(EXIT_FAILURE, "npf_config_import");
+ }
+ if ((errno = npf_config_submit(ncf, fd, &errinfo)) != 0) {
npfctl_print_error(&errinfo);
}
npf_config_destroy(ncf);
- return error;
+ return errno;
}
-struct npf_conn_filter {
- uint16_t alen;
- const char * ifname;
- bool nat;
- bool wide;
- bool name;
- int width;
- FILE * fp;
-};
-
static int
-npfctl_conn_print(unsigned alen, const npf_addr_t *a, const in_port_t *p,
- const char *ifname, void *v)
+npfctl_open_dev(const char *path)
{
- struct npf_conn_filter *fil = v;
- FILE *fp = fil->fp;
- char *src, *dst;
-
- if (fil->ifname && strcmp(ifname, fil->ifname) != 0)
- return 0;
- if (fil->alen && alen != fil->alen)
- return 0;
- if (fil->nat && !p[2])
- return 0;
-
- int w = fil->width;
- const char *fmt = fil->name ? "%A" :
- (alen == sizeof(struct in_addr) ? "%a" : "[%a]");
- src = npfctl_print_addrmask(alen, fmt, &a[0], NPF_NO_NETMASK);
- dst = npfctl_print_addrmask(alen, fmt, &a[1], NPF_NO_NETMASK);
-
- if (fil->wide) {
- fprintf(fp, "%s:%d %s:%d", src, p[0], dst, p[1]);
- } else {
- fprintf(fp, "%*.*s:%-5d %*.*s:%-5d", w, w, src, p[0],
- w, w, dst, p[1]);
+ struct stat st;
+ int fd;
+
+ if (lstat(path, &st) == -1) {
+ err(EXIT_FAILURE, "fstat");
}
- free(src);
- free(dst);
- if (!p[2]) {
- fputc('\n', fp);
- return 1;
+ if ((st.st_mode & S_IFMT) == S_IFSOCK) {
+ struct sockaddr_un addr;
+
+ if ((fd = socket(AF_UNIX, SOCK_STREAM, 0)) == -1) {
+ err(EXIT_FAILURE, "socket");
+ }
+ memset(&addr, 0, sizeof(addr));
+ addr.sun_family = AF_UNIX;
+ strncpy(addr.sun_path, path, sizeof(addr.sun_path) - 1);
+
+ if (connect(fd, (struct sockaddr *)&addr, sizeof(addr)) == -1) {
+ err(EXIT_FAILURE, "connect");
+ }
+ } else {
+ if ((fd = open(path, O_RDONLY)) == -1) {
+ err(EXIT_FAILURE, "cannot open '%s'", path);
+ }
}
- fprintf(fp, " via %s:%d\n", ifname, p[2]);
- return 1;
+ return fd;
}
-static int
-npfctl_conn_list(int fd, int argc, char **argv)
+static void
+npfctl_debug(int argc, char **argv)
{
- struct npf_conn_filter f;
- int c, w, header = true;
+ const char *conf = NULL, *bconf = NULL, *outfile = NULL;
+ bool use_active = false;
+ nl_config_t *ncf = NULL;
+ int fd, c, optcount;
- memset(&f, 0, sizeof(f));
argc--;
argv++;
- while ((c = getopt(argc, argv, "46hi:nNw")) != -1) {
+ npfctl_config_init(true);
+ while ((c = getopt(argc, argv, "ab:c:o:")) != -1) {
switch (c) {
- case '4':
- f.alen = sizeof(struct in_addr);
- break;
- case '6':
- f.alen = sizeof(struct in6_addr);
+ case 'a':
+ use_active = true;
break;
- case 'h':
- header = false;
+ case 'b':
+ bconf = optarg;
break;
- case 'i':
- f.ifname = optarg;
+ case 'c':
+ conf = optarg;
break;
- case 'n':
- f.nat = true;
- break;
- case 'N':
- f.name = true;
- break;
- case 'w':
- f.wide = true;
+ case 'o':
+ outfile = optarg;
break;
default:
- fprintf(stderr,
- "Usage: %s list [-46hnNw] [-i <ifname>]\n",
- getprogname());
- exit(EXIT_FAILURE);
+ usage();
}
}
- f.width = f.alen == sizeof(struct in_addr) ? 25 : 41;
- w = f.width + 6;
- f.fp = stdout;
- if (header) {
- fprintf(f.fp, "%*.*s %*.*s\n",
- w, w, "From address:port ", w, w, "To address:port ");
+ /*
+ * Options -a, -b and -c are mutually exclusive, so allow only one.
+ * If no options were specified, then set the defaults.
+ */
+ optcount = (int)!!use_active + (int)!!conf + (int)!!bconf;
+ if (optcount != 1) {
+ if (optcount > 1) {
+ usage();
+ }
+ conf = NPF_CONF_PATH;
+ outfile = outfile ? outfile : "npf.nvlist";
}
- npf_conn_list(fd, npfctl_conn_print, &f);
- return 0;
-}
-static int
-npfctl_open_dev(const char *path)
-{
- int fd, kernver;
+ if (use_active) {
+ puts("Loading the active configuration");
+ fd = npfctl_open_dev(NPF_DEV_PATH);
+ if ((ncf = npf_config_retrieve(fd)) == NULL) {
+ err(EXIT_FAILURE, "npf_config_retrieve");
+ }
+ }
- fd = open(path, O_RDONLY);
- if (fd == -1) {
- err(EXIT_FAILURE, "cannot open '%s'", path);
+ if (conf) {
+ printf("Loading %s\n", conf);
+ npfctl_parse_file(conf);
+ npfctl_config_build();
+ ncf = npfctl_config_ref();
}
- if (ioctl(fd, IOC_NPF_VERSION, &kernver) == -1) {
- err(EXIT_FAILURE, "ioctl(IOC_NPF_VERSION)");
+
+ if (bconf) {
+ printf("Importing %s\n", bconf);
+ ncf = npfctl_import(bconf);
}
- if (kernver != NPF_VERSION) {
- errx(EXIT_FAILURE,
- "incompatible NPF interface version (%d, kernel %d)\n"
- "Hint: update %s?", NPF_VERSION, kernver,
- kernver > NPF_VERSION ? "userland" : "kernel");
+
+ printf("Configuration:\n\n");
+ _npf_config_dump(ncf, STDOUT_FILENO);
+ if (outfile) {
+ printf("\nSaving binary to %s\n", outfile);
+ npfctl_config_save(ncf, outfile);
}
- return fd;
+ npf_config_destroy(ncf);
}
static void
@@ -882,7 +509,8 @@ npfctl(int action, int argc, char **argv)
case NPFCTL_SAVE:
ncf = npf_config_retrieve(fd);
if (ncf) {
- npfctl_config_save(ncf, NPF_DB_PATH);
+ npfctl_config_save(ncf,
+ argc > 2 ? argv[2] : NPF_DB_PATH);
npf_config_destroy(ncf);
} else {
ret = errno;
@@ -904,9 +532,7 @@ npfctl(int action, int argc, char **argv)
fun = "npfctl_config_show";
break;
case NPFCTL_DEBUG:
- npfctl_config_init(true);
- npfctl_parse_file(argc > 2 ? argv[2] : NPF_CONF_PATH);
- npfctl_config_debug(argc > 3 ? argv[3] : "/tmp/npf.nvlist");
+ npfctl_debug(argc, argv);
break;
}
if (ret) {
@@ -920,19 +546,46 @@ npfctl(int action, int argc, char **argv)
int
main(int argc, char **argv)
{
+ static const struct operations_s {
+ const char * cmd;
+ int action;
+ } operations[] = {
+ /* Start, stop, reload */
+ { "start", NPFCTL_START },
+ { "stop", NPFCTL_STOP },
+ { "reload", NPFCTL_RELOAD },
+ { "show", NPFCTL_SHOWCONF, },
+ { "flush", NPFCTL_FLUSH },
+ /* Table */
+ { "table", NPFCTL_TABLE },
+ /* Rule */
+ { "rule", NPFCTL_RULE },
+ /* Stats */
+ { "stats", NPFCTL_STATS },
+ /* Full state save/load */
+ { "save", NPFCTL_SAVE },
+ { "load", NPFCTL_LOAD },
+ { "list", NPFCTL_CONN_LIST },
+ /* Misc. */
+ { "valid", NPFCTL_VALIDATE },
+ { "debug", NPFCTL_DEBUG },
+ /* --- */
+ { NULL, 0 }
+ };
char *cmd;
if (argc < 2) {
usage();
}
- npfctl_show_init();
cmd = argv[1];
/* Find and call the subroutine. */
for (int n = 0; operations[n].cmd != NULL; n++) {
const char *opcmd = operations[n].cmd;
- if (strncmp(cmd, opcmd, strlen(opcmd)) != 0)
+
+ if (strncmp(cmd, opcmd, strlen(opcmd)) != 0) {
continue;
+ }
npfctl(operations[n].action, argc, argv);
return EXIT_SUCCESS;
}
diff --git a/usr.sbin/npf/npfctl/npfctl.h b/usr.sbin/npf/npfctl/npfctl.h
index 7adca404a21..4a175174880 100644
--- a/usr.sbin/npf/npfctl/npfctl.h
+++ b/usr.sbin/npf/npfctl/npfctl.h
@@ -111,12 +111,14 @@ typedef enum {
#define NPF_IFNET_TABLE_PREF ".ifnet-"
#define NPF_IFNET_TABLE_PREFLEN (sizeof(NPF_IFNET_TABLE_PREF) - 1)
-bool join(char *, size_t, int, char **, const char *);
void yyerror(const char *, ...) __printflike(1, 2) __dead;
void npfctl_bpfjit(bool);
void npfctl_parse_file(const char *);
void npfctl_parse_string(const char *, parse_entry_t);
+bool join(char *, size_t, int, char **, const char *);
+bool npfctl_addr_iszero(const npf_addr_t *);
+
void npfctl_print_error(const npf_error_t *);
char * npfctl_print_addrmask(int, const char *, const npf_addr_t *,
npf_netmask_t);
@@ -142,6 +144,12 @@ uint16_t npfctl_npt66_calcadj(npf_netmask_t, const npf_addr_t *,
const npf_addr_t *);
int npfctl_nat_ruleset_p(const char *, bool *);
+void usage(void);
+void npfctl_rule(int, int, char **);
+void npfctl_table_replace(int, int, char **);
+void npfctl_table(int, int, char **);
+int npfctl_conn_list(int, int, char **);
+
/*
* NPF extension loading.
*/
@@ -165,7 +173,9 @@ typedef struct npf_bpf npf_bpf_t;
enum {
BM_IPVER, BM_PROTO, BM_SRC_CIDR, BM_SRC_TABLE, BM_DST_CIDR,
BM_DST_TABLE, BM_SRC_PORTS, BM_DST_PORTS, BM_TCPFL, BM_ICMP_TYPE,
- BM_ICMP_CODE,
+ BM_ICMP_CODE, BM_SRC_NEG, BM_DST_NEG,
+
+ BM_COUNT // total number of the marks
};
npf_bpf_t * npfctl_bpf_create(void);
@@ -173,14 +183,15 @@ struct bpf_program *npfctl_bpf_complete(npf_bpf_t *);
const void * npfctl_bpf_bmarks(npf_bpf_t *, size_t *);
void npfctl_bpf_destroy(npf_bpf_t *);
-void npfctl_bpf_group_enter(npf_bpf_t *);
-void npfctl_bpf_group_exit(npf_bpf_t *, bool);
+void npfctl_bpf_group_enter(npf_bpf_t *, bool);
+void npfctl_bpf_group_exit(npf_bpf_t *);
-void npfctl_bpf_proto(npf_bpf_t *, sa_family_t, int);
+void npfctl_bpf_ipver(npf_bpf_t *, sa_family_t);
+void npfctl_bpf_proto(npf_bpf_t *, unsigned);
void npfctl_bpf_cidr(npf_bpf_t *, u_int, sa_family_t,
const npf_addr_t *, const npf_netmask_t);
void npfctl_bpf_ports(npf_bpf_t *, u_int, in_port_t, in_port_t);
-void npfctl_bpf_tcpfl(npf_bpf_t *, uint8_t, uint8_t, bool);
+void npfctl_bpf_tcpfl(npf_bpf_t *, uint8_t, uint8_t);
void npfctl_bpf_icmp(npf_bpf_t *, int, int);
void npfctl_bpf_table(npf_bpf_t *, u_int, u_int);
@@ -197,9 +208,6 @@ int npfctl_config_send(int);
nl_config_t * npfctl_config_ref(void);
int npfctl_config_show(int);
void npfctl_config_save(nl_config_t *, const char *);
-void npfctl_config_debug(const char *);
-
-void npfctl_show_init(void);
int npfctl_ruleset_show(int, const char *);
nl_rule_t * npfctl_rule_ref(void);
@@ -213,11 +221,11 @@ void npfctl_build_rproc(const char *, npfvar_t *);
void npfctl_build_group(const char *, int, const char *, bool);
void npfctl_build_group_end(void);
void npfctl_build_rule(uint32_t, const char *, sa_family_t,
- const opt_proto_t *, const filt_opts_t *,
+ const npfvar_t *, const filt_opts_t *,
const char *, const char *);
void npfctl_build_natseg(int, int, unsigned, const char *,
const addr_port_t *, const addr_port_t *,
- const opt_proto_t *, const filt_opts_t *, unsigned);
+ const npfvar_t *, const filt_opts_t *, unsigned);
void npfctl_build_maprset(const char *, int, const char *);
void npfctl_build_table(const char *, u_int, const char *);
@@ -226,11 +234,11 @@ void npfctl_setparam(const char *, int);
/*
* For the systems which do not define TH_ECE and TW_CRW.
*/
-#ifndef TH_ECE
-#define TH_ECE 0x40
+#ifndef TH_ECE
+#define TH_ECE 0x40
#endif
-#ifndef TH_CWR
-#define TH_CWR 0x80
+#ifndef TH_CWR
+#define TH_CWR 0x80
#endif
#endif
diff --git a/usr.sbin/npf/npftest/libnpftest/Makefile b/usr.sbin/npf/npftest/libnpftest/Makefile
index 29952cf28f3..c8f05f91ac4 100644
--- a/usr.sbin/npf/npftest/libnpftest/Makefile
+++ b/usr.sbin/npf/npftest/libnpftest/Makefile
@@ -1,4 +1,4 @@
-# $NetBSD: Makefile,v 1.10 2019/01/19 21:19:32 rmind Exp $
+# $NetBSD: Makefile,v 1.10.2.1 2020/06/20 15:46:48 martin Exp $
#
# Public Domain
#
@@ -14,7 +14,7 @@ SRCS+= npf_mbuf_subr.c
SRCS+= npf_nbuf_test.c
SRCS+= npf_bpf_test.c
SRCS+= npf_table_test.c
-SRCS+= npf_conn_test.c
+SRCS+= npf_gc_test.c
SRCS+= npf_state_test.c
SRCS+= npf_rule_test.c
SRCS+= npf_nat_test.c
diff --git a/usr.sbin/npf/npftest/libnpftest/npf_conn_test.c b/usr.sbin/npf/npftest/libnpftest/npf_gc_test.c
index 867f13a9a71..3c3ee5704db 100644
--- a/usr.sbin/npf/npftest/libnpftest/npf_conn_test.c
+++ b/usr.sbin/npf/npftest/libnpftest/npf_gc_test.c
@@ -6,8 +6,10 @@
#ifdef _KERNEL
#include <sys/types.h>
+#include <sys/kmem.h>
#endif
+#include "npf.h"
#include "npf_impl.h"
#include "npf_conn.h"
#include "npf_test.h"
@@ -152,15 +154,12 @@ run_gc_tests(void)
return true;
}
-bool
-npf_conn_test(bool verbose)
+static bool
+run_conndb_tests(npf_t *npf)
{
- npf_t *npf = npf_getkernctx();
npf_conndb_t *orig_cd = npf->conn_db;
bool ok;
- lverbose = verbose;
-
npf_config_enter(npf);
npf_conn_tracking(npf, true);
npf_config_exit(npf);
@@ -171,3 +170,98 @@ npf_conn_test(bool verbose)
npf->conn_db = orig_cd;
return ok;
}
+
+
+static void
+worker_test_task(npf_t *npf)
+{
+ bool *done = atomic_load_acquire(&npf->arg);
+ atomic_store_release(done, true);
+}
+
+static bool
+run_worker_tests(npf_t *npf)
+{
+ unsigned n = 100;
+ int error;
+
+ /* Spawn a worker thread. */
+ error = npf_worker_sysinit(1);
+ assert(error == 0);
+
+ /*
+ * Enlist/discharge an instance, trying to trigger a race.
+ */
+ while (n--) {
+ bool task_done = false;
+ unsigned retry = 100;
+ npf_t *test_npf;
+
+ /*
+ * Initialize a dummy NPF instance and add a test task.
+ * We will (ab)use npf_t::arg here.
+ *
+ * XXX/TODO: We should use:
+ *
+ * npfk_create(NPF_NO_GC, &npftest_mbufops,
+ * &npftest_ifops, &task_done);
+ *
+ * However, it resets the interface state and breaks
+ * other tests; to be refactor.
+ */
+ test_npf = kmem_zalloc(sizeof(npf_t), KM_SLEEP);
+ atomic_store_release(&test_npf->arg, &task_done);
+ test_npf->ebr = npf_ebr_create();
+
+ error = npf_worker_addfunc(test_npf, worker_test_task);
+ assert(error == 0);
+
+ /* Enlist the NPF instance. */
+ npf_worker_enlist(test_npf);
+
+ /* Wait for the task to be done. */
+ while (!atomic_load_acquire(&task_done) && retry--) {
+ npf_worker_signal(test_npf);
+ kpause("gctest", false, mstohz(1), NULL);
+ }
+
+ CHECK_TRUE(atomic_load_acquire(&task_done));
+ npf_worker_discharge(test_npf);
+
+ /* Clear the parameter and signal again. */
+ atomic_store_release(&test_npf->arg, NULL);
+ npf_worker_signal(test_npf);
+
+ npf_ebr_destroy(test_npf->ebr);
+ kmem_free(test_npf, sizeof(npf_t)); // npfk_destroy()
+ }
+
+ /*
+ * Destroy the worker.
+ *
+ * Attempts to enlist, discharge or signal should have no effect.
+ */
+
+ npf_worker_sysfini();
+ npf_worker_enlist(npf);
+ npf_worker_signal(npf);
+ npf_worker_discharge(npf);
+ return true;
+}
+
+bool
+npf_gc_test(bool verbose)
+{
+ npf_t *npf = npf_getkernctx();
+ bool ok;
+
+ lverbose = verbose;
+
+ ok = run_conndb_tests(npf);
+ CHECK_TRUE(ok);
+
+ ok = run_worker_tests(npf);
+ CHECK_TRUE(ok);
+
+ return ok;
+}
diff --git a/usr.sbin/npf/npftest/libnpftest/npf_mbuf_subr.c b/usr.sbin/npf/npftest/libnpftest/npf_mbuf_subr.c
index 603685e4560..4d9c0d6c9dc 100644
--- a/usr.sbin/npf/npftest/libnpftest/npf_mbuf_subr.c
+++ b/usr.sbin/npf/npftest/libnpftest/npf_mbuf_subr.c
@@ -15,7 +15,7 @@
#if defined(_NPF_STANDALONE)
struct mbuf *
-npfkern_m_get(int flags, int space)
+npfkern_m_get(npf_t *npf __unused, unsigned flags, size_t space)
{
unsigned mlen = offsetof(struct mbuf, m_data0[space]);
struct mbuf *m;
@@ -30,7 +30,7 @@ npfkern_m_get(int flags, int space)
}
#else
struct mbuf *
-npfkern_m_get(int flags, int space)
+npfkern_m_get(npf_t *npf __unused, unsigned flags, size_t space)
{
return m_get(flags, space);
}
@@ -92,7 +92,7 @@ npfkern_m_ensure_contig(struct mbuf **m0, size_t len)
char *dptr;
tlen = npfkern_m_length(*m0);
- if ((m1 = npfkern_m_get(M_PKTHDR, tlen)) == NULL) {
+ if ((m1 = npfkern_m_get(NULL, M_PKTHDR, tlen)) == NULL) {
return false;
}
m1->m_pkthdr.len = m1->m_len = tlen;
diff --git a/usr.sbin/npf/npftest/libnpftest/npf_nat_test.c b/usr.sbin/npf/npftest/libnpftest/npf_nat_test.c
index 458dd117517..d3a06c0c9bf 100644
--- a/usr.sbin/npf/npftest/libnpftest/npf_nat_test.c
+++ b/usr.sbin/npf/npftest/libnpftest/npf_nat_test.c
@@ -16,7 +16,7 @@
#define NPF_BINAT (NPF_NATIN | NPF_NATOUT)
-#define RANDOM_PORT 46759
+#define RANDOM_PORT 18791
static const struct test_case {
const char * src;
@@ -164,8 +164,8 @@ static bool
checkresult(bool verbose, unsigned i, struct mbuf *m, ifnet_t *ifp, int error)
{
const struct test_case *t = &test_cases[i];
- npf_cache_t npc = { .npc_info = 0, .npc_ctx = npf_getkernctx() };
const int af = t->af;
+ npf_cache_t npc;
nbuf_t nbuf;
if (verbose) {
@@ -176,7 +176,10 @@ checkresult(bool verbose, unsigned i, struct mbuf *m, ifnet_t *ifp, int error)
}
nbuf_init(npf_getkernctx(), &nbuf, m, ifp);
+ memset(&npc, 0, sizeof(npf_cache_t));
+ npc.npc_ctx = npf_getkernctx();
npc.npc_nbuf = &nbuf;
+
if (!npf_cache_all(&npc)) {
printf("error: could not fetch the packet data");
return false;
@@ -190,7 +193,7 @@ checkresult(bool verbose, unsigned i, struct mbuf *m, ifnet_t *ifp, int error)
npf_inet_ntop(af, npc.npc_ips[NPF_SRC], sbuf, sizeof(sbuf));
npf_inet_ntop(af, npc.npc_ips[NPF_DST], dbuf, sizeof(dbuf));
- printf("\tpost-translation:");
+ printf("\tpost-translation: ");
printf("src %s (%d) ", sbuf, ntohs(uh->uh_sport));
printf("dst %s (%d)\n", dbuf, ntohs(uh->uh_dport));
}
diff --git a/usr.sbin/npf/npftest/libnpftest/npf_state_test.c b/usr.sbin/npf/npftest/libnpftest/npf_state_test.c
index d9483dc680e..1a5b3a552f9 100644
--- a/usr.sbin/npf/npftest/libnpftest/npf_state_test.c
+++ b/usr.sbin/npf/npftest/libnpftest/npf_state_test.c
@@ -149,7 +149,8 @@ process_packet(const int i, npf_state_t *nst, bool *snew)
KASSERT(ret == true);
*snew = false;
}
- ret = npf_state_inspect(npc, nst, p->flags == OUT);
+ ret = npf_state_inspect(npc, nst,
+ (p->flags == OUT) ? NPF_FLOW_FORW : NPF_FLOW_BACK);
put_cached_pkt(npc);
return ret ? true : (p->flags & ERR) != 0;
diff --git a/usr.sbin/npf/npftest/libnpftest/npf_test.h b/usr.sbin/npf/npftest/libnpftest/npf_test.h
index c759abe38dc..14865be1ce5 100644
--- a/usr.sbin/npf/npftest/libnpftest/npf_test.h
+++ b/usr.sbin/npf/npftest/libnpftest/npf_test.h
@@ -73,8 +73,8 @@ struct mbuf {
#define M_NOWAIT 0x00001
#define M_PKTHDR 0x00002
-#define m_get(x, y) npfkern_m_get(0, MLEN)
-#define m_gethdr(x, y) npfkern_m_get(M_PKTHDR, MLEN)
+#define m_get(x, y) npfkern_m_get(NULL, 0, MLEN)
+#define m_gethdr(x, y) npfkern_m_get(NULL, M_PKTHDR, MLEN)
#define m_length(m) npfkern_m_length(m)
#define m_freem(m) npfkern_m_freem(m)
#define mtod(m, t) ((t)((m)->m_data))
@@ -87,7 +87,7 @@ struct mbuf {
const npf_mbufops_t npftest_mbufops;
const npf_ifops_t npftest_ifops;
-struct mbuf * npfkern_m_get(int, int);
+struct mbuf * npfkern_m_get(npf_t *, unsigned, size_t);
size_t npfkern_m_length(const struct mbuf *);
void npfkern_m_freem(struct mbuf *);
@@ -121,8 +121,8 @@ bool npf_table_test(bool, void *, size_t);
bool npf_state_test(bool);
bool npf_rule_test(bool);
-bool npf_conn_test(bool);
bool npf_nat_test(bool);
+bool npf_gc_test(bool);
int npf_inet_pton(int, const char *, void *);
const char * npf_inet_ntop(int, const void *, char *, socklen_t);
diff --git a/usr.sbin/npf/npftest/libnpftest/npf_test_subr.c b/usr.sbin/npf/npftest/libnpftest/npf_test_subr.c
index 609a08b5bd6..6ea53da5bc8 100644
--- a/usr.sbin/npf/npftest/libnpftest/npf_test_subr.c
+++ b/usr.sbin/npf/npftest/libnpftest/npf_test_subr.c
@@ -43,14 +43,15 @@ struct ifnet {
static TAILQ_HEAD(, ifnet) npftest_ifnet_list =
TAILQ_HEAD_INITIALIZER(npftest_ifnet_list);
-static const char * npftest_ifop_getname(ifnet_t *);
-static void npftest_ifop_flush(void *);
-static void * npftest_ifop_getmeta(const ifnet_t *);
-static void npftest_ifop_setmeta(ifnet_t *, void *);
+static const char * npftest_ifop_getname(npf_t *, ifnet_t *);
+static ifnet_t * npftest_ifop_lookup(npf_t *, const char *);
+static void npftest_ifop_flush(npf_t *, void *);
+static void * npftest_ifop_getmeta(npf_t *, const ifnet_t *);
+static void npftest_ifop_setmeta(npf_t *, ifnet_t *, void *);
const npf_ifops_t npftest_ifops = {
.getname = npftest_ifop_getname,
- .lookup = npf_test_getif,
+ .lookup = npftest_ifop_lookup,
.flush = npftest_ifop_flush,
.getmeta = npftest_ifop_getmeta,
.setmeta = npftest_ifop_setmeta,
@@ -63,8 +64,15 @@ npf_test_init(int (*pton_func)(int, const char *, void *),
{
npf_t *npf;
+#ifdef __NetBSD__
+ // XXX: Workaround for npf_init()
+ if ((npf = npf_getkernctx()) != NULL) {
+ npf_worker_discharge(npf);
+ npf_worker_sysfini();
+ }
+#endif
npfk_sysinit(0);
- npf = npfk_create(0, &npftest_mbufops, &npftest_ifops);
+ npf = npfk_create(0, &npftest_mbufops, &npftest_ifops, NULL);
npfk_thread_register(npf);
npf_setkernctx(npf);
@@ -90,6 +98,7 @@ npf_test_load(const void *buf, size_t len, bool verbose)
{
nvlist_t *npf_dict;
npf_error_t error;
+ int ret;
npf_dict = nvlist_unpack(buf, len, 0);
if (!npf_dict) {
@@ -97,9 +106,9 @@ npf_test_load(const void *buf, size_t len, bool verbose)
return EINVAL;
}
load_npf_config_ifs(npf_dict, verbose);
-
- // Note: npf_dict will be consumed by npf_load().
- return npfk_load(npf_getkernctx(), npf_dict, &error);
+ ret = npfk_load(npf_getkernctx(), npf_dict, &error);
+ nvlist_destroy(npf_dict);
+ return ret;
}
ifnet_t *
@@ -127,6 +136,18 @@ npf_test_addif(const char *ifname, bool reg, bool verbose)
return ifp;
}
+ifnet_t *
+npf_test_getif(const char *ifname)
+{
+ ifnet_t *ifp;
+
+ TAILQ_FOREACH(ifp, &npftest_ifnet_list, if_list) {
+ if (!strcmp(ifp->if_xname, ifname))
+ return ifp;
+ }
+ return NULL;
+}
+
static void
load_npf_config_ifs(nvlist_t *npf_dict, bool verbose)
{
@@ -153,25 +174,19 @@ load_npf_config_ifs(nvlist_t *npf_dict, bool verbose)
}
static const char *
-npftest_ifop_getname(ifnet_t *ifp)
+npftest_ifop_getname(npf_t *npf __unused, ifnet_t *ifp)
{
return ifp->if_xname;
}
-ifnet_t *
-npf_test_getif(const char *ifname)
+static ifnet_t *
+npftest_ifop_lookup(npf_t *npf __unused, const char *ifname)
{
- ifnet_t *ifp;
-
- TAILQ_FOREACH(ifp, &npftest_ifnet_list, if_list) {
- if (!strcmp(ifp->if_xname, ifname))
- return ifp;
- }
- return NULL;
+ return npf_test_getif(ifname);
}
static void
-npftest_ifop_flush(void *arg)
+npftest_ifop_flush(npf_t *npf __unused, void *arg)
{
ifnet_t *ifp;
@@ -180,13 +195,13 @@ npftest_ifop_flush(void *arg)
}
static void *
-npftest_ifop_getmeta(const ifnet_t *ifp)
+npftest_ifop_getmeta(npf_t *npf __unused, const ifnet_t *ifp)
{
return ifp->if_softc;
}
static void
-npftest_ifop_setmeta(ifnet_t *ifp, void *arg)
+npftest_ifop_setmeta(npf_t *npf __unused, ifnet_t *ifp, void *arg)
{
ifp->if_softc = arg;
}
diff --git a/usr.sbin/npf/npftest/npftest.c b/usr.sbin/npf/npftest/npftest.c
index 98adde22136..18a3df496b6 100644
--- a/usr.sbin/npf/npftest/npftest.c
+++ b/usr.sbin/npf/npftest/npftest.c
@@ -1,4 +1,4 @@
-/* $NetBSD: npftest.c,v 1.24 2019/07/23 00:52:02 rmind Exp $ */
+/* $NetBSD: npftest.c,v 1.24.2.1 2020/06/20 15:46:48 martin Exp $ */
/*
* NPF testing framework.
@@ -64,7 +64,7 @@ describe_tests(void)
"bpf\tBPF coprocessor\n"
"table\ttable handling\n"
"state\tstate handling and processing\n"
- "conn\tconnection processing\n"
+ "gc\tconnection G/C\n"
"rule\trule processing\n"
"nat\tNAT rule processing\n");
exit(EXIT_SUCCESS);
@@ -309,9 +309,9 @@ main(int argc, char **argv)
tname_matched = true;
}
- if (!testname || strcmp("conn", testname) == 0) {
- ok = rumpns_npf_conn_test(verbose);
- fail |= result("conn", ok);
+ if (!testname || strcmp("gc", testname) == 0) {
+ ok = rumpns_npf_gc_test(verbose);
+ fail |= result("gc", ok);
tname_matched = true;
}
}
diff --git a/usr.sbin/npf/npftest/npftest.conf b/usr.sbin/npf/npftest/npftest.conf
index 562a0d55077..f6eedadb742 100644
--- a/usr.sbin/npf/npftest/npftest.conf
+++ b/usr.sbin/npf/npftest/npftest.conf
@@ -1,8 +1,11 @@
-# $NetBSD: npftest.conf,v 1.7.2.1 2019/10/04 08:06:35 martin Exp $
+# $NetBSD: npftest.conf,v 1.7.2.2 2020/06/20 15:46:48 martin Exp $
$ext_if = "npftest0"
$int_if = "npftest1"
+set portmap.min_port 1024
+set portmap.max_port 65535
+
#
# RFC 5737
#
diff --git a/usr.sbin/npf/npftest/npftest.h b/usr.sbin/npf/npftest/npftest.h
index 540b9157ca0..0eb68edb169 100644
--- a/usr.sbin/npf/npftest/npftest.h
+++ b/usr.sbin/npf/npftest/npftest.h
@@ -1,4 +1,4 @@
-/* $NetBSD: npftest.h,v 1.16 2019/01/19 21:19:32 rmind Exp $ */
+/* $NetBSD: npftest.h,v 1.16.2.1 2020/06/20 15:46:48 martin Exp $ */
/*
* Public Domain.
@@ -23,8 +23,8 @@
#define rumpns_npf_table_test npf_table_test
#define rumpns_npf_state_test npf_state_test
#define rumpns_npf_rule_test npf_rule_test
-#define rumpns_npf_conn_test npf_conn_test
#define rumpns_npf_nat_test npf_nat_test
+#define rumpns_npf_gc_test npf_gc_test
#define rumpns_npf_test_conc npf_test_conc
#define rumpns_npf_test_statetrack npf_test_statetrack
#endif
@@ -49,8 +49,8 @@ bool rumpns_npf_table_test(bool, void *, size_t);
bool rumpns_npf_state_test(bool);
bool rumpns_npf_rule_test(bool);
-bool rumpns_npf_conn_test(bool);
bool rumpns_npf_nat_test(bool);
+bool rumpns_npf_gc_test(bool);
int process_stream(const char *, const char *, ifnet_t *);