aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--ObsoleteFiles.inc6
-rw-r--r--sbin/Makefile1
-rw-r--r--sbin/pfilctl/Makefile9
-rw-r--r--sbin/pfilctl/pfilctl.8117
-rw-r--r--sbin/pfilctl/pfilctl.c230
-rw-r--r--share/man/man9/Makefile6
-rw-r--r--share/man/man9/pfil.9291
-rw-r--r--sys/contrib/ipfilter/netinet/ip_fil_freebsd.c111
-rw-r--r--sys/net/if_bridge.c102
-rw-r--r--sys/net/if_enc.c10
-rw-r--r--sys/net/if_ethersubr.c41
-rw-r--r--sys/net/if_var.h5
-rw-r--r--sys/net/pfil.c862
-rw-r--r--sys/net/pfil.h209
-rw-r--r--sys/netinet/ip_fastfwd.c16
-rw-r--r--sys/netinet/ip_input.c25
-rw-r--r--sys/netinet/ip_output.c15
-rw-r--r--sys/netinet/ip_var.h5
-rw-r--r--sys/netinet/siftr.c70
-rw-r--r--sys/netinet6/ip6_fastfwd.c12
-rw-r--r--sys/netinet6/ip6_forward.c9
-rw-r--r--sys/netinet6/ip6_input.c25
-rw-r--r--sys/netinet6/ip6_output.c15
-rw-r--r--sys/netinet6/ip6_var.h6
-rw-r--r--sys/netpfil/ipfw/ip_fw_eaction.c2
-rw-r--r--sys/netpfil/ipfw/ip_fw_pfil.c173
-rw-r--r--sys/netpfil/pf/pf_ioctl.c158
27 files changed, 1556 insertions, 975 deletions
diff --git a/ObsoleteFiles.inc b/ObsoleteFiles.inc
index 3a872c3ca3ff..1cdb447200a3 100644
--- a/ObsoleteFiles.inc
+++ b/ObsoleteFiles.inc
@@ -38,6 +38,12 @@
# xargs -n1 | sort | uniq -d;
# done
+# 20190131: pfil(9) changed
+OLD_FILES+=usr/share/man/man9/pfil_hook_get.9
+OLD_FILES+=usr/share/man/man9/pfil_rlock.9
+OLD_FILES+=usr/share/man/man9/pfil_runlock.9
+OLD_FILES+=usr/share/man/man9/pfil_wlock.9
+OLD_FILES+=usr/share/man/man9/pfil_wunlock.9
# 20190126: adv(4) / adw(4) removal
OLD_FILES+=usr/share/man/man4/adv.4.gz
OLD_FILES+=usr/share/man/man4/adw.4.gz
diff --git a/sbin/Makefile b/sbin/Makefile
index 4f08a82fe572..2c4b042d91ec 100644
--- a/sbin/Makefile
+++ b/sbin/Makefile
@@ -52,6 +52,7 @@ SUBDIR=adjkerntz \
newfs_msdos \
nfsiod \
nos-tun \
+ pfilctl \
ping \
rcorder \
reboot \
diff --git a/sbin/pfilctl/Makefile b/sbin/pfilctl/Makefile
new file mode 100644
index 000000000000..04f0a622ce14
--- /dev/null
+++ b/sbin/pfilctl/Makefile
@@ -0,0 +1,9 @@
+# $FreeBSD$
+
+PROG= pfilctl
+SRCS= pfilctl.c
+WARNS?= 6
+
+MAN= pfilctl.8
+
+.include <bsd.prog.mk>
diff --git a/sbin/pfilctl/pfilctl.8 b/sbin/pfilctl/pfilctl.8
new file mode 100644
index 000000000000..d0a50e489a03
--- /dev/null
+++ b/sbin/pfilctl/pfilctl.8
@@ -0,0 +1,117 @@
+.\" Copyright (c) 2019 Gleb Smirnoff <glebius@FreeBSD.org>
+.\"
+.\" 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 AUTHOR 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 AUTHOR 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.
+.\"
+.\" $FreeBSD$
+.\"
+.Dd January 28, 2019
+.Dt PFILCTL 8
+.Os
+.Sh NAME
+.Nm pfilctl
+.Nd pfil(9) control utility
+.Sh SYNOPSIS
+.Nm
+.Cm heads
+.Nm
+.Cm hooks
+.Nm
+.Cm link
+.Aq Fl i | Fl o
+.Op Fl a
+.Ar hook Ar head
+.Nm
+.Cm unlink
+.Aq Fl i | Fl o
+.Ar hook Ar head
+.Sh DESCRIPTION
+The
+.Nm
+utility is intended to view and change configuration of the
+.Xr pfil 9
+packet filtering hooks and filters on them.
+.Sh COMMANDS
+.Bl -tag -width "unlink"
+.It Cm heads
+List available packet filtering points.
+.It Cm hooks
+List available packet filters.
+.It Xo
+.Cm link
+.Aq Fl i | Fl o
+.Op Fl a
+.Ar hook Ar head
+.Xc
+Link
+.Ar hook
+to
+.Ar head .
+With the
+.Fl i
+flag the hook will be connected as input and with
+.Fl o
+as output hook.
+At least one of
+.Fl i
+or
+.Fl o
+is required.
+By default
+.Nm
+will prepend the hook in front of other hooks if any present:
+new hook will be as close to the wire as possible, so that on input
+it will be the first filter and on output it will be the last.
+Adding the
+.Fl a
+flag switches to appending new hook instead of prepending.
+.It Xo
+.Cm unlink
+.Aq Fl i | Fl o
+.Ar hook Ar head
+.Xc
+Unlink
+.Ar hook
+on
+.Ar head .
+At least one of
+.Fl i
+or
+.Fl o
+is required.
+With the
+.Fl i
+flag the hook will be removed from the input list of hooks
+and with
+.Fl o
+on output list.
+.El
+.Sh SEE ALSO
+.Xr ipfilter 4 ,
+.Xr ipfw 4 ,
+.Xr pf 4 ,
+.Xr pfil 9
+.Sh AUTHORS
+.An -nosplit
+The
+.Nm
+utility was written by
+.An Gleb Smirnoff Aq Mt glebius@FreeBSD.org .
diff --git a/sbin/pfilctl/pfilctl.c b/sbin/pfilctl/pfilctl.c
new file mode 100644
index 000000000000..363feabca116
--- /dev/null
+++ b/sbin/pfilctl/pfilctl.c
@@ -0,0 +1,230 @@
+/*-
+ * SPDX-License-Identifier: BSD-2-Clause-FreeBSD
+ *
+ * Copyright (c) 2019 Gleb Smirnoff <glebius@FreeBSD.org>
+ *
+ * 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 AUTHOR 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 AUTHOR 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>
+__FBSDID("$FreeBSD$");
+
+#include <sys/param.h>
+#include <sys/ioctl.h>
+#include <net/if.h>
+#include <net/pfil.h>
+
+#include <err.h>
+#include <fcntl.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+
+static int dev;
+
+static const char * const typenames[] = {
+ [PFIL_TYPE_IP4] = "IPv4",
+ [PFIL_TYPE_IP6] = "IPv6",
+ [PFIL_TYPE_ETHERNET] = "Ethernet",
+};
+
+static void listheads(int argc, char *argv[]);
+static void listhooks(int argc, char *argv[]);
+static void hook(int argc, char *argv[]);
+static void help(void);
+
+static const struct cmd {
+ const char *cmd_name;
+ void (*cmd_func)(int argc, char *argv[]);
+} cmds[] = {
+ { "heads", listheads },
+ { "hooks", listhooks },
+ { "link", hook },
+ { "unlink", hook },
+ { NULL, NULL },
+};
+
+int
+main(int argc __unused, char *argv[] __unused)
+{
+ int cmd = -1;
+
+ if (--argc == 0)
+ help();
+ argv++;
+
+ for (int i = 0; cmds[i].cmd_name != NULL; i++)
+ if (!strncmp(argv[0], cmds[i].cmd_name, strlen(argv[0]))) {
+ if (cmd != -1)
+ errx(1, "ambiguous command: %s", argv[0]);
+ cmd = i;
+ }
+ if (cmd == -1)
+ errx(1, "unknown command: %s", argv[0]);
+
+ dev = open("/dev/" PFILDEV, O_RDWR);
+ if (dev == -1)
+ err(1, "open(%s)", "/dev/" PFILDEV);
+
+ (*cmds[cmd].cmd_func)(argc, argv);
+
+ return (0);
+}
+
+static void
+help(void)
+{
+ extern char *__progname;
+
+ fprintf(stderr, "usage: %s (heads|hooks|link|unlink)\n", __progname);
+ exit(0);
+}
+
+static void
+listheads(int argc __unused, char *argv[] __unused)
+{
+ struct pfilioc_list plh;
+ u_int nheads, nhooks, i;
+ int j, h;
+
+ plh.pio_nheads = 0;
+ plh.pio_nhooks = 0;
+ if (ioctl(dev, PFILIOC_LISTHEADS, &plh) != 0)
+ err(1, "ioctl(PFILIOC_LISTHEADS)");
+
+retry:
+ plh.pio_heads = calloc(plh.pio_nheads, sizeof(struct pfilioc_head));
+ if (plh.pio_heads == NULL)
+ err(1, "malloc");
+ plh.pio_hooks = calloc(plh.pio_nhooks, sizeof(struct pfilioc_hook));
+ if (plh.pio_hooks == NULL)
+ err(1, "malloc");
+
+ nheads = plh.pio_nheads;
+ nhooks = plh.pio_nhooks;
+
+ if (ioctl(dev, PFILIOC_LISTHEADS, &plh) != 0)
+ err(1, "ioctl(PFILIOC_LISTHEADS)");
+
+ if (plh.pio_nheads > nheads || plh.pio_nhooks > nhooks) {
+ free(plh.pio_heads);
+ free(plh.pio_hooks);
+ goto retry;
+ }
+
+#define FMTHD "%16s %8s\n"
+#define FMTHK "%29s %16s %16s\n"
+ printf(FMTHD, "Intercept point", "Type");
+ for (i = 0, h = 0; i < plh.pio_nheads; i++) {
+ printf(FMTHD, plh.pio_heads[i].pio_name,
+ typenames[plh.pio_heads[i].pio_type]);
+ for (j = 0; j < plh.pio_heads[i].pio_nhooksin; j++, h++)
+ printf(FMTHK, "In", plh.pio_hooks[h].pio_module,
+ plh.pio_hooks[h].pio_ruleset);
+ for (j = 0; j < plh.pio_heads[i].pio_nhooksout; j++, h++)
+ printf(FMTHK, "Out", plh.pio_hooks[h].pio_module,
+ plh.pio_hooks[h].pio_ruleset);
+ }
+}
+
+static void
+listhooks(int argc __unused, char *argv[] __unused)
+{
+ struct pfilioc_list plh;
+ u_int nhooks, i;
+
+ plh.pio_nhooks = 0;
+ if (ioctl(dev, PFILIOC_LISTHEADS, &plh) != 0)
+ err(1, "ioctl(PFILIOC_LISTHEADS)");
+retry:
+ plh.pio_hooks = calloc(plh.pio_nhooks, sizeof(struct pfilioc_hook));
+ if (plh.pio_hooks == NULL)
+ err(1, "malloc");
+
+ nhooks = plh.pio_nhooks;
+
+ if (ioctl(dev, PFILIOC_LISTHOOKS, &plh) != 0)
+ err(1, "ioctl(PFILIOC_LISTHOOKS)");
+
+ if (plh.pio_nhooks > nhooks) {
+ free(plh.pio_hooks);
+ goto retry;
+ }
+
+ printf("Available hooks:\n");
+ for (i = 0; i < plh.pio_nhooks; i++) {
+ printf("\t%s:%s %s\n", plh.pio_hooks[i].pio_module,
+ plh.pio_hooks[i].pio_ruleset,
+ typenames[plh.pio_hooks[i].pio_type]);
+ }
+}
+
+static void
+hook(int argc, char *argv[])
+{
+ struct pfilioc_link req;
+ int c;
+ char *ruleset;
+
+ if (argv[0][0] == 'u')
+ req.pio_flags = PFIL_UNLINK;
+ else
+ req.pio_flags = 0;
+
+ while ((c = getopt(argc, argv, "ioa")) != -1)
+ switch (c) {
+ case 'i':
+ req.pio_flags |= PFIL_IN;
+ break;
+ case 'o':
+ req.pio_flags |= PFIL_OUT;
+ break;
+ case 'a':
+ req.pio_flags |= PFIL_APPEND;
+ break;
+ default:
+ help();
+ }
+
+ if (!PFIL_DIR(req.pio_flags))
+ help();
+
+ argc -= optind;
+ argv += optind;
+
+ if (argc != 2)
+ help();
+
+ /* link mod:ruleset head */
+ if ((ruleset = strchr(argv[0], ':')) == NULL)
+ help();
+ *ruleset = '\0';
+ ruleset++;
+
+ strlcpy(req.pio_name, argv[1], sizeof(req.pio_name));
+ strlcpy(req.pio_module, argv[0], sizeof(req.pio_module));
+ strlcpy(req.pio_ruleset, ruleset, sizeof(req.pio_ruleset));
+
+ if (ioctl(dev, PFILIOC_LINK, &req) != 0)
+ err(1, "ioctl(PFILIOC_LINK)");
+}
diff --git a/share/man/man9/Makefile b/share/man/man9/Makefile
index 3034c24c6adb..bd816bf519fa 100644
--- a/share/man/man9/Makefile
+++ b/share/man/man9/Makefile
@@ -1635,13 +1635,9 @@ MLINKS+=pci_iov_schema.9 pci_iov_schema_alloc_node.9 \
MLINKS+=pfil.9 pfil_add_hook.9 \
pfil.9 pfil_head_register.9 \
pfil.9 pfil_head_unregister.9 \
- pfil.9 pfil_hook_get.9 \
pfil.9 pfil_remove_hook.9 \
- pfil.9 pfil_rlock.9 \
pfil.9 pfil_run_hooks.9 \
- pfil.9 pfil_runlock.9 \
- pfil.9 pfil_wlock.9 \
- pfil.9 pfil_wunlock.9
+ pfil.9 pfil_link.9
MLINKS+=pfind.9 zpfind.9
MLINKS+=PHOLD.9 PRELE.9 \
PHOLD.9 _PHOLD.9 \
diff --git a/share/man/man9/pfil.9 b/share/man/man9/pfil.9
index 843191e0b4ab..c2186cf1b540 100644
--- a/share/man/man9/pfil.9
+++ b/share/man/man9/pfil.9
@@ -1,5 +1,6 @@
.\" $NetBSD: pfil.9,v 1.22 2003/07/01 13:04:06 wiz Exp $
.\"
+.\" Copyright (c) 2019 Gleb Smirnoff <glebius@FreeBSD.org>
.\" Copyright (c) 1996 Matthew R. Green
.\" All rights reserved.
.\"
@@ -28,194 +29,127 @@
.\"
.\" $FreeBSD$
.\"
-.Dd March 10, 2018
+.Dd January 28, 2019
.Dt PFIL 9
.Os
.Sh NAME
.Nm pfil ,
.Nm pfil_head_register ,
.Nm pfil_head_unregister ,
-.Nm pfil_head_get ,
-.Nm pfil_add_hook ,
-.Nm pfil_add_hook_flags ,
-.Nm pfil_remove_hook ,
-.Nm pfil_remove_hook_flags ,
-.Nm pfil_run_hooks ,
-.Nm pfil_rlock ,
-.Nm pfil_runlock ,
-.Nm pfil_wlock ,
-.Nm pfil_wunlock
+.Nm pfil_link ,
+.Nm pfil_run_hooks
.Nd packet filter interface
.Sh SYNOPSIS
.In sys/param.h
.In sys/mbuf.h
-.In net/if.h
.In net/pfil.h
-.Bd -literal
-typedef int (*pfil_func_t)(void *arg, struct mbuf **mp, struct ifnet *, int dir, struct inpcb);
-.Bd -literal
-typedef int (*pfil_func_flags_t)(void *arg, struct mbuf **mp, struct ifnet *, int dir, int flags, struct inpcb);
-.Ft int
-.Fn pfil_head_register "struct pfil_head *head"
-.Ft int
-.Fn pfil_head_unregister "struct pfil_head *head"
-.Ft "struct pfil_head *"
-.Fn pfil_head_get "int af" "u_long dlt"
-.Ft int
-.Fn pfil_add_hook "pfil_func_t" "void *arg" "struct pfil_head *"
-.Ft int
-.Fn pfil_add_hook_flags "pfil_func_flags_t" "void *arg" "int flags" "struct pfil_head *"
-.Ft int
-.Fn pfil_remove_hook "pfil_func_t" "void *arg" "struct pfil_head *"
-.Ft int
-.Fn pfil_remove_hook_flags "pfil_func_flags_t" "void *arg" "int flags" "struct pfil_head *"
-.Ft int
-.Fn pfil_run_hooks "struct pfil_head *head" "struct mbuf **mp" "struct ifnet *" "int dir" "int flags" "struct inpcb *"
-.Ft void
-.Fn pfil_rlock "struct pfil_head *" "struct rm_priotracker *"
-.Ft void
-.Fn pfil_runlock "struct pfil_head *" "struct rm_priotracker *"
+.Ft pfil_head_t
+.Fn pfil_head_register "struct pfil_head_args *args"
.Ft void
-.Fn pfil_wlock "struct pfil_head *"
+.Fn pfil_head_unregister "struct pfil_head_t *head"
+.Ft pfil_hook_t
+.Fn pfil_add_hook "struct pfil_hook_args *"
.Ft void
-.Fn pfil_wunlock "struct pfil_head *"
-.Ed
+.Fn pfil_remove_hook "pfil_hook_t"
+.Ft int
+.Fn pfil_link "struct pfil_link_args *args"
+.Ft int
+.Fn pfil_run_hooks "phil_head_t *" "pfil_packet_t" "struct ifnet *" "int" "struct inpcb *"
.Sh DESCRIPTION
The
.Nm
-framework allows for a specified function to be invoked for every
-incoming or outgoing packet for a particular network I/O stream.
+framework allows for a specified function or a list of functions
+to be invoked for every incoming or outgoing packet for a particular
+network I/O stream.
These hooks may be used to implement a firewall or perform packet
transformations.
.Pp
-Packet filtering points are registered with
+Packet filtering points, for historical reasons named
+.Em heads ,
+are registered with
.Fn pfil_head_register .
-Filtering points are identified by a key
-.Pq Vt "void *"
-and a data link type
-.Pq Vt int
-in the
-.Vt pfil_head
-structure.
-Packet filters use the key and data link type to look up the filtering
-point with which they register themselves.
-The key is unique to the filtering point.
-The data link type is a
-.Xr bpf 4
-DLT constant indicating what kind of header is present on the packet
-at the filtering point.
-Each filtering point uses common per-VNET rmlock by default.
-This can be changed by specifying
-.Vt PFIL_FLAG_PRIVATE_LOCK
-as
-.Vt "flags"
-field in the
-.Vt pfil_head
-structure.
-Note that specifying private lock can break filters sharing the same
-ruleset and/or state between different data link types.
-Filtering points may be unregistered with the
-.Fn pfil_head_unregister
-function.
+The function is supplied with special versioned
+.Vt struct pfil_head_args
+structure that specifies type and features of the head as well as
+human readable name.
+If the filtering point to be ever destroyed, the subsystem that
+created it must unregister it with call to
+.Fn pfil_head_unregister .
.Pp
-Packet filters register/unregister themselves with a filtering point
-with the
+Packet filtering systems may register arbitrary number of filters,
+for historical reasons named
+.Em hooks .
+To register a new hook
.Fn pfil_add_hook
-and
+with special versioned
+.Vt struct pfil_hook_args
+structure is called.
+The structure specifies type and features of the hook, pointer to
+the actual filtering function and user readable name of the filtering
+module and ruleset name.
+Later hooks can be removed with
.Fn pfil_remove_hook
-functions, respectively.
-.I
-The head is looked up using the
-.Fn pfil_head_get
-function, which takes the key and data link type that the packet filter
-expects.
-Filters may provide an argument to be passed to the filter when
-invoked on a packet.
-.Pp
-When a filter is invoked, the packet appears just as if it
-.Dq came off the wire .
-That is, all protocol fields are in network byte order.
-The filter is called with its specified argument, the pointer to the
-pointer to the
-.Vt mbuf
-containing the packet, the pointer to the network
-interface that the packet is traversing, and the direction
-.Dv ( PFIL_IN
-or
-.Dv PFIL_OUT )
-that the packet is traveling.
-The
-.Vt flags
-argument will indicate if an outgoing packet is simply being forwarded with the
-value PFIL_FWD.
-The filter may change which mbuf the
-.Vt "mbuf\ **"
-argument references.
-The filter returns an error (errno) if the packet processing is to stop, or 0
-if the processing is to continue.
-If the packet processing is to stop, it is the responsibility of the
-filter to free the packet.
+functions.
.Pp
-Every filter hook is called with
-.Nm
-read lock held.
-All heads uses the same lock within the same VNET instance.
-Packet filter can use this lock instead of own locking model to
-improve performance.
-Since
+To connect existing
+.Em hook
+to an existing
+.Em head
+function
+.Fn pfil_link
+shall be used.
+The function is supplied with versioned
+.Vt struct pfil_link_args
+structure that specifies either literal names of hook and head or
+pointers to them.
+Typically
+.Fn pfil_link
+is called by filtering modules to autoregister their default ruleset
+and default filtering points.
+It also serves on the kernel side of
+.Xr ioctl 2
+when user changes
.Nm
-uses
-.Xr rmlock 9
-.Fn pfil_rlock
-and
-.Fn pfil_runlock
-require
-.Va struct rm_priotracker
-to be passed as argument.
-Filter can acquire and release writer lock via
-.Fn pfil_wlock
-and
-.Fn pfil_wunlock
-functions.
-See
-.Xr rmlock 9
-for more details.
-.Sh FILTERING POINTS
-Currently, filtering points are implemented for the following link types:
+configuration with help of
+.Xr pfilctl 8
+utility.
.Pp
-.Bl -tag -width "AF_INET6" -offset XXX -compact
-.It AF_INET
+For every packet traveling through a
+.Em head
+the latter shall invoke
+.Fn pfil_run_hooks .
+The function can accept either
+.Vt struct mbuf *
+pointer or a
+.Vt void *
+pointer and length.
+In case if a hooked filtering module cannot understand
+.Vt void *
+pointer
+.Nm
+will provide it with a fake one.
+All calls to
+.Fn pfil_run_hooks
+are performed in network
+.Xr epoch 9 .
+.Sh HEADS (filtering points)
+By default kernel creates the following heads:
+.Bl -tag -width "ethernet"
+.It inet
IPv4 packets.
-.It AF_INET6
+.It inet6
IPv6 packets.
-.It AF_LINK
+.It ethernet
Link-layer packets.
.El
-.Sh RETURN VALUES
-If successful,
-.Fn pfil_head_get
-returns the
-.Vt pfil_head
-structure for the given key/dlt.
-The
-.Fn pfil_add_hook
-and
-.Fn pfil_remove_hook
-functions
-return 0 if successful.
-If called with flag
-.Dv PFIL_WAITOK ,
-.Fn pfil_remove_hook
-is expected to always succeed.
.Pp
-The
-.Fn pfil_head_unregister
-function
-might sleep!
+Default rulesets are automatically linked to these heads to preserve
+historical behavavior.
.Sh SEE ALSO
-.Xr bpf 4 ,
-.Xr if_bridge 4 ,
-.Xr rmlock 9
+.Xr ipfilter 4 ,
+.Xr ipfw 4 ,
+.Xr pf 4 ,
+.Xr pfilctl 8
.Sh HISTORY
The
.Nm
@@ -223,45 +157,8 @@ interface first appeared in
.Nx 1.3 .
The
.Nm
-input and output lists were originally implemented as
-.In sys/queue.h
-.Dv LIST
-structures;
-however this was changed in
-.Nx 1.4
-to
-.Dv TAILQ
-structures.
-This change was to allow the input and output filters to be processed in
-reverse order, to allow the same path to be taken, in or out of the kernel.
-.Pp
-The
-.Nm
-interface was changed in 1.4T to accept a 3rd parameter to both
-.Fn pfil_add_hook
-and
-.Fn pfil_remove_hook ,
-introducing the capability of per-protocol filtering.
-This was done primarily in order to support filtering of IPv6.
-.Pp
-In 1.5K, the
-.Nm
-framework was changed to work with an arbitrary number of filtering points,
-as well as be less IP-centric.
-.Pp
-Fine-grained locking was added in
+interface was imported into
.Fx 5.2 .
-.Nm
-lock export was added in
-.Fx 10.0 .
-.Sh BUGS
-When a
-.Vt pfil_head
-is being modified, no traffic is diverted
-(to avoid deadlock).
-This means that traffic may be dropped unconditionally for a short period
-of time.
-.Fn pfil_run_hooks
-will return
-.Er ENOBUFS
-to indicate this.
+In
+.Fx 13.0
+the interface was significantly rewritten.
diff --git a/sys/contrib/ipfilter/netinet/ip_fil_freebsd.c b/sys/contrib/ipfilter/netinet/ip_fil_freebsd.c
index 309027b500cc..292a119e2c43 100644
--- a/sys/contrib/ipfilter/netinet/ip_fil_freebsd.c
+++ b/sys/contrib/ipfilter/netinet/ip_fil_freebsd.c
@@ -25,6 +25,7 @@ static const char rcsid[] = "@(#)$Id$";
# include "opt_random_ip_id.h"
#endif
#include <sys/param.h>
+#include <sys/conf.h>
#include <sys/errno.h>
#include <sys/types.h>
#include <sys/file.h>
@@ -126,32 +127,33 @@ static void ipf_ifevent(arg, ifp)
-static int
-ipf_check_wrapper(void *arg, struct mbuf **mp, struct ifnet *ifp, int dir)
+static pfil_return_t
+ipf_check_wrapper(struct mbuf **mp, struct ifnet *ifp, int flags,
+ void *ruleset __unused, struct inpcb *inp)
{
struct ip *ip = mtod(*mp, struct ip *);
- int rv;
+ pfil_return_t rv;
CURVNET_SET(ifp->if_vnet);
- rv = ipf_check(&V_ipfmain, ip, ip->ip_hl << 2, ifp, (dir == PFIL_OUT),
- mp);
+ rv = ipf_check(&V_ipfmain, ip, ip->ip_hl << 2, ifp, (flags & PFIL_OUT),
+ mp);
CURVNET_RESTORE();
- return rv;
+ return (rv == 0 ? PFIL_PASS : PFIL_DROPPED);
}
-# ifdef USE_INET6
-# include <netinet/ip6.h>
-
-static int
-ipf_check_wrapper6(void *arg, struct mbuf **mp, struct ifnet *ifp, int dir)
+#ifdef USE_INET6
+static pfil_return_t
+ipf_check_wrapper6(struct mbuf **mp, struct ifnet *ifp, int flags,
+ void *ruleset __unused, struct inpcb *inp)
{
- int error;
+ pfil_return_t rv;
CURVNET_SET(ifp->if_vnet);
- error = ipf_check(&V_ipfmain, mtod(*mp, struct ip *),
- sizeof(struct ip6_hdr), ifp, (dir == PFIL_OUT), mp);
+ rv = ipf_check(&V_ipfmain, mtod(*mp, struct ip *),
+ sizeof(struct ip6_hdr), ifp, (flags & PFIL_OUT), mp);
CURVNET_RESTORE();
- return (error);
+
+ return (rv == 0 ? PFIL_PASS : PFIL_DROPPED);
}
# endif
#if defined(IPFILTER_LKM)
@@ -1318,53 +1320,62 @@ ipf_inject(fin, m)
return error;
}
+VNET_DEFINE_STATIC(pfil_hook_t, ipf_inet_hook);
+VNET_DEFINE_STATIC(pfil_hook_t, ipf_inet6_hook);
+#define V_ipf_inet_hook VNET(ipf_inet_hook)
+#define V_ipf_inet6_hook VNET(ipf_inet6_hook)
+
int ipf_pfil_unhook(void) {
- struct pfil_head *ph_inet;
+
+ pfil_remove_hook(V_ipf_inet_hook);
+
#ifdef USE_INET6
- struct pfil_head *ph_inet6;
+ pfil_remove_hook(V_ipf_inet6_hook);
#endif
- ph_inet = pfil_head_get(PFIL_TYPE_AF, AF_INET);
- if (ph_inet != NULL)
- pfil_remove_hook((void *)ipf_check_wrapper, NULL,
- PFIL_IN|PFIL_OUT|PFIL_WAITOK, ph_inet);
-# ifdef USE_INET6
- ph_inet6 = pfil_head_get(PFIL_TYPE_AF, AF_INET6);
- if (ph_inet6 != NULL)
- pfil_remove_hook((void *)ipf_check_wrapper6, NULL,
- PFIL_IN|PFIL_OUT|PFIL_WAITOK, ph_inet6);
-# endif
-
return (0);
}
int ipf_pfil_hook(void) {
- struct pfil_head *ph_inet;
+ struct pfil_hook_args pha;
+ struct pfil_link_args pla;
+ int error, error6;
+
+ pha.pa_version = PFIL_VERSION;
+ pha.pa_flags = PFIL_IN | PFIL_OUT;
+ pha.pa_modname = "ipfilter";
+ pha.pa_rulname = "default";
+ pha.pa_func = ipf_check_wrapper;
+ pha.pa_ruleset = NULL;
+ pha.pa_type = PFIL_TYPE_IP4;
+ V_ipf_inet_hook = pfil_add_hook(&pha);
+
#ifdef USE_INET6
- struct pfil_head *ph_inet6;
+ pha.pa_func = ipf_check_wrapper6;
+ pha.pa_type = PFIL_TYPE_IP6;
+ V_ipf_inet6_hook = pfil_add_hook(&pha);
#endif
- ph_inet = pfil_head_get(PFIL_TYPE_AF, AF_INET);
-# ifdef USE_INET6
- ph_inet6 = pfil_head_get(PFIL_TYPE_AF, AF_INET6);
-# endif
- if (ph_inet == NULL
-# ifdef USE_INET6
- && ph_inet6 == NULL
-# endif
- ) {
- return ENODEV;
- }
+ pla.pa_version = PFIL_VERSION;
+ pla.pa_flags = PFIL_IN | PFIL_OUT |
+ PFIL_HEADPTR | PFIL_HOOKPTR;
+ pla.pa_head = V_inet_pfil_head;
+ pla.pa_hook = V_ipf_inet_hook;
+ error = pfil_link(&pla);
- if (ph_inet != NULL)
- pfil_add_hook((void *)ipf_check_wrapper, NULL,
- PFIL_IN|PFIL_OUT|PFIL_WAITOK, ph_inet);
-# ifdef USE_INET6
- if (ph_inet6 != NULL)
- pfil_add_hook((void *)ipf_check_wrapper6, NULL,
- PFIL_IN|PFIL_OUT|PFIL_WAITOK, ph_inet6);
-# endif
- return (0);
+ error6 = 0;
+#ifdef USE_INET6
+ pla.pa_head = V_inet6_pfil_head;
+ pla.pa_hook = V_ipf_inet6_hook;
+ error6 = pfil_link(&pla);
+#endif
+
+ if (error || error6)
+ error = ENODEV;
+ else
+ error = 0;
+
+ return (error);
}
void
diff --git a/sys/net/if_bridge.c b/sys/net/if_bridge.c
index 578e0756498b..96dc046840d4 100644
--- a/sys/net/if_bridge.c
+++ b/sys/net/if_bridge.c
@@ -1970,9 +1970,9 @@ bridge_dummynet(struct mbuf *m, struct ifnet *ifp)
return;
}
- if (PFIL_HOOKED(&V_inet_pfil_hook)
+ if (PFIL_HOOKED_OUT(V_inet_pfil_head)
#ifdef INET6
- || PFIL_HOOKED(&V_inet6_pfil_hook)
+ || PFIL_HOOKED_OUT(V_inet6_pfil_head)
#endif
) {
if (bridge_pfil(&m, sc->sc_ifp, ifp, PFIL_OUT) != 0)
@@ -2230,9 +2230,9 @@ bridge_forward(struct bridge_softc *sc, struct bridge_iflist *sbif,
ETHER_BPF_MTAP(ifp, m);
/* run the packet filter */
- if (PFIL_HOOKED(&V_inet_pfil_hook)
+ if (PFIL_HOOKED_IN(V_inet_pfil_head)
#ifdef INET6
- || PFIL_HOOKED(&V_inet6_pfil_hook)
+ || PFIL_HOOKED_IN(V_inet6_pfil_head)
#endif
) {
BRIDGE_UNLOCK(sc);
@@ -2270,9 +2270,9 @@ bridge_forward(struct bridge_softc *sc, struct bridge_iflist *sbif,
BRIDGE_UNLOCK(sc);
- if (PFIL_HOOKED(&V_inet_pfil_hook)
+ if (PFIL_HOOKED_OUT(V_inet_pfil_head)
#ifdef INET6
- || PFIL_HOOKED(&V_inet6_pfil_hook)
+ || PFIL_HOOKED_OUT(V_inet6_pfil_head)
#endif
) {
if (bridge_pfil(&m, ifp, dst_if, PFIL_OUT) != 0)
@@ -2409,7 +2409,7 @@ bridge_input(struct ifnet *ifp, struct mbuf *m)
#ifdef INET6
# define OR_PFIL_HOOKED_INET6 \
- || PFIL_HOOKED(&V_inet6_pfil_hook)
+ || PFIL_HOOKED_IN(V_inet6_pfil_head)
#else
# define OR_PFIL_HOOKED_INET6
#endif
@@ -2427,7 +2427,7 @@ bridge_input(struct ifnet *ifp, struct mbuf *m)
if_inc_counter(iface, IFCOUNTER_IBYTES, m->m_pkthdr.len); \
/* Filter on the physical interface. */ \
if (V_pfil_local_phys && \
- (PFIL_HOOKED(&V_inet_pfil_hook) \
+ (PFIL_HOOKED_IN(V_inet_pfil_head) \
OR_PFIL_HOOKED_INET6)) { \
if (bridge_pfil(&m, NULL, ifp, \
PFIL_IN) != 0 || m == NULL) { \
@@ -2517,9 +2517,9 @@ bridge_broadcast(struct bridge_softc *sc, struct ifnet *src_if,
}
/* Filter on the bridge interface before broadcasting */
- if (runfilt && (PFIL_HOOKED(&V_inet_pfil_hook)
+ if (runfilt && (PFIL_HOOKED_OUT(V_inet_pfil_head)
#ifdef INET6
- || PFIL_HOOKED(&V_inet6_pfil_hook)
+ || PFIL_HOOKED_OUT(V_inet6_pfil_head)
#endif
)) {
if (bridge_pfil(&m, sc->sc_ifp, NULL, PFIL_OUT) != 0)
@@ -2564,9 +2564,9 @@ bridge_broadcast(struct bridge_softc *sc, struct ifnet *src_if,
* pointer so we do not redundantly filter on the bridge for
* each interface we broadcast on.
*/
- if (runfilt && (PFIL_HOOKED(&V_inet_pfil_hook)
+ if (runfilt && (PFIL_HOOKED_OUT(V_inet_pfil_head)
#ifdef INET6
- || PFIL_HOOKED(&V_inet6_pfil_hook)
+ || PFIL_HOOKED_OUT(V_inet6_pfil_head)
#endif
)) {
if (used == 0) {
@@ -3101,6 +3101,7 @@ bridge_pfil(struct mbuf **mp, struct ifnet *bifp, struct ifnet *ifp, int dir)
struct ip *ip;
struct llc llc1;
u_int16_t ether_type;
+ pfil_return_t rv;
snap = 0;
error = -1; /* Default error if not error == 0 */
@@ -3172,14 +3173,14 @@ bridge_pfil(struct mbuf **mp, struct ifnet *bifp, struct ifnet *ifp, int dir)
}
/* Run the packet through pfil before stripping link headers */
- if (PFIL_HOOKED(&V_link_pfil_hook) && V_pfil_ipfw != 0 &&
- dir == PFIL_OUT && ifp != NULL) {
-
- error = pfil_run_hooks(&V_link_pfil_hook, mp, ifp, dir, 0,
- NULL);
-
- if (*mp == NULL || error != 0) /* packet consumed by filter */
- return (error);
+ if (PFIL_HOOKED_OUT(V_link_pfil_head) && V_pfil_ipfw != 0 &&
+ dir == PFIL_OUT && ifp != NULL) {
+ switch (pfil_run_hooks(V_link_pfil_head, mp, ifp, dir, NULL)) {
+ case PFIL_DROPPED:
+ return (EPERM);
+ case PFIL_CONSUMED:
+ return (0);
+ }
}
/* Strip off the Ethernet header and keep a copy. */
@@ -3217,6 +3218,7 @@ bridge_pfil(struct mbuf **mp, struct ifnet *bifp, struct ifnet *ifp, int dir)
/*
* Run the packet through pfil
*/
+ rv = PFIL_PASS;
switch (ether_type) {
case ETHERTYPE_IP:
/*
@@ -3226,25 +3228,19 @@ bridge_pfil(struct mbuf **mp, struct ifnet *bifp, struct ifnet *ifp, int dir)
* Keep the order:
* in_if -> bridge_if -> out_if
*/
- if (V_pfil_bridge && dir == PFIL_OUT && bifp != NULL)
- error = pfil_run_hooks(&V_inet_pfil_hook, mp, bifp,
- dir, 0, NULL);
-
- if (*mp == NULL || error != 0) /* filter may consume */
+ if (V_pfil_bridge && dir == PFIL_OUT && bifp != NULL && (rv =
+ pfil_run_hooks(V_inet_pfil_head, mp, bifp, dir, NULL)) !=
+ PFIL_PASS)
break;
- if (V_pfil_member && ifp != NULL)
- error = pfil_run_hooks(&V_inet_pfil_hook, mp, ifp,
- dir, 0, NULL);
-
- if (*mp == NULL || error != 0) /* filter may consume */
+ if (V_pfil_member && ifp != NULL && (rv =
+ pfil_run_hooks(V_inet_pfil_head, mp, ifp, dir, NULL)) !=
+ PFIL_PASS)
break;
- if (V_pfil_bridge && dir == PFIL_IN && bifp != NULL)
- error = pfil_run_hooks(&V_inet_pfil_hook, mp, bifp,
- dir, 0, NULL);
-
- if (*mp == NULL || error != 0) /* filter may consume */
+ if (V_pfil_bridge && dir == PFIL_IN && bifp != NULL && (rv =
+ pfil_run_hooks(V_inet_pfil_head, mp, bifp, dir, NULL)) !=
+ PFIL_PASS)
break;
/* check if we need to fragment the packet */
@@ -3280,35 +3276,33 @@ bridge_pfil(struct mbuf **mp, struct ifnet *bifp, struct ifnet *ifp, int dir)
break;
#ifdef INET6
case ETHERTYPE_IPV6:
- if (V_pfil_bridge && dir == PFIL_OUT && bifp != NULL)
- error = pfil_run_hooks(&V_inet6_pfil_hook, mp, bifp,
- dir, 0, NULL);
-
- if (*mp == NULL || error != 0) /* filter may consume */
+ if (V_pfil_bridge && dir == PFIL_OUT && bifp != NULL && (rv =
+ pfil_run_hooks(V_inet6_pfil_head, mp, bifp, dir, NULL)) !=
+ PFIL_PASS)
break;
- if (V_pfil_member && ifp != NULL)
- error = pfil_run_hooks(&V_inet6_pfil_hook, mp, ifp,
- dir, 0, NULL);
-
- if (*mp == NULL || error != 0) /* filter may consume */
+ if (V_pfil_member && ifp != NULL && (rv =
+ pfil_run_hooks(V_inet6_pfil_head, mp, ifp, dir, NULL)) !=
+ PFIL_PASS)
break;
- if (V_pfil_bridge && dir == PFIL_IN && bifp != NULL)
- error = pfil_run_hooks(&V_inet6_pfil_hook, mp, bifp,
- dir, 0, NULL);
+ if (V_pfil_bridge && dir == PFIL_IN && bifp != NULL && (rv =
+ pfil_run_hooks(V_inet6_pfil_head, mp, bifp, dir, NULL)) !=
+ PFIL_PASS)
+ break;
break;
#endif
+ }
+
+ switch (rv) {
+ case PFIL_CONSUMED:
+ return (0);
+ case PFIL_DROPPED:
+ return (EPERM);
default:
- error = 0;
break;
}
- if (*mp == NULL)
- return (error);
- if (error != 0)
- goto bad;
-
error = -1;
/*
diff --git a/sys/net/if_enc.c b/sys/net/if_enc.c
index 01e416535dbd..7bb196b672c1 100644
--- a/sys/net/if_enc.c
+++ b/sys/net/if_enc.c
@@ -285,24 +285,24 @@ enc_hhook(int32_t hhook_type, int32_t hhook_id, void *udata, void *ctx_data,
switch (hhook_id) {
#ifdef INET
case AF_INET:
- ph = &V_inet_pfil_hook;
+ ph = V_inet_pfil_head;
break;
#endif
#ifdef INET6
case AF_INET6:
- ph = &V_inet6_pfil_hook;
+ ph = V_inet6_pfil_head;
break;
#endif
default:
ph = NULL;
}
- if (ph == NULL || !PFIL_HOOKED(ph))
+ if (ph == NULL || (pdir == PFIL_OUT && !PFIL_HOOKED_OUT(ph)) ||
+ (pdir == PFIL_IN && !PFIL_HOOKED_IN(ph)))
return (0);
/* Make a packet looks like it was received on enc(4) */
rcvif = (*ctx->mp)->m_pkthdr.rcvif;
(*ctx->mp)->m_pkthdr.rcvif = ifp;
- if (pfil_run_hooks(ph, ctx->mp, ifp, pdir, 0, ctx->inp) != 0 ||
- *ctx->mp == NULL) {
+ if (pfil_run_hooks(ph, ctx->mp, ifp, pdir, ctx->inp) != PFIL_PASS) {
*ctx->mp = NULL; /* consumed by filter */
return (EACCES);
}
diff --git a/sys/net/if_ethersubr.c b/sys/net/if_ethersubr.c
index 628e32b17642..ff7705b2e21d 100644
--- a/sys/net/if_ethersubr.c
+++ b/sys/net/if_ethersubr.c
@@ -90,7 +90,7 @@ CTASSERT(sizeof (struct ether_header) == ETHER_ADDR_LEN * 2 + 2);
CTASSERT(sizeof (struct ether_addr) == ETHER_ADDR_LEN);
#endif
-VNET_DEFINE(struct pfil_head, link_pfil_hook); /* Packet filter hooks */
+VNET_DEFINE(pfil_head_t, link_pfil_head); /* Packet filter hooks */
/* netgraph node hooks for ng_ether(4) */
void (*ng_ether_input_p)(struct ifnet *ifp, struct mbuf **mp);
@@ -457,7 +457,6 @@ ether_set_pcp(struct mbuf **mp, struct ifnet *ifp, uint8_t pcp)
int
ether_output_frame(struct ifnet *ifp, struct mbuf *m)
{
- int error;
uint8_t pcp;
pcp = ifp->if_pcp;
@@ -465,15 +464,14 @@ ether_output_frame(struct ifnet *ifp, struct mbuf *m)
!ether_set_pcp(&m, ifp, pcp))
return (0);
- if (PFIL_HOOKED(&V_link_pfil_hook)) {
- error = pfil_run_hooks(&V_link_pfil_hook, &m, ifp,
- PFIL_OUT, 0, NULL);
- if (error != 0)
+ if (PFIL_HOOKED_OUT(V_link_pfil_head))
+ switch (pfil_run_hooks(V_link_pfil_head, &m, ifp, PFIL_OUT,
+ NULL)) {
+ case PFIL_DROPPED:
return (EACCES);
-
- if (m == NULL)
+ case PFIL_CONSUMED:
return (0);
- }
+ }
#ifdef EXPERIMENTAL
#if defined(INET6) && defined(INET)
@@ -737,14 +735,14 @@ SYSINIT(ether, SI_SUB_INIT_IF, SI_ORDER_ANY, ether_init, NULL);
static void
vnet_ether_init(__unused void *arg)
{
- int i;
+ struct pfil_head_args args;
+
+ args.pa_version = PFIL_VERSION;
+ args.pa_flags = PFIL_IN | PFIL_OUT;
+ args.pa_type = PFIL_TYPE_ETHERNET;
+ args.pa_headname = PFIL_ETHER_NAME;
+ V_link_pfil_head = pfil_head_register(&args);
- /* Initialize packet filter hooks. */
- V_link_pfil_hook.ph_type = PFIL_TYPE_AF;
- V_link_pfil_hook.ph_af = AF_LINK;
- if ((i = pfil_head_register(&V_link_pfil_hook)) != 0)
- printf("%s: WARNING: unable to register pfil link hook, "
- "error %d\n", __func__, i);
#ifdef VIMAGE
netisr_register_vnet(&ether_nh);
#endif
@@ -756,11 +754,8 @@ VNET_SYSINIT(vnet_ether_init, SI_SUB_PROTO_IF, SI_ORDER_ANY,
static void
vnet_ether_pfil_destroy(__unused void *arg)
{
- int i;
- if ((i = pfil_head_unregister(&V_link_pfil_hook)) != 0)
- printf("%s: WARNING: unable to unregister pfil link hook, "
- "error %d\n", __func__, i);
+ pfil_head_unregister(V_link_pfil_head);
}
VNET_SYSUNINIT(vnet_ether_pfil_uninit, SI_SUB_PROTO_PFIL, SI_ORDER_ANY,
vnet_ether_pfil_destroy, NULL);
@@ -818,10 +813,8 @@ ether_demux(struct ifnet *ifp, struct mbuf *m)
KASSERT(ifp != NULL, ("%s: NULL interface pointer", __func__));
/* Do not grab PROMISC frames in case we are re-entered. */
- if (PFIL_HOOKED(&V_link_pfil_hook) && !(m->m_flags & M_PROMISC)) {
- i = pfil_run_hooks(&V_link_pfil_hook, &m, ifp, PFIL_IN, 0,
- NULL);
-
+ if (PFIL_HOOKED_IN(V_link_pfil_head) && !(m->m_flags & M_PROMISC)) {
+ i = pfil_run_hooks(V_link_pfil_head, &m, ifp, PFIL_IN, NULL);
if (i != 0 || m == NULL)
return;
}
diff --git a/sys/net/if_var.h b/sys/net/if_var.h
index 42241e74fb81..22cac21b2136 100644
--- a/sys/net/if_var.h
+++ b/sys/net/if_var.h
@@ -95,8 +95,9 @@ CK_STAILQ_HEAD(ifmultihead, ifmultiaddr);
CK_STAILQ_HEAD(ifgrouphead, ifg_group);
#ifdef _KERNEL
-VNET_DECLARE(struct pfil_head, link_pfil_hook); /* packet filter hooks */
-#define V_link_pfil_hook VNET(link_pfil_hook)
+VNET_DECLARE(struct pfil_head *, link_pfil_head);
+#define V_link_pfil_head VNET(link_pfil_head)
+#define PFIL_ETHER_NAME "ethernet"
#define HHOOK_IPSEC_INET 0
#define HHOOK_IPSEC_INET6 1
diff --git a/sys/net/pfil.c b/sys/net/pfil.c
index 96069123a935..acfb25467fee 100644
--- a/sys/net/pfil.c
+++ b/sys/net/pfil.c
@@ -4,6 +4,7 @@
/*-
* SPDX-License-Identifier: BSD-3-Clause
*
+ * Copyright (c) 2019 Gleb Smirnoff <glebius@FreeBSD.org>
* Copyright (c) 1996 Matthew R. Green
* All rights reserved.
*
@@ -32,15 +33,15 @@
*/
#include <sys/param.h>
+#include <sys/conf.h>
#include <sys/kernel.h>
+#include <sys/epoch.h>
#include <sys/errno.h>
#include <sys/lock.h>
#include <sys/malloc.h>
-#include <sys/rmlock.h>
#include <sys/socket.h>
#include <sys/socketvar.h>
#include <sys/systm.h>
-#include <sys/condvar.h>
#include <sys/lock.h>
#include <sys/mutex.h>
#include <sys/proc.h>
@@ -50,426 +51,609 @@
#include <net/if_var.h>
#include <net/pfil.h>
-static struct mtx pfil_global_lock;
-
-MTX_SYSINIT(pfil_heads_lock, &pfil_global_lock, "pfil_head_list lock",
- MTX_DEF);
-
-static struct packet_filter_hook *pfil_chain_get(int, struct pfil_head *);
-static int pfil_chain_add(pfil_chain_t *, struct packet_filter_hook *, int);
-static int pfil_chain_remove(pfil_chain_t *, void *, void *);
-static int pfil_add_hook_priv(void *, void *, int, struct pfil_head *, bool);
+static MALLOC_DEFINE(M_PFIL, "pfil", "pfil(9) packet filter hooks");
+
+static int pfil_ioctl(struct cdev *, u_long, caddr_t, int, struct thread *);
+static struct cdevsw pfil_cdevsw = {
+ .d_ioctl = pfil_ioctl,
+ .d_name = PFILDEV,
+ .d_version = D_VERSION,
+};
+static struct cdev *pfil_dev;
+
+static struct mtx pfil_lock;
+MTX_SYSINIT(pfil_mtxinit, &pfil_lock, "pfil(9) lock", MTX_DEF);
+#define PFIL_LOCK() mtx_lock(&pfil_lock)
+#define PFIL_UNLOCK() mtx_unlock(&pfil_lock)
+#define PFIL_LOCK_ASSERT() mtx_assert(&pfil_lock, MA_OWNED)
+
+#define PFIL_EPOCH net_epoch_preempt
+#define PFIL_EPOCH_ENTER(et) epoch_enter_preempt(net_epoch_preempt, &(et))
+#define PFIL_EPOCH_EXIT(et) epoch_exit_preempt(net_epoch_preempt, &(et))
+
+struct pfil_hook {
+ pfil_func_t hook_func;
+ void *hook_ruleset;
+ int hook_flags;
+ int hook_links;
+ enum pfil_types hook_type;
+ const char *hook_modname;
+ const char *hook_rulname;
+ LIST_ENTRY(pfil_hook) hook_list;
+};
+
+struct pfil_link {
+ CK_STAILQ_ENTRY(pfil_link) link_chain;
+ pfil_func_t link_func;
+ void *link_ruleset;
+ int link_flags;
+ struct pfil_hook *link_hook;
+ struct epoch_context link_epoch_ctx;
+};
+
+typedef CK_STAILQ_HEAD(pfil_chain, pfil_link) pfil_chain_t;
+struct pfil_head {
+ int head_nhooksin;
+ int head_nhooksout;
+ pfil_chain_t head_in;
+ pfil_chain_t head_out;
+ int head_flags;
+ enum pfil_types head_type;
+ LIST_ENTRY(pfil_head) head_list;
+ const char *head_name;
+};
LIST_HEAD(pfilheadhead, pfil_head);
-VNET_DEFINE(struct pfilheadhead, pfil_head_list);
+VNET_DEFINE_STATIC(struct pfilheadhead, pfil_head_list) =
+ LIST_HEAD_INITIALIZER(pfil_head_list);
#define V_pfil_head_list VNET(pfil_head_list)
-VNET_DEFINE(struct rmlock, pfil_lock);
-#define V_pfil_lock VNET(pfil_lock)
-
-#define PFIL_LOCK_INIT_REAL(l, t) \
- rm_init_flags(l, "PFil " t " rmlock", RM_RECURSE)
-#define PFIL_LOCK_DESTROY_REAL(l) \
- rm_destroy(l)
-#define PFIL_LOCK_INIT(p) do { \
- if ((p)->flags & PFIL_FLAG_PRIVATE_LOCK) { \
- PFIL_LOCK_INIT_REAL(&(p)->ph_lock, "private"); \
- (p)->ph_plock = &(p)->ph_lock; \
- } else \
- (p)->ph_plock = &V_pfil_lock; \
-} while (0)
-#define PFIL_LOCK_DESTROY(p) do { \
- if ((p)->flags & PFIL_FLAG_PRIVATE_LOCK) \
- PFIL_LOCK_DESTROY_REAL((p)->ph_plock); \
-} while (0)
-
-#define PFIL_TRY_RLOCK(p, t) rm_try_rlock((p)->ph_plock, (t))
-#define PFIL_RLOCK(p, t) rm_rlock((p)->ph_plock, (t))
-#define PFIL_WLOCK(p) rm_wlock((p)->ph_plock)
-#define PFIL_RUNLOCK(p, t) rm_runlock((p)->ph_plock, (t))
-#define PFIL_WUNLOCK(p) rm_wunlock((p)->ph_plock)
-#define PFIL_WOWNED(p) rm_wowned((p)->ph_plock)
-
-#define PFIL_HEADLIST_LOCK() mtx_lock(&pfil_global_lock)
-#define PFIL_HEADLIST_UNLOCK() mtx_unlock(&pfil_global_lock)
-/*
- * pfil_run_hooks() runs the specified packet filter hook chain.
- */
-int
-pfil_run_hooks(struct pfil_head *ph, struct mbuf **mp, struct ifnet *ifp,
- int dir, int flags, struct inpcb *inp)
+LIST_HEAD(pfilhookhead, pfil_hook);
+VNET_DEFINE_STATIC(struct pfilhookhead, pfil_hook_list) =
+ LIST_HEAD_INITIALIZER(pfil_hook_list);
+#define V_pfil_hook_list VNET(pfil_hook_list)
+
+static struct pfil_link *pfil_link_remove(pfil_chain_t *, pfil_hook_t );
+static void pfil_link_free(epoch_context_t);
+
+static __noinline int
+pfil_fake_mbuf(pfil_func_t func, void *mem, struct ifnet *ifp, int flags,
+ void *ruleset, struct inpcb *inp)
{
- struct rm_priotracker rmpt;
- struct packet_filter_hook *pfh;
- struct mbuf *m = *mp;
- int rv = 0;
-
- PFIL_RLOCK(ph, &rmpt);
- KASSERT(ph->ph_nhooks >= 0, ("Pfil hook count dropped < 0"));
- for (pfh = pfil_chain_get(dir, ph); pfh != NULL;
- pfh = TAILQ_NEXT(pfh, pfil_chain)) {
- if (pfh->pfil_func_flags != NULL) {
- rv = (*pfh->pfil_func_flags)(pfh->pfil_arg, &m, ifp,
- dir, flags, inp);
- if (rv != 0 || m == NULL)
- break;
- }
- if (pfh->pfil_func != NULL) {
- rv = (*pfh->pfil_func)(pfh->pfil_arg, &m, ifp, dir,
- inp);
- if (rv != 0 || m == NULL)
- break;
- }
+ struct mbuf m, *mp;
+ pfil_return_t rv;
+
+ (void)m_init(&m, M_NOWAIT, MT_DATA, M_NOFREE | M_PKTHDR);
+ m_extadd(&m, mem, PFIL_LENGTH(flags), NULL, NULL, NULL, 0, EXT_RXRING);
+ m.m_len = m.m_pkthdr.len = PFIL_LENGTH(flags);
+ mp = &m;
+ flags &= ~(PFIL_MEMPTR | PFIL_LENMASK);
+
+ rv = func(&mp, ifp, flags, ruleset, inp);
+ if (rv == PFIL_PASS && mp != &m) {
+ /*
+ * Firewalls that need pfil_fake_mbuf() most likely don't
+ * know to return PFIL_REALLOCED.
+ */
+ rv = PFIL_REALLOCED;
+ *(struct mbuf **)mem = mp;
}
- PFIL_RUNLOCK(ph, &rmpt);
- *mp = m;
+
return (rv);
}
-static struct packet_filter_hook *
-pfil_chain_get(int dir, struct pfil_head *ph)
+/*
+ * pfil_run_hooks() runs the specified packet filter hook chain.
+ */
+int
+pfil_run_hooks(struct pfil_head *head, pfil_packet_t p, struct ifnet *ifp,
+ int flags, struct inpcb *inp)
{
-
- if (dir == PFIL_IN)
- return (TAILQ_FIRST(&ph->ph_in));
- else if (dir == PFIL_OUT)
- return (TAILQ_FIRST(&ph->ph_out));
+ struct epoch_tracker et;
+ pfil_chain_t *pch;
+ struct pfil_link *link;
+ pfil_return_t rv, rvi;
+
+ if (PFIL_DIR(flags) == PFIL_IN)
+ pch = &head->head_in;
+ else if (__predict_true(PFIL_DIR(flags) == PFIL_OUT))
+ pch = &head->head_out;
else
- return (NULL);
+ panic("%s: bogus flags %d", __func__, flags);
+
+ rv = PFIL_PASS;
+ PFIL_EPOCH_ENTER(et);
+ CK_STAILQ_FOREACH(link, pch, link_chain) {
+ if ((flags & PFIL_MEMPTR) && !(link->link_flags & PFIL_MEMPTR))
+ rvi = pfil_fake_mbuf(link->link_func, p.mem, ifp,
+ flags, link->link_ruleset, inp);
+ else
+ rvi = (*link->link_func)(p, ifp, flags,
+ link->link_ruleset, inp);
+ if (rvi == PFIL_DROPPED || rvi == PFIL_CONSUMED) {
+ rv = rvi;
+ break;
+ } else if (rv == PFIL_REALLOCED) {
+ flags &= ~(PFIL_MEMPTR | PFIL_LENMASK);
+ rv = rvi;
+ }
+ }
+ PFIL_EPOCH_EXIT(et);
+ return (rvi);
}
/*
- * pfil_try_rlock() acquires rm reader lock for specified head
- * if this is immediately possible.
+ * pfil_head_register() registers a pfil_head with the packet filter hook
+ * mechanism.
*/
-int
-pfil_try_rlock(struct pfil_head *ph, struct rm_priotracker *tracker)
+pfil_head_t
+pfil_head_register(struct pfil_head_args *pa)
{
+ struct pfil_head *head, *list;
+
+ MPASS(pa->pa_version == PFIL_VERSION);
+
+ head = malloc(sizeof(struct pfil_head), M_PFIL, M_WAITOK);
- return (PFIL_TRY_RLOCK(ph, tracker));
+ head->head_nhooksin = head->head_nhooksout = 0;
+ head->head_flags = pa->pa_flags;
+ head->head_type = pa->pa_type;
+ head->head_name = pa->pa_headname;
+ CK_STAILQ_INIT(&head->head_in);
+ CK_STAILQ_INIT(&head->head_out);
+
+ PFIL_LOCK();
+ LIST_FOREACH(list, &V_pfil_head_list, head_list)
+ if (strcmp(pa->pa_headname, list->head_name) == 0) {
+ printf("pfil: duplicate head \"%s\"\n",
+ pa->pa_headname);
+ }
+ LIST_INSERT_HEAD(&V_pfil_head_list, head, head_list);
+ PFIL_UNLOCK();
+
+ return (head);
}
/*
- * pfil_rlock() acquires rm reader lock for specified head.
+ * pfil_head_unregister() removes a pfil_head from the packet filter hook
+ * mechanism. The producer of the hook promises that all outstanding
+ * invocations of the hook have completed before it unregisters the hook.
*/
void
-pfil_rlock(struct pfil_head *ph, struct rm_priotracker *tracker)
+pfil_head_unregister(pfil_head_t ph)
{
+ struct pfil_link *link, *next;
+
+ PFIL_LOCK();
+ LIST_REMOVE(ph, head_list);
- PFIL_RLOCK(ph, tracker);
+ CK_STAILQ_FOREACH_SAFE(link, &ph->head_in, link_chain, next) {
+ link->link_hook->hook_links--;
+ free(link, M_PFIL);
+ }
+ CK_STAILQ_FOREACH_SAFE(link, &ph->head_out, link_chain, next) {
+ link->link_hook->hook_links--;
+ free(link, M_PFIL);
+ }
+ PFIL_UNLOCK();
}
-/*
- * pfil_runlock() releases reader lock for specified head.
- */
-void
-pfil_runlock(struct pfil_head *ph, struct rm_priotracker *tracker)
+pfil_hook_t
+pfil_add_hook(struct pfil_hook_args *pa)
{
+ struct pfil_hook *hook, *list;
+
+ MPASS(pa->pa_version == PFIL_VERSION);
+
+ hook = malloc(sizeof(struct pfil_hook), M_PFIL, M_WAITOK | M_ZERO);
+ hook->hook_func = pa->pa_func;
+ hook->hook_ruleset = pa->pa_ruleset;
+ hook->hook_flags = pa->pa_flags;
+ hook->hook_type = pa->pa_type;
+ hook->hook_modname = pa->pa_modname;
+ hook->hook_rulname = pa->pa_rulname;
+
+ PFIL_LOCK();
+ LIST_FOREACH(list, &V_pfil_hook_list, hook_list)
+ if (strcmp(pa->pa_modname, list->hook_modname) == 0 &&
+ strcmp(pa->pa_rulname, list->hook_rulname) == 0) {
+ printf("pfil: duplicate hook \"%s:%s\"\n",
+ pa->pa_modname, pa->pa_rulname);
+ }
+ LIST_INSERT_HEAD(&V_pfil_hook_list, hook, hook_list);
+ PFIL_UNLOCK();
- PFIL_RUNLOCK(ph, tracker);
+ return (hook);
}
-/*
- * pfil_wlock() acquires writer lock for specified head.
- */
-void
-pfil_wlock(struct pfil_head *ph)
+static int
+pfil_unlink(struct pfil_link_args *pa, pfil_head_t head, pfil_hook_t hook)
{
+ struct pfil_link *in, *out;
+
+ PFIL_LOCK_ASSERT();
+
+ if (pa->pa_flags & PFIL_IN) {
+ in = pfil_link_remove(&head->head_in, hook);
+ if (in != NULL) {
+ head->head_nhooksin--;
+ hook->hook_links--;
+ }
+ } else
+ in = NULL;
+ if (pa->pa_flags & PFIL_OUT) {
+ out = pfil_link_remove(&head->head_out, hook);
+ if (out != NULL) {
+ head->head_nhooksout--;
+ hook->hook_links--;
+ }
+ } else
+ out = NULL;
+ PFIL_UNLOCK();
+
+ if (in != NULL)
+ epoch_call(PFIL_EPOCH, &in->link_epoch_ctx, pfil_link_free);
+ if (out != NULL)
+ epoch_call(PFIL_EPOCH, &out->link_epoch_ctx, pfil_link_free);
- PFIL_WLOCK(ph);
+ if (in == NULL && out == NULL)
+ return (ENOENT);
+ else
+ return (0);
}
-/*
- * pfil_wunlock() releases writer lock for specified head.
- */
-void
-pfil_wunlock(struct pfil_head *ph)
+int
+pfil_link(struct pfil_link_args *pa)
{
+ struct pfil_link *in, *out, *link;
+ struct pfil_head *head;
+ struct pfil_hook *hook;
+ int error;
+
+ MPASS(pa->pa_version == PFIL_VERSION);
+
+ if ((pa->pa_flags & (PFIL_IN | PFIL_UNLINK)) == PFIL_IN)
+ in = malloc(sizeof(*in), M_PFIL, M_WAITOK | M_ZERO);
+ else
+ in = NULL;
+ if ((pa->pa_flags & (PFIL_OUT | PFIL_UNLINK)) == PFIL_OUT)
+ out = malloc(sizeof(*out), M_PFIL, M_WAITOK | M_ZERO);
+ else
+ out = NULL;
+
+ PFIL_LOCK();
+ if (pa->pa_flags & PFIL_HEADPTR)
+ head = pa->pa_head;
+ else
+ LIST_FOREACH(head, &V_pfil_head_list, head_list)
+ if (strcmp(pa->pa_headname, head->head_name) == 0)
+ break;
+ if (pa->pa_flags & PFIL_HOOKPTR)
+ hook = pa->pa_hook;
+ else
+ LIST_FOREACH(hook, &V_pfil_hook_list, hook_list)
+ if (strcmp(pa->pa_modname, hook->hook_modname) == 0 &&
+ strcmp(pa->pa_rulname, hook->hook_rulname) == 0)
+ break;
+ if (head == NULL || hook == NULL) {
+ error = ENOENT;
+ goto fail;
+ }
+
+ if (pa->pa_flags & PFIL_UNLINK)
+ return (pfil_unlink(pa, head, hook));
+
+ if (head->head_type != hook->hook_type ||
+ ((hook->hook_flags & pa->pa_flags) & ~head->head_flags)) {
+ error = EINVAL;
+ goto fail;
+ }
+
+ if (pa->pa_flags & PFIL_IN)
+ CK_STAILQ_FOREACH(link, &head->head_in, link_chain)
+ if (link->link_hook == hook) {
+ error = EEXIST;
+ goto fail;
+ }
+ if (pa->pa_flags & PFIL_OUT)
+ CK_STAILQ_FOREACH(link, &head->head_out, link_chain)
+ if (link->link_hook == hook) {
+ error = EEXIST;
+ goto fail;
+ }
+
+ if (pa->pa_flags & PFIL_IN) {
+ in->link_hook = hook;
+ in->link_func = hook->hook_func;
+ in->link_flags = hook->hook_flags;
+ in->link_ruleset = hook->hook_ruleset;
+ if (pa->pa_flags & PFIL_APPEND)
+ CK_STAILQ_INSERT_TAIL(&head->head_in, in, link_chain);
+ else
+ CK_STAILQ_INSERT_HEAD(&head->head_in, in, link_chain);
+ hook->hook_links++;
+ head->head_nhooksin++;
+ }
+ if (pa->pa_flags & PFIL_OUT) {
+ out->link_hook = hook;
+ out->link_func = hook->hook_func;
+ out->link_flags = hook->hook_flags;
+ out->link_ruleset = hook->hook_ruleset;
+ if (pa->pa_flags & PFIL_APPEND)
+ CK_STAILQ_INSERT_HEAD(&head->head_out, out, link_chain);
+ else
+ CK_STAILQ_INSERT_TAIL(&head->head_out, out, link_chain);
+ hook->hook_links++;
+ head->head_nhooksout++;
+ }
+ PFIL_UNLOCK();
+
+ return (0);
- PFIL_WUNLOCK(ph);
+fail:
+ PFIL_UNLOCK();
+ free(in, M_PFIL);
+ free(out, M_PFIL);
+ return (error);
}
-/*
- * pfil_wowned() returns a non-zero value if the current thread owns
- * an exclusive lock.
- */
-int
-pfil_wowned(struct pfil_head *ph)
+static void
+pfil_link_free(epoch_context_t ctx)
{
+ struct pfil_link *link;
- return (PFIL_WOWNED(ph));
+ link = __containerof(ctx, struct pfil_link, link_epoch_ctx);
+ free(link, M_PFIL);
}
/*
- * pfil_head_register() registers a pfil_head with the packet filter hook
- * mechanism.
+ * pfil_remove_hook removes a filter from all filtering points.
*/
-int
-pfil_head_register(struct pfil_head *ph)
+void
+pfil_remove_hook(pfil_hook_t hook)
{
- struct pfil_head *lph;
-
- PFIL_HEADLIST_LOCK();
- LIST_FOREACH(lph, &V_pfil_head_list, ph_list) {
- if (ph->ph_type == lph->ph_type &&
- ph->ph_un.phu_val == lph->ph_un.phu_val) {
- PFIL_HEADLIST_UNLOCK();
- return (EEXIST);
+ struct pfil_head *head;
+ struct pfil_link *in, *out;
+
+ PFIL_LOCK();
+ LIST_FOREACH(head, &V_pfil_head_list, head_list) {
+retry:
+ in = pfil_link_remove(&head->head_in, hook);
+ if (in != NULL) {
+ head->head_nhooksin--;
+ hook->hook_links--;
+ epoch_call(PFIL_EPOCH, &in->link_epoch_ctx,
+ pfil_link_free);
}
+ out = pfil_link_remove(&head->head_out, hook);
+ if (out != NULL) {
+ head->head_nhooksout--;
+ hook->hook_links--;
+ epoch_call(PFIL_EPOCH, &out->link_epoch_ctx,
+ pfil_link_free);
+ }
+ if (in != NULL || out != NULL)
+ /* What if some stupid admin put same filter twice? */
+ goto retry;
}
- PFIL_LOCK_INIT(ph);
- ph->ph_nhooks = 0;
- TAILQ_INIT(&ph->ph_in);
- TAILQ_INIT(&ph->ph_out);
- LIST_INSERT_HEAD(&V_pfil_head_list, ph, ph_list);
- PFIL_HEADLIST_UNLOCK();
- return (0);
+ LIST_REMOVE(hook, hook_list);
+ PFIL_UNLOCK();
+ MPASS(hook->hook_links == 0);
+ free(hook, M_PFIL);
}
/*
- * pfil_head_unregister() removes a pfil_head from the packet filter hook
- * mechanism. The producer of the hook promises that all outstanding
- * invocations of the hook have completed before it unregisters the hook.
+ * Internal: Remove a pfil hook from a hook chain.
*/
-int
-pfil_head_unregister(struct pfil_head *ph)
+static struct pfil_link *
+pfil_link_remove(pfil_chain_t *chain, pfil_hook_t hook)
{
- struct packet_filter_hook *pfh, *pfnext;
-
- PFIL_HEADLIST_LOCK();
- LIST_REMOVE(ph, ph_list);
- PFIL_HEADLIST_UNLOCK();
- TAILQ_FOREACH_SAFE(pfh, &ph->ph_in, pfil_chain, pfnext)
- free(pfh, M_IFADDR);
- TAILQ_FOREACH_SAFE(pfh, &ph->ph_out, pfil_chain, pfnext)
- free(pfh, M_IFADDR);
- PFIL_LOCK_DESTROY(ph);
- return (0);
-}
+ struct pfil_link *link;
-/*
- * pfil_head_get() returns the pfil_head for a given key/dlt.
- */
-struct pfil_head *
-pfil_head_get(int type, u_long val)
-{
- struct pfil_head *ph;
+ PFIL_LOCK_ASSERT();
- PFIL_HEADLIST_LOCK();
- LIST_FOREACH(ph, &V_pfil_head_list, ph_list)
- if (ph->ph_type == type && ph->ph_un.phu_val == val)
- break;
- PFIL_HEADLIST_UNLOCK();
- return (ph);
+ CK_STAILQ_FOREACH(link, chain, link_chain)
+ if (link->link_hook == hook) {
+ CK_STAILQ_REMOVE(chain, link, pfil_link, link_chain);
+ return (link);
+ }
+
+ return (NULL);
}
-/*
- * pfil_add_hook_flags() adds a function to the packet filter hook. the
- * flags are:
- * PFIL_IN call me on incoming packets
- * PFIL_OUT call me on outgoing packets
- * PFIL_ALL call me on all of the above
- * PFIL_WAITOK OK to call malloc with M_WAITOK.
- */
-int
-pfil_add_hook_flags(pfil_func_flags_t func, void *arg, int flags,
- struct pfil_head *ph)
+static void
+pfil_init(const void *unused __unused)
{
- return (pfil_add_hook_priv(func, arg, flags, ph, true));
+ struct make_dev_args args;
+ int error;
+
+ make_dev_args_init(&args);
+ args.mda_flags = MAKEDEV_WAITOK | MAKEDEV_CHECKNAME;
+ args.mda_devsw = &pfil_cdevsw;
+ args.mda_uid = UID_ROOT;
+ args.mda_gid = GID_WHEEL;
+ args.mda_mode = 0600;
+ error = make_dev_s(&args, &pfil_dev, PFILDEV);
+ KASSERT(error == 0, ("%s: failed to create dev: %d", __func__, error));
}
+/*
+ * Make sure the pfil bits are first before any possible subsystem which
+ * might piggyback on the SI_SUB_PROTO_PFIL.
+ */
+SYSINIT(pfil_init, SI_SUB_PROTO_PFIL, SI_ORDER_FIRST, pfil_init, NULL);
/*
- * pfil_add_hook() adds a function to the packet filter hook. the
- * flags are:
- * PFIL_IN call me on incoming packets
- * PFIL_OUT call me on outgoing packets
- * PFIL_ALL call me on all of the above
- * PFIL_WAITOK OK to call malloc with M_WAITOK.
+ * User control interface.
*/
-int
-pfil_add_hook(pfil_func_t func, void *arg, int flags, struct pfil_head *ph)
+static int pfilioc_listheads(struct pfilioc_list *);
+static int pfilioc_listhooks(struct pfilioc_list *);
+static int pfilioc_link(struct pfilioc_link *);
+
+static int
+pfil_ioctl(struct cdev *dev, u_long cmd, caddr_t addr, int flags,
+ struct thread *td)
{
- return (pfil_add_hook_priv(func, arg, flags, ph, false));
+ int error;
+
+ error = 0;
+ switch (cmd) {
+ case PFILIOC_LISTHEADS:
+ error = pfilioc_listheads((struct pfilioc_list *)addr);
+ break;
+ case PFILIOC_LISTHOOKS:
+ error = pfilioc_listhooks((struct pfilioc_list *)addr);
+ break;
+ case PFILIOC_LINK:
+ error = pfilioc_link((struct pfilioc_link *)addr);
+ break;
+ default:
+ return (EINVAL);
+ }
+
+ return (error);
}
static int
-pfil_add_hook_priv(void *func, void *arg, int flags,
- struct pfil_head *ph, bool hasflags)
+pfilioc_listheads(struct pfilioc_list *req)
{
- struct packet_filter_hook *pfh1 = NULL;
- struct packet_filter_hook *pfh2 = NULL;
- int err;
-
- if (flags & PFIL_IN) {
- pfh1 = (struct packet_filter_hook *)malloc(sizeof(*pfh1),
- M_IFADDR, (flags & PFIL_WAITOK) ? M_WAITOK : M_NOWAIT);
- if (pfh1 == NULL) {
- err = ENOMEM;
- goto error;
- }
+ struct pfil_head *head;
+ struct pfil_link *link;
+ struct pfilioc_head *iohead;
+ struct pfilioc_hook *iohook;
+ u_int nheads, nhooks, hd, hk;
+ int error;
+
+ PFIL_LOCK();
+restart:
+ nheads = nhooks = 0;
+ LIST_FOREACH(head, &V_pfil_head_list, head_list) {
+ nheads++;
+ nhooks += head->head_nhooksin + head->head_nhooksout;
}
- if (flags & PFIL_OUT) {
- pfh2 = (struct packet_filter_hook *)malloc(sizeof(*pfh1),
- M_IFADDR, (flags & PFIL_WAITOK) ? M_WAITOK : M_NOWAIT);
- if (pfh2 == NULL) {
- err = ENOMEM;
- goto error;
- }
- }
- PFIL_WLOCK(ph);
- if (flags & PFIL_IN) {
- pfh1->pfil_func_flags = hasflags ? func : NULL;
- pfh1->pfil_func = hasflags ? NULL : func;
- pfh1->pfil_arg = arg;
- err = pfil_chain_add(&ph->ph_in, pfh1, flags & ~PFIL_OUT);
- if (err)
- goto locked_error;
- ph->ph_nhooks++;
+ PFIL_UNLOCK();
+
+ if (req->pio_nheads < nheads || req->pio_nhooks < nhooks) {
+ req->pio_nheads = nheads;
+ req->pio_nhooks = nhooks;
+ return (0);
}
- if (flags & PFIL_OUT) {
- pfh2->pfil_func_flags = hasflags ? func : NULL;
- pfh2->pfil_func = hasflags ? NULL : func;
- pfh2->pfil_arg = arg;
- err = pfil_chain_add(&ph->ph_out, pfh2, flags & ~PFIL_IN);
- if (err) {
- if (flags & PFIL_IN)
- pfil_chain_remove(&ph->ph_in, func, arg);
- goto locked_error;
+
+ iohead = malloc(sizeof(*iohead) * nheads, M_TEMP, M_WAITOK);
+ iohook = malloc(sizeof(*iohook) * nhooks, M_TEMP, M_WAITOK);
+
+ hd = hk = 0;
+ PFIL_LOCK();
+ LIST_FOREACH(head, &V_pfil_head_list, head_list) {
+ if (hd + 1 > nheads ||
+ hk + head->head_nhooksin + head->head_nhooksout > nhooks) {
+ /* Configuration changed during malloc(). */
+ free(iohead, M_TEMP);
+ free(iohook, M_TEMP);
+ goto restart;
+ }
+ strlcpy(iohead[hd].pio_name, head->head_name,
+ sizeof(iohead[0].pio_name));
+ iohead[hd].pio_nhooksin = head->head_nhooksin;
+ iohead[hd].pio_nhooksout = head->head_nhooksout;
+ iohead[hd].pio_type = head->head_type;
+ CK_STAILQ_FOREACH(link, &head->head_in, link_chain) {
+ strlcpy(iohook[hk].pio_module,
+ link->link_hook->hook_modname,
+ sizeof(iohook[0].pio_module));
+ strlcpy(iohook[hk].pio_ruleset,
+ link->link_hook->hook_rulname,
+ sizeof(iohook[0].pio_ruleset));
+ hk++;
+ }
+ CK_STAILQ_FOREACH(link, &head->head_out, link_chain) {
+ strlcpy(iohook[hk].pio_module,
+ link->link_hook->hook_modname,
+ sizeof(iohook[0].pio_module));
+ strlcpy(iohook[hk].pio_ruleset,
+ link->link_hook->hook_rulname,
+ sizeof(iohook[0].pio_ruleset));
+ hk++;
}
- ph->ph_nhooks++;
+ hd++;
}
- PFIL_WUNLOCK(ph);
- return (0);
-locked_error:
- PFIL_WUNLOCK(ph);
-error:
- if (pfh1 != NULL)
- free(pfh1, M_IFADDR);
- if (pfh2 != NULL)
- free(pfh2, M_IFADDR);
- return (err);
-}
+ PFIL_UNLOCK();
-/*
- * pfil_remove_hook_flags removes a specific function from the packet filter hook
- * chain.
- */
-int
-pfil_remove_hook_flags(pfil_func_flags_t func, void *arg, int flags,
- struct pfil_head *ph)
-{
- return (pfil_remove_hook((pfil_func_t)func, arg, flags, ph));
-}
+ error = copyout(iohead, req->pio_heads,
+ sizeof(*iohead) * min(hd, req->pio_nheads));
+ if (error == 0)
+ error = copyout(iohook, req->pio_hooks,
+ sizeof(*iohook) * min(req->pio_nhooks, hk));
-/*
- * pfil_remove_hook removes a specific function from the packet filter hook
- * chain.
- */
-int
-pfil_remove_hook(pfil_func_t func, void *arg, int flags, struct pfil_head *ph)
-{
- int err = 0;
+ req->pio_nheads = hd;
+ req->pio_nhooks = hk;
- PFIL_WLOCK(ph);
- if (flags & PFIL_IN) {
- err = pfil_chain_remove(&ph->ph_in, func, arg);
- if (err == 0)
- ph->ph_nhooks--;
- }
- if ((err == 0) && (flags & PFIL_OUT)) {
- err = pfil_chain_remove(&ph->ph_out, func, arg);
- if (err == 0)
- ph->ph_nhooks--;
- }
- PFIL_WUNLOCK(ph);
- return (err);
-}
+ free(iohead, M_TEMP);
+ free(iohook, M_TEMP);
-/*
- * Internal: Add a new pfil hook into a hook chain.
- */
-static int
-pfil_chain_add(pfil_chain_t *chain, struct packet_filter_hook *pfh1, int flags)
-{
- struct packet_filter_hook *pfh;
-
- /*
- * First make sure the hook is not already there.
- */
- TAILQ_FOREACH(pfh, chain, pfil_chain)
- if (((pfh->pfil_func != NULL && pfh->pfil_func == pfh1->pfil_func) ||
- (pfh->pfil_func_flags != NULL &&
- pfh->pfil_func_flags == pfh1->pfil_func_flags)) &&
- pfh->pfil_arg == pfh1->pfil_arg)
- return (EEXIST);
-
- /*
- * Insert the input list in reverse order of the output list so that
- * the same path is followed in or out of the kernel.
- */
- if (flags & PFIL_IN)
- TAILQ_INSERT_HEAD(chain, pfh1, pfil_chain);
- else
- TAILQ_INSERT_TAIL(chain, pfh1, pfil_chain);
- return (0);
+ return (error);
}
-/*
- * Internal: Remove a pfil hook from a hook chain.
- */
static int
-pfil_chain_remove(pfil_chain_t *chain, void *func, void *arg)
+pfilioc_listhooks(struct pfilioc_list *req)
{
- struct packet_filter_hook *pfh;
-
- TAILQ_FOREACH(pfh, chain, pfil_chain)
- if ((pfh->pfil_func == func || pfh->pfil_func_flags == func) &&
- pfh->pfil_arg == arg) {
- TAILQ_REMOVE(chain, pfh, pfil_chain);
- free(pfh, M_IFADDR);
- return (0);
+ struct pfil_hook *hook;
+ struct pfilioc_hook *iohook;
+ u_int nhooks, hk;
+ int error;
+
+ PFIL_LOCK();
+restart:
+ nhooks = 0;
+ LIST_FOREACH(hook, &V_pfil_hook_list, hook_list)
+ nhooks++;
+ PFIL_UNLOCK();
+
+ if (req->pio_nhooks < nhooks) {
+ req->pio_nhooks = nhooks;
+ return (0);
+ }
+
+ iohook = malloc(sizeof(*iohook) * nhooks, M_TEMP, M_WAITOK);
+
+ hk = 0;
+ PFIL_LOCK();
+ LIST_FOREACH(hook, &V_pfil_hook_list, hook_list) {
+ if (hk + 1 > nhooks) {
+ /* Configuration changed during malloc(). */
+ free(iohook, M_TEMP);
+ goto restart;
}
- return (ENOENT);
-}
+ strlcpy(iohook[hk].pio_module, hook->hook_modname,
+ sizeof(iohook[0].pio_module));
+ strlcpy(iohook[hk].pio_ruleset, hook->hook_rulname,
+ sizeof(iohook[0].pio_ruleset));
+ iohook[hk].pio_type = hook->hook_type;
+ iohook[hk].pio_flags = hook->hook_flags;
+ hk++;
+ }
+ PFIL_UNLOCK();
-/*
- * Stuff that must be initialized for every instance (including the first of
- * course).
- */
-static void
-vnet_pfil_init(const void *unused __unused)
-{
+ error = copyout(iohook, req->pio_hooks,
+ sizeof(*iohook) * min(req->pio_nhooks, hk));
+ req->pio_nhooks = hk;
+ free(iohook, M_TEMP);
- LIST_INIT(&V_pfil_head_list);
- PFIL_LOCK_INIT_REAL(&V_pfil_lock, "shared");
+ return (error);
}
-/*
- * Called for the removal of each instance.
- */
-static void
-vnet_pfil_uninit(const void *unused __unused)
+static int
+pfilioc_link(struct pfilioc_link *req)
{
+ struct pfil_link_args args;
- KASSERT(LIST_EMPTY(&V_pfil_head_list),
- ("%s: pfil_head_list %p not empty", __func__, &V_pfil_head_list));
- PFIL_LOCK_DESTROY_REAL(&V_pfil_lock);
-}
+ if (req->pio_flags & ~(PFIL_IN | PFIL_OUT | PFIL_UNLINK | PFIL_APPEND))
+ return (EINVAL);
-/*
- * Starting up.
- *
- * VNET_SYSINIT is called for each existing vnet and each new vnet.
- * Make sure the pfil bits are first before any possible subsystem which
- * might piggyback on the SI_SUB_PROTO_PFIL.
- */
-VNET_SYSINIT(vnet_pfil_init, SI_SUB_PROTO_PFIL, SI_ORDER_FIRST,
- vnet_pfil_init, NULL);
-
-/*
- * Closing up shop. These are done in REVERSE ORDER. Not called on reboot.
- *
- * VNET_SYSUNINIT is called for each exiting vnet as it exits.
- */
-VNET_SYSUNINIT(vnet_pfil_uninit, SI_SUB_PROTO_PFIL, SI_ORDER_FIRST,
- vnet_pfil_uninit, NULL);
+ args.pa_version = PFIL_VERSION;
+ args.pa_flags = req->pio_flags;
+ args.pa_headname = req->pio_name;
+ args.pa_modname = req->pio_module;
+ args.pa_rulname = req->pio_ruleset;
+
+ return (pfil_link(&args));
+}
diff --git a/sys/net/pfil.h b/sys/net/pfil.h
index bfe108a1f1fe..13d78e6a277f 100644
--- a/sys/net/pfil.h
+++ b/sys/net/pfil.h
@@ -4,6 +4,7 @@
/*-
* SPDX-License-Identifier: BSD-3-Clause
*
+ * Copyright (c) 2019 Gleb Smirnoff <glebius@FreeBSD.org>
* Copyright (c) 1996 Matthew R. Green
* All rights reserved.
*
@@ -34,94 +35,158 @@
#ifndef _NET_PFIL_H_
#define _NET_PFIL_H_
-#include <sys/systm.h>
-#include <sys/queue.h>
-#include <sys/_lock.h>
-#include <sys/_mutex.h>
-#include <sys/lock.h>
-#include <sys/rmlock.h>
+#include <sys/ioccom.h>
+enum pfil_types {
+ PFIL_TYPE_IP4,
+ PFIL_TYPE_IP6,
+ PFIL_TYPE_ETHERNET,
+};
+
+#define MAXPFILNAME 64
+
+struct pfilioc_head {
+ char pio_name[MAXPFILNAME];
+ int pio_nhooksin;
+ int pio_nhooksout;
+ enum pfil_types pio_type;
+};
+
+struct pfilioc_hook {
+ char pio_module[MAXPFILNAME];
+ char pio_ruleset[MAXPFILNAME];
+ int pio_flags;
+ enum pfil_types pio_type;
+};
+
+struct pfilioc_list {
+ u_int pio_nheads;
+ u_int pio_nhooks;
+ struct pfilioc_head *pio_heads;
+ struct pfilioc_hook *pio_hooks;
+};
+
+struct pfilioc_link {
+ char pio_name[MAXPFILNAME];
+ char pio_module[MAXPFILNAME];
+ char pio_ruleset[MAXPFILNAME];
+ int pio_flags;
+};
+
+#define PFILDEV "pfil"
+#define PFILIOC_LISTHEADS _IOWR('P', 1, struct pfilioc_list)
+#define PFILIOC_LISTHOOKS _IOWR('P', 2, struct pfilioc_list)
+#define PFILIOC_LINK _IOW('P', 3, struct pfilioc_link)
+
+#define PFIL_IN 0x00010000
+#define PFIL_OUT 0x00020000
+#define PFIL_FWD 0x00040000
+#define PFIL_DIR(f) ((f) & (PFIL_IN|PFIL_OUT))
+#define PFIL_MEMPTR 0x00080000
+#define PFIL_HEADPTR 0x00100000
+#define PFIL_HOOKPTR 0x00200000
+#define PFIL_APPEND 0x00400000
+#define PFIL_UNLINK 0x00800000
+#define PFIL_LENMASK 0x0000ffff
+#define PFIL_LENGTH(f) ((f) & PFIL_LENMASK)
+
+#ifdef _KERNEL
struct mbuf;
struct ifnet;
struct inpcb;
-typedef int (*pfil_func_t)(void *, struct mbuf **, struct ifnet *, int,
- struct inpcb *);
-typedef int (*pfil_func_flags_t)(void *, struct mbuf **, struct ifnet *,
- int, int, struct inpcb *);
+typedef union {
+ struct mbuf **m;
+ void *mem;
+} pfil_packet_t __attribute__((__transparent_union__));
+typedef enum {
+ PFIL_PASS = 0,
+ PFIL_DROPPED,
+ PFIL_CONSUMED,
+ PFIL_REALLOCED,
+} pfil_return_t;
+
+typedef pfil_return_t (*pfil_func_t)(pfil_packet_t, struct ifnet *, int,
+ void *, struct inpcb *);
/*
- * The packet filter hooks are designed for anything to call them to
- * possibly intercept the packet. Multiple filter hooks are chained
- * together and after each other in the specified order.
+ * A pfil head is created by a packet intercept point.
+ *
+ * A pfil hook is created by a packet filter.
+ *
+ * Hooks are chained on heads. Historically some hooking happens
+ * automatically, e.g. ipfw(4), pf(4) and ipfilter(4) would register
+ * theirselves on IPv4 and IPv6 input/output.
*/
-struct packet_filter_hook {
- TAILQ_ENTRY(packet_filter_hook) pfil_chain;
- pfil_func_t pfil_func;
- pfil_func_flags_t pfil_func_flags;
- void *pfil_arg;
+
+typedef struct pfil_hook * pfil_hook_t;
+typedef struct pfil_head * pfil_head_t;
+
+/*
+ * Give us a chance to modify pfil_xxx_args structures in future.
+ */
+#define PFIL_VERSION 1
+
+/* Argument structure used by packet filters to register themselves. */
+struct pfil_hook_args {
+ int pa_version;
+ int pa_flags;
+ enum pfil_types pa_type;
+ pfil_func_t pa_func;
+ void *pa_ruleset;
+ const char *pa_modname;
+ const char *pa_rulname;
};
-#define PFIL_IN 0x00000001
-#define PFIL_OUT 0x00000002
-#define PFIL_WAITOK 0x00000004
-#define PFIL_FWD 0x00000008
-#define PFIL_ALL (PFIL_IN|PFIL_OUT)
+/* Public functions for pfil hook management by packet filters. */
+pfil_hook_t pfil_add_hook(struct pfil_hook_args *);
+void pfil_remove_hook(pfil_hook_t);
-typedef TAILQ_HEAD(pfil_chain, packet_filter_hook) pfil_chain_t;
+/* Argument structure used by ioctl() and packet filters to set filters. */
+struct pfil_link_args {
+ int pa_version;
+ int pa_flags;
+ union {
+ const char *pa_headname;
+ pfil_head_t pa_head;
+ };
+ union {
+ struct {
+ const char *pa_modname;
+ const char *pa_rulname;
+ };
+ pfil_hook_t pa_hook;
+ };
+};
+
+/* Public function to configure filter chains. Used by ioctl() and filters. */
+int pfil_link(struct pfil_link_args *);
-#define PFIL_TYPE_AF 1 /* key is AF_* type */
-#define PFIL_TYPE_IFNET 2 /* key is ifnet pointer */
+/* Argument structure used by inspection points to register themselves. */
+struct pfil_head_args {
+ int pa_version;
+ int pa_flags;
+ enum pfil_types pa_type;
+ const char *pa_headname;
+};
-#define PFIL_FLAG_PRIVATE_LOCK 0x01 /* Personal lock instead of global */
+/* Public functions for pfil head management by inspection points. */
+pfil_head_t pfil_head_register(struct pfil_head_args *);
+void pfil_head_unregister(pfil_head_t);
+/* Public functions to run the packet inspection by inspection points. */
+int pfil_run_hooks(struct pfil_head *, pfil_packet_t, struct ifnet *, int,
+ struct inpcb *inp);
/*
- * A pfil head is created by each protocol or packet intercept point.
- * For packet is then run through the hook chain for inspection.
+ * Minimally exposed structure to avoid function call in case of absence
+ * of any filters by protocols and macros to do the check.
*/
-struct pfil_head {
- pfil_chain_t ph_in;
- pfil_chain_t ph_out;
- int ph_type;
- int ph_nhooks;
-#if defined( __linux__ ) || defined( _WIN32 )
- rwlock_t ph_mtx;
-#else
- struct rmlock *ph_plock; /* Pointer to the used lock */
- struct rmlock ph_lock; /* Private lock storage */
- int flags;
-#endif
- union {
- u_long phu_val;
- void *phu_ptr;
- } ph_un;
-#define ph_af ph_un.phu_val
-#define ph_ifnet ph_un.phu_ptr
- LIST_ENTRY(pfil_head) ph_list;
+struct _pfil_head {
+ int head_nhooksin;
+ int head_nhooksout;
};
+#define PFIL_HOOKED_IN(p) (((struct _pfil_head *)(p))->head_nhooksin > 0)
+#define PFIL_HOOKED_OUT(p) (((struct _pfil_head *)(p))->head_nhooksout > 0)
-/* Public functions for pfil hook management by packet filters. */
-struct pfil_head *pfil_head_get(int, u_long);
-int pfil_add_hook_flags(pfil_func_flags_t, void *, int, struct pfil_head *);
-int pfil_add_hook(pfil_func_t, void *, int, struct pfil_head *);
-int pfil_remove_hook_flags(pfil_func_flags_t, void *, int, struct pfil_head *);
-int pfil_remove_hook(pfil_func_t, void *, int, struct pfil_head *);
-#define PFIL_HOOKED(p) ((p)->ph_nhooks > 0)
-
-/* Public functions to run the packet inspection by protocols. */
-int pfil_run_hooks(struct pfil_head *, struct mbuf **, struct ifnet *, int,
- int, struct inpcb *inp);
-
-/* Public functions for pfil head management by protocols. */
-int pfil_head_register(struct pfil_head *);
-int pfil_head_unregister(struct pfil_head *);
-
-/* Public pfil locking functions for self managed locks by packet filters. */
-int pfil_try_rlock(struct pfil_head *, struct rm_priotracker *);
-void pfil_rlock(struct pfil_head *, struct rm_priotracker *);
-void pfil_runlock(struct pfil_head *, struct rm_priotracker *);
-void pfil_wlock(struct pfil_head *);
-void pfil_wunlock(struct pfil_head *);
-int pfil_wowned(struct pfil_head *ph);
-
+#endif /* _KERNEL */
#endif /* _NET_PFIL_H_ */
diff --git a/sys/netinet/ip_fastfwd.c b/sys/netinet/ip_fastfwd.c
index 643a75e2294b..77a08b1a8af1 100644
--- a/sys/netinet/ip_fastfwd.c
+++ b/sys/netinet/ip_fastfwd.c
@@ -90,11 +90,11 @@ __FBSDID("$FreeBSD$");
#include <sys/socket.h>
#include <sys/sysctl.h>
-#include <net/pfil.h>
#include <net/if.h>
#include <net/if_types.h>
#include <net/if_var.h>
#include <net/if_dl.h>
+#include <net/pfil.h>
#include <net/route.h>
#include <net/vnet.h>
@@ -228,12 +228,11 @@ ip_tryforward(struct mbuf *m)
/*
* Run through list of ipfilter hooks for input packets
*/
- if (!PFIL_HOOKED(&V_inet_pfil_hook))
+ if (!PFIL_HOOKED_IN(V_inet_pfil_head))
goto passin;
- if (pfil_run_hooks(
- &V_inet_pfil_hook, &m, m->m_pkthdr.rcvif, PFIL_IN, 0, NULL) ||
- m == NULL)
+ if (pfil_run_hooks(V_inet_pfil_head, &m, m->m_pkthdr.rcvif, PFIL_IN,
+ NULL) != PFIL_PASS)
goto drop;
M_ASSERTVALID(m);
@@ -321,13 +320,12 @@ passin:
/*
* Step 5: outgoing firewall packet processing
*/
- if (!PFIL_HOOKED(&V_inet_pfil_hook))
+ if (!PFIL_HOOKED_OUT(V_inet_pfil_head))
goto passout;
- if (pfil_run_hooks(&V_inet_pfil_hook, &m, nh.nh_ifp, PFIL_OUT, PFIL_FWD,
- NULL) || m == NULL) {
+ if (pfil_run_hooks(V_inet_pfil_head, &m, nh.nh_ifp,
+ PFIL_OUT | PFIL_FWD, NULL) != PFIL_PASS)
goto drop;
- }
M_ASSERTVALID(m);
M_ASSERTPKTHDR(m);
diff --git a/sys/netinet/ip_input.c b/sys/netinet/ip_input.c
index dd00d13a4d71..a1ec5935a826 100644
--- a/sys/netinet/ip_input.c
+++ b/sys/netinet/ip_input.c
@@ -57,11 +57,11 @@ __FBSDID("$FreeBSD$");
#include <sys/syslog.h>
#include <sys/sysctl.h>
-#include <net/pfil.h>
#include <net/if.h>
#include <net/if_types.h>
#include <net/if_var.h>
#include <net/if_dl.h>
+#include <net/pfil.h>
#include <net/route.h>
#include <net/netisr.h>
#include <net/rss_config.h>
@@ -134,7 +134,7 @@ SYSCTL_INT(_net_inet_ip, OID_AUTO, check_interface, CTLFLAG_VNET | CTLFLAG_RW,
&VNET_NAME(ip_checkinterface), 0,
"Verify packet arrives on correct interface");
-VNET_DEFINE(struct pfil_head, inet_pfil_hook); /* Packet filter hooks */
+VNET_DEFINE(pfil_head_t, inet_pfil_head); /* Packet filter hooks */
static struct netisr_handler ip_nh = {
.nh_name = "ip",
@@ -301,6 +301,7 @@ SYSCTL_PROC(_net_inet_ip, IPCTL_INTRDQDROPS, intr_direct_queue_drops,
void
ip_init(void)
{
+ struct pfil_head_args args;
struct protosw *pr;
int i;
@@ -311,11 +312,11 @@ ip_init(void)
ipreass_init();
/* Initialize packet filter hooks. */
- V_inet_pfil_hook.ph_type = PFIL_TYPE_AF;
- V_inet_pfil_hook.ph_af = AF_INET;
- if ((i = pfil_head_register(&V_inet_pfil_hook)) != 0)
- printf("%s: WARNING: unable to register pfil hook, "
- "error %d\n", __func__, i);
+ args.pa_version = PFIL_VERSION;
+ args.pa_flags = PFIL_IN | PFIL_OUT;
+ args.pa_type = PFIL_TYPE_IP4;
+ args.pa_headname = PFIL_INET_NAME;
+ V_inet_pfil_head = pfil_head_register(&args);
if (hhook_head_register(HHOOK_TYPE_IPSEC_IN, AF_INET,
&V_ipsec_hhh_in[HHOOK_IPSEC_INET],
@@ -377,10 +378,7 @@ ip_destroy(void *unused __unused)
#endif
netisr_unregister_vnet(&ip_nh);
- if ((error = pfil_head_unregister(&V_inet_pfil_hook)) != 0)
- printf("%s: WARNING: unable to unregister pfil hook, "
- "error %d\n", __func__, error);
-
+ pfil_head_unregister(V_inet_pfil_head);
error = hhook_head_deregister(V_ipsec_hhh_in[HHOOK_IPSEC_INET]);
if (error != 0) {
printf("%s: WARNING: unable to deregister input helper hook "
@@ -599,11 +597,12 @@ tooshort:
*/
/* Jump over all PFIL processing if hooks are not active. */
- if (!PFIL_HOOKED(&V_inet_pfil_hook))
+ if (!PFIL_HOOKED_IN(V_inet_pfil_head))
goto passin;
odst = ip->ip_dst;
- if (pfil_run_hooks(&V_inet_pfil_hook, &m, ifp, PFIL_IN, 0, NULL) != 0)
+ if (pfil_run_hooks(V_inet_pfil_head, &m, ifp, PFIL_IN, NULL) !=
+ PFIL_PASS)
return;
if (m == NULL) /* consumed by filter */
return;
diff --git a/sys/netinet/ip_output.c b/sys/netinet/ip_output.c
index 7595c3f90535..0c7a26503d07 100644
--- a/sys/netinet/ip_output.c
+++ b/sys/netinet/ip_output.c
@@ -121,11 +121,16 @@ ip_output_pfil(struct mbuf **mp, struct ifnet *ifp, struct inpcb *inp,
/* Run through list of hooks for output packets. */
odst.s_addr = ip->ip_dst.s_addr;
- *error = pfil_run_hooks(&V_inet_pfil_hook, mp, ifp, PFIL_OUT, 0, inp);
- m = *mp;
- if ((*error) != 0 || m == NULL)
+ switch (pfil_run_hooks(V_inet_pfil_head, mp, ifp, PFIL_OUT, inp)) {
+ case PFIL_DROPPED:
+ *error = EPERM;
+ /* FALLTHROUGH */
+ case PFIL_CONSUMED:
return 1; /* Finished */
-
+ case PFIL_PASS:
+ *error = 0;
+ }
+ m = *mp;
ip = mtod(m, struct ip *);
/* See if destination IP address was changed by packet filter. */
@@ -568,7 +573,7 @@ sendit:
#endif /* IPSEC */
/* Jump over all PFIL processing if hooks are not active. */
- if (PFIL_HOOKED(&V_inet_pfil_hook)) {
+ if (PFIL_HOOKED_OUT(V_inet_pfil_head)) {
switch (ip_output_pfil(&m, ifp, inp, dst, &fibnum, &error)) {
case 1: /* Finished */
goto done;
diff --git a/sys/netinet/ip_var.h b/sys/netinet/ip_var.h
index 86615a15ad26..d55a18bba91d 100644
--- a/sys/netinet/ip_var.h
+++ b/sys/netinet/ip_var.h
@@ -241,8 +241,9 @@ extern int (*ip_rsvp_vif)(struct socket *, struct sockopt *);
extern void (*ip_rsvp_force_done)(struct socket *);
extern int (*rsvp_input_p)(struct mbuf **, int *, int);
-VNET_DECLARE(struct pfil_head, inet_pfil_hook); /* packet filter hooks */
-#define V_inet_pfil_hook VNET(inet_pfil_hook)
+VNET_DECLARE(struct pfil_head *, inet_pfil_head);
+#define V_inet_pfil_head VNET(inet_pfil_head)
+#define PFIL_INET_NAME "inet"
void in_delayed_cksum(struct mbuf *m);
diff --git a/sys/netinet/siftr.c b/sys/netinet/siftr.c
index 4d063c360386..68d8f8e16fa1 100644
--- a/sys/netinet/siftr.c
+++ b/sys/netinet/siftr.c
@@ -94,10 +94,12 @@ __FBSDID("$FreeBSD$");
#include <netinet/in_systm.h>
#include <netinet/in_var.h>
#include <netinet/ip.h>
+#include <netinet/ip_var.h>
#include <netinet/tcp_var.h>
#ifdef SIFTR_IPV6
#include <netinet/ip6.h>
+#include <netinet/ip6_var.h>
#include <netinet6/in6_pcb.h>
#endif /* SIFTR_IPV6 */
@@ -831,9 +833,9 @@ siftr_siftdata(struct pkt_node *pn, struct inpcb *inp, struct tcpcb *tp,
* It's very important to use the M_NOWAIT flag with all function calls
* that support it so that they won't sleep, otherwise you get a panic.
*/
-static int
-siftr_chkpkt(void *arg, struct mbuf **m, struct ifnet *ifp, int dir,
- struct inpcb *inp)
+static pfil_return_t
+siftr_chkpkt(struct mbuf **m, struct ifnet *ifp, int flags,
+ void *ruleset __unused, struct inpcb *inp)
{
struct pkt_node *pn;
struct ip *ip;
@@ -841,9 +843,10 @@ siftr_chkpkt(void *arg, struct mbuf **m, struct ifnet *ifp, int dir,
struct tcpcb *tp;
struct siftr_stats *ss;
unsigned int ip_hl;
- int inp_locally_locked;
+ int inp_locally_locked, dir;
inp_locally_locked = 0;
+ dir = PFIL_DIR(flags);
ss = DPCPU_PTR(ss);
/*
@@ -1007,15 +1010,13 @@ inp_unlock:
INP_RUNLOCK(inp);
ret:
- /* Returning 0 ensures pfil will not discard the pkt */
- return (0);
+ return (PFIL_PASS);
}
#ifdef SIFTR_IPV6
static int
-siftr_chkpkt6(void *arg, struct mbuf **m, struct ifnet *ifp, int dir,
- struct inpcb *inp)
+siftr_chkpkt6(struct mbuf **m, struct ifnet *ifp, int flags, struct inpcb *inp)
{
struct pkt_node *pn;
struct ip6_hdr *ip6;
@@ -1023,9 +1024,10 @@ siftr_chkpkt6(void *arg, struct mbuf **m, struct ifnet *ifp, int dir,
struct tcpcb *tp;
struct siftr_stats *ss;
unsigned int ip6_hl;
- int inp_locally_locked;
+ int inp_locally_locked, dir;
inp_locally_locked = 0;
+ dir = PFIL_DIR(flags);
ss = DPCPU_PTR(ss);
/*
@@ -1138,37 +1140,53 @@ ret6:
}
#endif /* #ifdef SIFTR_IPV6 */
-
+VNET_DEFINE_STATIC(pfil_hook_t, siftr_inet_hook);
+#define V_siftr_inet_hook VNET(siftr_inet_hook)
+#ifdef INET6
+VNET_DEFINE_STATIC(pfil_hook_t, siftr_inet6_hook);
+#define V_siftr_inet6_hook VNET(siftr_inet6_hook)
+#endif
static int
siftr_pfil(int action)
{
- struct pfil_head *pfh_inet;
-#ifdef SIFTR_IPV6
- struct pfil_head *pfh_inet6;
-#endif
+ struct pfil_hook_args pha;
+ struct pfil_link_args pla;
+
+ pha.pa_version = PFIL_VERSION;
+ pha.pa_flags = PFIL_IN | PFIL_OUT;
+ pha.pa_modname = "siftr";
+ pha.pa_ruleset = NULL;
+ pha.pa_rulname = "default";
+
+ pla.pa_version = PFIL_VERSION;
+ pla.pa_flags = PFIL_IN | PFIL_OUT |
+ PFIL_HEADPTR | PFIL_HOOKPTR;
+
VNET_ITERATOR_DECL(vnet_iter);
VNET_LIST_RLOCK();
VNET_FOREACH(vnet_iter) {
CURVNET_SET(vnet_iter);
- pfh_inet = pfil_head_get(PFIL_TYPE_AF, AF_INET);
-#ifdef SIFTR_IPV6
- pfh_inet6 = pfil_head_get(PFIL_TYPE_AF, AF_INET6);
-#endif
if (action == HOOK) {
- pfil_add_hook(siftr_chkpkt, NULL,
- PFIL_IN | PFIL_OUT | PFIL_WAITOK, pfh_inet);
+ pha.pa_func = siftr_chkpkt;
+ pha.pa_type = PFIL_TYPE_IP4;
+ V_siftr_inet_hook = pfil_add_hook(&pha);
+ pla.pa_hook = V_siftr_inet_hook;
+ pla.pa_head = V_inet_pfil_head;
+ (void)pfil_link(&pla);
#ifdef SIFTR_IPV6
- pfil_add_hook(siftr_chkpkt6, NULL,
- PFIL_IN | PFIL_OUT | PFIL_WAITOK, pfh_inet6);
+ pha.pa_func = siftr_chkpkt6;
+ pha.pa_type = PFIL_TYPE_IP6;
+ V_siftr_inet6_hook = pfil_add_hook(&pha);
+ pla.pa_hook = V_siftr_inet6_hook;
+ pla.pa_head = V_inet6_pfil_head;
+ (void)pfil_link(&pla);
#endif
} else if (action == UNHOOK) {
- pfil_remove_hook(siftr_chkpkt, NULL,
- PFIL_IN | PFIL_OUT | PFIL_WAITOK, pfh_inet);
+ pfil_remove_hook(V_siftr_inet_hook);
#ifdef SIFTR_IPV6
- pfil_remove_hook(siftr_chkpkt6, NULL,
- PFIL_IN | PFIL_OUT | PFIL_WAITOK, pfh_inet6);
+ pfil_remove_hook(V_siftr_inet6_hook);
#endif
}
CURVNET_RESTORE();
diff --git a/sys/netinet6/ip6_fastfwd.c b/sys/netinet6/ip6_fastfwd.c
index 8f8b176607c5..11eb0e7548d5 100644
--- a/sys/netinet6/ip6_fastfwd.c
+++ b/sys/netinet6/ip6_fastfwd.c
@@ -156,10 +156,10 @@ ip6_tryforward(struct mbuf *m)
/*
* Incoming packet firewall processing.
*/
- if (!PFIL_HOOKED(&V_inet6_pfil_hook))
+ if (!PFIL_HOOKED_IN(V_inet6_pfil_head))
goto passin;
- if (pfil_run_hooks(&V_inet6_pfil_hook, &m, rcvif, PFIL_IN, 0,
- NULL) != 0 || m == NULL)
+ if (pfil_run_hooks(V_inet6_pfil_head, &m, rcvif, PFIL_IN, NULL) !=
+ PFIL_PASS)
goto dropin;
/*
* If packet filter sets the M_FASTFWD_OURS flag, this means
@@ -195,7 +195,7 @@ passin:
in6_ifstat_inc(rcvif, ifs6_in_noroute);
goto dropin;
}
- if (!PFIL_HOOKED(&V_inet6_pfil_hook)) {
+ if (!PFIL_HOOKED_OUT(V_inet6_pfil_head)) {
if (m->m_pkthdr.len > nh.nh_mtu) {
in6_ifstat_inc(nh.nh_ifp, ifs6_in_toobig);
icmp6_error(m, ICMP6_PACKET_TOO_BIG, 0, nh.nh_mtu);
@@ -208,8 +208,8 @@ passin:
/*
* Outgoing packet firewall processing.
*/
- if (pfil_run_hooks(&V_inet6_pfil_hook, &m, nh.nh_ifp, PFIL_OUT,
- PFIL_FWD, NULL) != 0 || m == NULL)
+ if (pfil_run_hooks(V_inet6_pfil_head, &m, nh.nh_ifp, PFIL_OUT |
+ PFIL_FWD, NULL) != PFIL_PASS)
goto dropout;
/*
diff --git a/sys/netinet6/ip6_forward.c b/sys/netinet6/ip6_forward.c
index ed743f65c867..0676a58225a3 100644
--- a/sys/netinet6/ip6_forward.c
+++ b/sys/netinet6/ip6_forward.c
@@ -320,15 +320,14 @@ again2:
in6_clearscope(&ip6->ip6_dst);
/* Jump over all PFIL processing if hooks are not active. */
- if (!PFIL_HOOKED(&V_inet6_pfil_hook))
+ if (!PFIL_HOOKED_OUT(V_inet6_pfil_head))
goto pass;
odst = ip6->ip6_dst;
/* Run through list of hooks for forwarded packets. */
- error = pfil_run_hooks(&V_inet6_pfil_hook, &m, rt->rt_ifp, PFIL_OUT,
- PFIL_FWD, NULL);
- if (error != 0 || m == NULL)
- goto freecopy; /* consumed by filter */
+ if (pfil_run_hooks(V_inet6_pfil_head, &m, rt->rt_ifp, PFIL_OUT |
+ PFIL_FWD, NULL) != PFIL_PASS)
+ goto freecopy;
ip6 = mtod(m, struct ip6_hdr *);
/* See if destination IP address was changed by packet filter. */
diff --git a/sys/netinet6/ip6_input.c b/sys/netinet6/ip6_input.c
index 712b9923d8e7..531cfff43f0e 100644
--- a/sys/netinet6/ip6_input.c
+++ b/sys/netinet6/ip6_input.c
@@ -191,7 +191,7 @@ SYSCTL_PROC(_net_inet6_ip6, IPV6CTL_INTRDQMAXLEN, intr_direct_queue_maxlen,
#endif
-VNET_DEFINE(struct pfil_head, inet6_pfil_hook);
+VNET_DEFINE(pfil_head_t, inet6_pfil_head);
VNET_PCPUSTAT_DEFINE(struct ip6stat, ip6stat);
VNET_PCPUSTAT_SYSINIT(ip6stat);
@@ -214,6 +214,7 @@ static struct mbuf *ip6_pullexthdr(struct mbuf *, size_t, int);
void
ip6_init(void)
{
+ struct pfil_head_args args;
struct protosw *pr;
int i;
@@ -227,11 +228,11 @@ ip6_init(void)
&V_in6_ifaddrhmask);
/* Initialize packet filter hooks. */
- V_inet6_pfil_hook.ph_type = PFIL_TYPE_AF;
- V_inet6_pfil_hook.ph_af = AF_INET6;
- if ((i = pfil_head_register(&V_inet6_pfil_hook)) != 0)
- printf("%s: WARNING: unable to register pfil hook, "
- "error %d\n", __func__, i);
+ args.pa_version = PFIL_VERSION;
+ args.pa_flags = PFIL_IN | PFIL_OUT;
+ args.pa_type = PFIL_TYPE_IP6;
+ args.pa_headname = PFIL_INET6_NAME;
+ V_inet6_pfil_head = pfil_head_register(&args);
if (hhook_head_register(HHOOK_TYPE_IPSEC_IN, AF_INET6,
&V_ipsec_hhh_in[HHOOK_IPSEC_INET6],
@@ -359,9 +360,7 @@ ip6_destroy(void *unused __unused)
#endif
netisr_unregister_vnet(&ip6_nh);
- if ((error = pfil_head_unregister(&V_inet6_pfil_hook)) != 0)
- printf("%s: WARNING: unable to unregister pfil hook, "
- "error %d\n", __func__, error);
+ pfil_head_unregister(V_inet6_pfil_head);
error = hhook_head_deregister(V_ipsec_hhh_in[HHOOK_IPSEC_INET6]);
if (error != 0) {
printf("%s: WARNING: unable to deregister input helper hook "
@@ -758,14 +757,12 @@ ip6_input(struct mbuf *m)
*/
/* Jump over all PFIL processing if hooks are not active. */
- if (!PFIL_HOOKED(&V_inet6_pfil_hook))
+ if (!PFIL_HOOKED_IN(V_inet6_pfil_head))
goto passin;
odst = ip6->ip6_dst;
- if (pfil_run_hooks(&V_inet6_pfil_hook, &m,
- m->m_pkthdr.rcvif, PFIL_IN, 0, NULL))
- return;
- if (m == NULL) /* consumed by filter */
+ if (pfil_run_hooks(V_inet6_pfil_head, &m, m->m_pkthdr.rcvif, PFIL_IN,
+ NULL) != PFIL_PASS)
return;
ip6 = mtod(m, struct ip6_hdr *);
srcrt = !IN6_ARE_ADDR_EQUAL(&odst, &ip6->ip6_dst);
diff --git a/sys/netinet6/ip6_output.c b/sys/netinet6/ip6_output.c
index 741521abb8a1..e36beb355b38 100644
--- a/sys/netinet6/ip6_output.c
+++ b/sys/netinet6/ip6_output.c
@@ -792,16 +792,21 @@ again:
}
/* Jump over all PFIL processing if hooks are not active. */
- if (!PFIL_HOOKED(&V_inet6_pfil_hook))
+ if (!PFIL_HOOKED_OUT(V_inet6_pfil_head))
goto passout;
odst = ip6->ip6_dst;
/* Run through list of hooks for output packets. */
- error = pfil_run_hooks(&V_inet6_pfil_hook, &m, ifp, PFIL_OUT, 0, inp);
- if (error != 0 || m == NULL)
+ switch (pfil_run_hooks(V_inet6_pfil_head, &m, ifp, PFIL_OUT, inp)) {
+ case PFIL_PASS:
+ ip6 = mtod(m, struct ip6_hdr *);
+ break;
+ case PFIL_DROPPED:
+ error = EPERM;
+ /* FALLTHROUGH */
+ case PFIL_CONSUMED:
goto done;
- /* adjust pointer */
- ip6 = mtod(m, struct ip6_hdr *);
+ }
needfiblookup = 0;
/* See if destination IP address was changed by packet filter. */
diff --git a/sys/netinet6/ip6_var.h b/sys/netinet6/ip6_var.h
index f235572dd03e..bf15f833b326 100644
--- a/sys/netinet6/ip6_var.h
+++ b/sys/netinet6/ip6_var.h
@@ -346,8 +346,10 @@ VNET_DECLARE(int, ip6_use_defzone); /* Whether to use the default scope
* zone when unspecified */
#define V_ip6_use_defzone VNET(ip6_use_defzone)
-VNET_DECLARE (struct pfil_head, inet6_pfil_hook); /* packet filter hooks */
-#define V_inet6_pfil_hook VNET(inet6_pfil_hook)
+VNET_DECLARE(struct pfil_head *, inet6_pfil_head);
+#define V_inet6_pfil_head VNET(inet6_pfil_head)
+#define PFIL_INET6_NAME "inet6"
+
#ifdef IPSTEALTH
VNET_DECLARE(int, ip6stealth);
#define V_ip6stealth VNET(ip6stealth)
diff --git a/sys/netpfil/ipfw/ip_fw_eaction.c b/sys/netpfil/ipfw/ip_fw_eaction.c
index 05cc174cb283..1cb2f812936c 100644
--- a/sys/netpfil/ipfw/ip_fw_eaction.c
+++ b/sys/netpfil/ipfw/ip_fw_eaction.c
@@ -38,9 +38,9 @@ __FBSDID("$FreeBSD$");
#include <sys/socket.h>
#include <sys/socketvar.h>
#include <sys/queue.h>
-#include <net/pfil.h>
#include <net/if.h> /* ip_fw.h requires IFNAMSIZ */
+#include <net/pfil.h>
#include <netinet/in.h>
#include <netinet/ip_var.h> /* struct ipfw_rule_ref */
#include <netinet/ip_fw.h>
diff --git a/sys/netpfil/ipfw/ip_fw_pfil.c b/sys/netpfil/ipfw/ip_fw_pfil.c
index feb4a20f9b69..5a3cd052bb2d 100644
--- a/sys/netpfil/ipfw/ip_fw_pfil.c
+++ b/sys/netpfil/ipfw/ip_fw_pfil.c
@@ -48,6 +48,7 @@ __FBSDID("$FreeBSD$");
#include <sys/sysctl.h>
#include <net/if.h>
+#include <net/if_var.h>
#include <net/route.h>
#include <net/ethernet.h>
#include <net/pfil.h>
@@ -85,10 +86,6 @@ int ipfw_chg_hook(SYSCTL_HANDLER_ARGS);
/* Forward declarations. */
static int ipfw_divert(struct mbuf **, int, struct ipfw_rule_ref *, int);
-int ipfw_check_packet(void *, struct mbuf **, struct ifnet *, int,
- struct inpcb *);
-int ipfw_check_frame(void *, struct mbuf **, struct ifnet *, int,
- struct inpcb *);
#ifdef SYSCTL_NODE
@@ -120,16 +117,17 @@ SYSEND
* dummynet, divert, netgraph or other modules.
* The packet may be consumed.
*/
-int
-ipfw_check_packet(void *arg, struct mbuf **m0, struct ifnet *ifp, int dir,
- struct inpcb *inp)
+static pfil_return_t
+ipfw_check_packet(struct mbuf **m0, struct ifnet *ifp, int dir,
+ void *ruleset __unused, struct inpcb *inp)
{
struct ip_fw_args args;
struct m_tag *tag;
- int ipfw, ret;
+ pfil_return_t ret;
+ int ipfw;
/* convert dir to IPFW values */
- dir = (dir == PFIL_IN) ? DIR_IN : DIR_OUT;
+ dir = (dir & PFIL_IN) ? DIR_IN : DIR_OUT;
args.flags = 0;
again:
/*
@@ -155,17 +153,15 @@ again:
KASSERT(*m0 != NULL || ipfw == IP_FW_DENY, ("%s: m0 is NULL",
__func__));
- /* breaking out of the switch means drop */
+ ret = PFIL_PASS;
switch (ipfw) {
case IP_FW_PASS:
/* next_hop may be set by ipfw_chk */
if ((args.flags & (IPFW_ARGS_NH4 | IPFW_ARGS_NH4PTR |
- IPFW_ARGS_NH6 | IPFW_ARGS_NH6PTR)) == 0) {
- ret = 0;
+ IPFW_ARGS_NH6 | IPFW_ARGS_NH6PTR)) == 0)
break;
- }
#if (!defined(INET6) && !defined(INET))
- ret = EACCES;
+ ret = PFIL_DROPPED;
#else
{
void *psa;
@@ -210,8 +206,8 @@ again:
tag = m_tag_get(PACKET_TAG_IPFORWARD, len,
M_NOWAIT);
if (tag == NULL) {
- ret = EACCES;
- break; /* i.e. drop */
+ ret = PFIL_DROPPED;
+ break;
}
}
if ((args.flags & IPFW_ARGS_NH6) == 0)
@@ -238,7 +234,7 @@ again:
* comparisons.
*/
if (sa6_embedscope(sa6, V_ip6_use_defzone) != 0) {
- ret = EACCES;
+ ret = PFIL_DROPPED;
break;
}
if (in6_localip(&sa6->sin6_addr))
@@ -250,20 +246,23 @@ again:
break;
case IP_FW_DENY:
- ret = EACCES;
- break; /* i.e. drop */
+ ret = PFIL_DROPPED;
+ break;
case IP_FW_DUMMYNET:
- ret = EACCES;
- if (ip_dn_io_ptr == NULL)
- break; /* i.e. drop */
+ if (ip_dn_io_ptr == NULL) {
+ ret = PFIL_DROPPED;
+ break;
+ }
MPASS(args.flags & IPFW_ARGS_REF);
if (mtod(*m0, struct ip *)->ip_v == 4)
- ret = ip_dn_io_ptr(m0, dir, &args);
+ (void )ip_dn_io_ptr(m0, dir, &args);
else if (mtod(*m0, struct ip *)->ip_v == 6)
- ret = ip_dn_io_ptr(m0, dir | PROTO_IPV6, &args);
- else
- break; /* drop it */
+ (void )ip_dn_io_ptr(m0, dir | PROTO_IPV6, &args);
+ else {
+ ret = PFIL_DROPPED;
+ break;
+ }
/*
* XXX should read the return value.
* dummynet normally eats the packet and sets *m0=NULL
@@ -273,41 +272,42 @@ again:
*/
if (*m0 != NULL)
goto again;
+ ret = PFIL_CONSUMED;
break;
case IP_FW_TEE:
case IP_FW_DIVERT:
if (ip_divert_ptr == NULL) {
- ret = EACCES;
- break; /* i.e. drop */
+ ret = PFIL_DROPPED;
+ break;
}
MPASS(args.flags & IPFW_ARGS_REF);
- ret = ipfw_divert(m0, dir, &args.rule,
+ (void )ipfw_divert(m0, dir, &args.rule,
(ipfw == IP_FW_TEE) ? 1 : 0);
/* continue processing for the original packet (tee). */
if (*m0)
goto again;
+ ret = PFIL_CONSUMED;
break;
case IP_FW_NGTEE:
case IP_FW_NETGRAPH:
if (ng_ipfw_input_p == NULL) {
- ret = EACCES;
- break; /* i.e. drop */
+ ret = PFIL_DROPPED;
+ break;
}
MPASS(args.flags & IPFW_ARGS_REF);
- ret = ng_ipfw_input_p(m0, dir, &args,
+ (void )ng_ipfw_input_p(m0, dir, &args,
(ipfw == IP_FW_NGTEE) ? 1 : 0);
if (ipfw == IP_FW_NGTEE) /* ignore errors for NGTEE */
goto again; /* continue with packet */
+ ret = PFIL_CONSUMED;
break;
case IP_FW_NAT:
/* honor one-pass in case of successful nat */
- if (V_fw_one_pass) {
- ret = 0;
+ if (V_fw_one_pass)
break;
- }
goto again;
case IP_FW_REASS:
@@ -317,7 +317,7 @@ again:
KASSERT(0, ("%s: unknown retval", __func__));
}
- if (ret != 0) {
+ if (ret != PFIL_PASS) {
if (*m0)
FREE_PKT(*m0);
*m0 = NULL;
@@ -329,16 +329,17 @@ again:
/*
* ipfw processing for ethernet packets (in and out).
*/
-int
-ipfw_check_frame(void *arg, struct mbuf **m0, struct ifnet *ifp, int dir,
- struct inpcb *inp)
+static pfil_return_t
+ipfw_check_frame(struct mbuf **m0, struct ifnet *ifp, int dir,
+ void *ruleset __unused, struct inpcb *inp)
{
struct ip_fw_args args;
struct ether_header save_eh;
struct ether_header *eh;
struct m_tag *mtag;
struct mbuf *m;
- int i, ret;
+ pfil_return_t ret;
+ int i;
args.flags = IPFW_ARGS_ETHER;
again:
@@ -367,7 +368,7 @@ again:
m_adj(m, ETHER_HDR_LEN); /* strip ethernet header */
args.m = m; /* the packet we are looking at */
- args.oif = dir == PFIL_OUT ? ifp: NULL; /* destination, if any */
+ args.oif = dir & PFIL_OUT ? ifp: NULL; /* destination, if any */
args.eh = &save_eh; /* MAC header for bridged/MAC packets */
args.inp = inp; /* used by ipfw uid/gid/jail rules */
i = ipfw_chk(&args);
@@ -388,46 +389,46 @@ again:
}
*m0 = m;
- ret = 0;
+ ret = PFIL_PASS;
/* Check result of ipfw_chk() */
switch (i) {
case IP_FW_PASS:
break;
case IP_FW_DENY:
- ret = EACCES;
- break; /* i.e. drop */
+ ret = PFIL_DROPPED;
+ break;
case IP_FW_DUMMYNET:
- ret = EACCES;
-
- if (ip_dn_io_ptr == NULL)
- break; /* i.e. drop */
-
+ if (ip_dn_io_ptr == NULL) {
+ ret = PFIL_DROPPED;
+ break;
+ }
*m0 = NULL;
- dir = (dir == PFIL_IN) ? DIR_IN : DIR_OUT;
+ dir = (dir & PFIL_IN) ? DIR_IN : DIR_OUT;
MPASS(args.flags & IPFW_ARGS_REF);
ip_dn_io_ptr(&m, dir | PROTO_LAYER2, &args);
- return 0;
+ return (PFIL_CONSUMED);
case IP_FW_NGTEE:
case IP_FW_NETGRAPH:
if (ng_ipfw_input_p == NULL) {
- ret = EACCES;
- break; /* i.e. drop */
+ ret = PFIL_DROPPED;
+ break;
}
MPASS(args.flags & IPFW_ARGS_REF);
- ret = ng_ipfw_input_p(m0, (dir == PFIL_IN) ? DIR_IN : DIR_OUT,
+ (void )ng_ipfw_input_p(m0, (dir & PFIL_IN) ? DIR_IN : DIR_OUT,
&args, (i == IP_FW_NGTEE) ? 1 : 0);
if (i == IP_FW_NGTEE) /* ignore errors for NGTEE */
goto again; /* continue with packet */
+ ret = PFIL_CONSUMED;
break;
default:
KASSERT(0, ("%s: unknown retval", __func__));
}
- if (ret != 0) {
+ if (ret != PFIL_PASS) {
if (*m0)
FREE_PKT(*m0);
*m0 = NULL;
@@ -531,20 +532,62 @@ ipfw_divert(struct mbuf **m0, int incoming, struct ipfw_rule_ref *rule,
/*
* attach or detach hooks for a given protocol family
*/
+VNET_DEFINE_STATIC(pfil_hook_t, ipfw_inet_hook);
+VNET_DEFINE_STATIC(pfil_hook_t, ipfw_inet6_hook);
+VNET_DEFINE_STATIC(pfil_hook_t, ipfw_link_hook);
+#define V_ipfw_inet_hook VNET(ipfw_inet_hook)
+#define V_ipfw_inet6_hook VNET(ipfw_inet6_hook)
+#define V_ipfw_link_hook VNET(ipfw_link_hook)
+
static int
ipfw_hook(int onoff, int pf)
{
- struct pfil_head *pfh;
- pfil_func_t hook_func;
-
- pfh = pfil_head_get(PFIL_TYPE_AF, pf);
- if (pfh == NULL)
- return ENOENT;
-
- hook_func = (pf == AF_LINK) ? ipfw_check_frame : ipfw_check_packet;
+ struct pfil_hook_args pha;
+ struct pfil_link_args pla;
+ pfil_hook_t *h;
+
+ pha.pa_version = PFIL_VERSION;
+ pha.pa_flags = PFIL_IN | PFIL_OUT;
+ pha.pa_modname = "ipfw";
+ pha.pa_ruleset = NULL;
+
+ pla.pa_version = PFIL_VERSION;
+ pla.pa_flags = PFIL_IN | PFIL_OUT |
+ PFIL_HEADPTR | PFIL_HOOKPTR;
+
+ switch (pf) {
+ case AF_INET:
+ pha.pa_func = ipfw_check_packet;
+ pha.pa_type = PFIL_TYPE_IP4;
+ pha.pa_rulname = "default";
+ h = &V_ipfw_inet_hook;
+ pla.pa_head = V_inet_pfil_head;
+ break;
+#ifdef INET6
+ case AF_INET6:
+ pha.pa_func = ipfw_check_packet;
+ pha.pa_type = PFIL_TYPE_IP6;
+ pha.pa_rulname = "default6";
+ h = &V_ipfw_inet6_hook;
+ pla.pa_head = V_inet6_pfil_head;
+ break;
+#endif
+ case AF_LINK:
+ pha.pa_func = ipfw_check_frame;
+ pha.pa_type = PFIL_TYPE_ETHERNET;
+ pha.pa_rulname = "default-link";
+ h = &V_ipfw_link_hook;
+ pla.pa_head = V_link_pfil_head;
+ break;
+ }
- (void) (onoff ? pfil_add_hook : pfil_remove_hook)
- (hook_func, NULL, PFIL_IN | PFIL_OUT | PFIL_WAITOK, pfh);
+ if (onoff) {
+ *h = pfil_add_hook(&pha);
+ pla.pa_hook = *h;
+ (void)pfil_link(&pla);
+ } else
+ if (*h != NULL)
+ pfil_remove_hook(*h);
return 0;
}
diff --git a/sys/netpfil/pf/pf_ioctl.c b/sys/netpfil/pf/pf_ioctl.c
index eba8a7f64a3c..91b2c1d33839 100644
--- a/sys/netpfil/pf/pf_ioctl.c
+++ b/sys/netpfil/pf/pf_ioctl.c
@@ -169,16 +169,16 @@ static void pf_tbladdr_copyout(struct pf_addr_wrap *);
* Wrapper functions for pfil(9) hooks
*/
#ifdef INET
-static int pf_check_in(void *arg, struct mbuf **m, struct ifnet *ifp,
- int dir, int flags, struct inpcb *inp);
-static int pf_check_out(void *arg, struct mbuf **m, struct ifnet *ifp,
- int dir, int flags, struct inpcb *inp);
+static pfil_return_t pf_check_in(struct mbuf **m, struct ifnet *ifp,
+ int flags, void *ruleset __unused, struct inpcb *inp);
+static pfil_return_t pf_check_out(struct mbuf **m, struct ifnet *ifp,
+ int flags, void *ruleset __unused, struct inpcb *inp);
#endif
#ifdef INET6
-static int pf_check6_in(void *arg, struct mbuf **m, struct ifnet *ifp,
- int dir, int flags, struct inpcb *inp);
-static int pf_check6_out(void *arg, struct mbuf **m, struct ifnet *ifp,
- int dir, int flags, struct inpcb *inp);
+static pfil_return_t pf_check6_in(struct mbuf **m, struct ifnet *ifp,
+ int flags, void *ruleset __unused, struct inpcb *inp);
+static pfil_return_t pf_check6_out(struct mbuf **m, struct ifnet *ifp,
+ int flags, void *ruleset __unused, struct inpcb *inp);
#endif
static int hook_pf(void);
@@ -4003,9 +4003,9 @@ shutdown_pf(void)
}
#ifdef INET
-static int
-pf_check_in(void *arg, struct mbuf **m, struct ifnet *ifp, int dir, int flags,
- struct inpcb *inp)
+static pfil_return_t
+pf_check_in(struct mbuf **m, struct ifnet *ifp, int flags,
+ void *ruleset __unused, struct inpcb *inp)
{
int chk;
@@ -4015,14 +4015,12 @@ pf_check_in(void *arg, struct mbuf **m, struct ifnet *ifp, int dir, int flags,
*m = NULL;
}
- if (chk != PF_PASS)
- return (EACCES);
- return (0);
+ return (chk == PF_PASS ? PFIL_PASS : PFIL_DROPPED);
}
-static int
-pf_check_out(void *arg, struct mbuf **m, struct ifnet *ifp, int dir, int flags,
- struct inpcb *inp)
+static pfil_return_t
+pf_check_out(struct mbuf **m, struct ifnet *ifp, int flags,
+ void *ruleset __unused, struct inpcb *inp)
{
int chk;
@@ -4032,16 +4030,14 @@ pf_check_out(void *arg, struct mbuf **m, struct ifnet *ifp, int dir, int flags,
*m = NULL;
}
- if (chk != PF_PASS)
- return (EACCES);
- return (0);
+ return (chk == PF_PASS ? PFIL_PASS : PFIL_DROPPED);
}
#endif
#ifdef INET6
-static int
-pf_check6_in(void *arg, struct mbuf **m, struct ifnet *ifp, int dir, int flags,
- struct inpcb *inp)
+static pfil_return_t
+pf_check6_in(struct mbuf **m, struct ifnet *ifp, int flags,
+ void *ruleset __unused, struct inpcb *inp)
{
int chk;
@@ -4057,14 +4053,13 @@ pf_check6_in(void *arg, struct mbuf **m, struct ifnet *ifp, int dir, int flags,
m_freem(*m);
*m = NULL;
}
- if (chk != PF_PASS)
- return (EACCES);
- return (0);
+
+ return (chk == PF_PASS ? PFIL_PASS : PFIL_DROPPED);
}
-static int
-pf_check6_out(void *arg, struct mbuf **m, struct ifnet *ifp, int dir, int flags,
- struct inpcb *inp)
+static pfil_return_t
+pf_check6_out(struct mbuf **m, struct ifnet *ifp, int flags,
+ void *ruleset __unused, struct inpcb *inp)
{
int chk;
@@ -4075,45 +4070,76 @@ pf_check6_out(void *arg, struct mbuf **m, struct ifnet *ifp, int dir, int flags,
m_freem(*m);
*m = NULL;
}
- if (chk != PF_PASS)
- return (EACCES);
- return (0);
+
+ return (chk == PF_PASS ? PFIL_PASS : PFIL_DROPPED);
}
#endif /* INET6 */
-static int
-hook_pf(void)
-{
#ifdef INET
- struct pfil_head *pfh_inet;
+VNET_DEFINE_STATIC(pfil_hook_t, pf_ip4_in_hook);
+VNET_DEFINE_STATIC(pfil_hook_t, pf_ip4_out_hook);
+#define V_pf_ip4_in_hook VNET(pf_ip4_in_hook)
+#define V_pf_ip4_out_hook VNET(pf_ip4_out_hook)
#endif
#ifdef INET6
- struct pfil_head *pfh_inet6;
+VNET_DEFINE_STATIC(pfil_hook_t, pf_ip6_in_hook);
+VNET_DEFINE_STATIC(pfil_hook_t, pf_ip6_out_hook);
+#define V_pf_ip6_in_hook VNET(pf_ip6_in_hook)
+#define V_pf_ip6_out_hook VNET(pf_ip6_out_hook)
#endif
+static int
+hook_pf(void)
+{
+ struct pfil_hook_args pha;
+ struct pfil_link_args pla;
+
if (V_pf_pfil_hooked)
return (0);
+ pha.pa_version = PFIL_VERSION;
+ pha.pa_modname = "pf";
+ pha.pa_ruleset = NULL;
+
+ pla.pa_version = PFIL_VERSION;
+
#ifdef INET
- pfh_inet = pfil_head_get(PFIL_TYPE_AF, AF_INET);
- if (pfh_inet == NULL)
- return (ESRCH); /* XXX */
- pfil_add_hook_flags(pf_check_in, NULL, PFIL_IN | PFIL_WAITOK, pfh_inet);
- pfil_add_hook_flags(pf_check_out, NULL, PFIL_OUT | PFIL_WAITOK, pfh_inet);
+ pha.pa_type = PFIL_TYPE_IP4;
+ pha.pa_func = pf_check_in;
+ pha.pa_flags = PFIL_IN;
+ pha.pa_rulname = "default-in";
+ V_pf_ip4_in_hook = pfil_add_hook(&pha);
+ pla.pa_flags = PFIL_IN | PFIL_HEADPTR | PFIL_HOOKPTR;
+ pla.pa_head = V_inet_pfil_head;
+ pla.pa_hook = V_pf_ip4_in_hook;
+ (void)pfil_link(&pla);
+ pha.pa_func = pf_check_out;
+ pha.pa_flags = PFIL_OUT;
+ pha.pa_rulname = "default-out";
+ V_pf_ip4_out_hook = pfil_add_hook(&pha);
+ pla.pa_flags = PFIL_OUT | PFIL_HEADPTR | PFIL_HOOKPTR;
+ pla.pa_head = V_inet_pfil_head;
+ pla.pa_hook = V_pf_ip4_out_hook;
+ (void)pfil_link(&pla);
#endif
#ifdef INET6
- pfh_inet6 = pfil_head_get(PFIL_TYPE_AF, AF_INET6);
- if (pfh_inet6 == NULL) {
-#ifdef INET
- pfil_remove_hook_flags(pf_check_in, NULL, PFIL_IN | PFIL_WAITOK,
- pfh_inet);
- pfil_remove_hook_flags(pf_check_out, NULL, PFIL_OUT | PFIL_WAITOK,
- pfh_inet);
-#endif
- return (ESRCH); /* XXX */
- }
- pfil_add_hook_flags(pf_check6_in, NULL, PFIL_IN | PFIL_WAITOK, pfh_inet6);
- pfil_add_hook_flags(pf_check6_out, NULL, PFIL_OUT | PFIL_WAITOK, pfh_inet6);
+ pha.pa_type = PFIL_TYPE_IP6;
+ pha.pa_func = pf_check6_in;
+ pha.pa_flags = PFIL_IN;
+ pha.pa_rulname = "default-in6";
+ V_pf_ip6_in_hook = pfil_add_hook(&pha);
+ pla.pa_flags = PFIL_IN | PFIL_HEADPTR | PFIL_HOOKPTR;
+ pla.pa_head = V_inet6_pfil_head;
+ pla.pa_hook = V_pf_ip6_in_hook;
+ (void)pfil_link(&pla);
+ pha.pa_func = pf_check6_out;
+ pha.pa_rulname = "default-out6";
+ pha.pa_flags = PFIL_OUT;
+ V_pf_ip6_out_hook = pfil_add_hook(&pha);
+ pla.pa_flags = PFIL_OUT | PFIL_HEADPTR | PFIL_HOOKPTR;
+ pla.pa_head = V_inet6_pfil_head;
+ pla.pa_hook = V_pf_ip6_out_hook;
+ (void)pfil_link(&pla);
#endif
V_pf_pfil_hooked = 1;
@@ -4123,33 +4149,17 @@ hook_pf(void)
static int
dehook_pf(void)
{
-#ifdef INET
- struct pfil_head *pfh_inet;
-#endif
-#ifdef INET6
- struct pfil_head *pfh_inet6;
-#endif
if (V_pf_pfil_hooked == 0)
return (0);
#ifdef INET
- pfh_inet = pfil_head_get(PFIL_TYPE_AF, AF_INET);
- if (pfh_inet == NULL)
- return (ESRCH); /* XXX */
- pfil_remove_hook_flags(pf_check_in, NULL, PFIL_IN | PFIL_WAITOK,
- pfh_inet);
- pfil_remove_hook_flags(pf_check_out, NULL, PFIL_OUT | PFIL_WAITOK,
- pfh_inet);
+ pfil_remove_hook(V_pf_ip4_in_hook);
+ pfil_remove_hook(V_pf_ip4_out_hook);
#endif
#ifdef INET6
- pfh_inet6 = pfil_head_get(PFIL_TYPE_AF, AF_INET6);
- if (pfh_inet6 == NULL)
- return (ESRCH); /* XXX */
- pfil_remove_hook_flags(pf_check6_in, NULL, PFIL_IN | PFIL_WAITOK,
- pfh_inet6);
- pfil_remove_hook_flags(pf_check6_out, NULL, PFIL_OUT | PFIL_WAITOK,
- pfh_inet6);
+ pfil_remove_hook(V_pf_ip6_in_hook);
+ pfil_remove_hook(V_pf_ip6_out_hook);
#endif
V_pf_pfil_hooked = 0;