diff options
| author | martin <martin@NetBSD.org> | 2020-06-20 15:46:47 +0000 |
|---|---|---|
| committer | martin <martin@NetBSD.org> | 2020-06-20 15:46:47 +0000 |
| commit | b19d58aad73c1adb8001858747a19d45bc8be0f9 (patch) | |
| tree | 9fe3f3431c7c5bed82411881629567d181b19097 /usr.sbin | |
| parent | 7d79e88db64cfbb1537b720c0a32d0c76aafaaa6 (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')
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 *); |
