git: ddf4f9eda9c2 - main - ipfw: create "ipfw0" and "ipfwlog0" bpf tapping points without ifnet(9)

From: Gleb Smirnoff <glebius_at_FreeBSD.org>
Date: Mon, 15 Dec 2025 21:45:33 UTC
The branch main has been updated by glebius:

URL: https://cgit.FreeBSD.org/src/commit/?id=ddf4f9eda9c295082f17e7f26963666b72c97bb9

commit ddf4f9eda9c295082f17e7f26963666b72c97bb9
Author:     Gleb Smirnoff <glebius@FreeBSD.org>
AuthorDate: 2025-12-15 20:51:42 +0000
Commit:     Gleb Smirnoff <glebius@FreeBSD.org>
CommitDate: 2025-12-15 21:17:23 +0000

    ipfw: create "ipfw0" and "ipfwlog0" bpf tapping points without ifnet(9)
    
    As a free bonus the tapping points are now able to match packet direction.
    
    Reviewed by:            ae
    Differential Revision:  https://reviews.freebsd.org/D53875
---
 libexec/rc/rc.conf           |   1 -
 libexec/rc/rc.d/ipfw         |  10 ---
 sbin/ipfw/ipfw.8             |  38 +++--------
 sys/netpfil/ipfw/ip_fw_bpf.c | 146 ++++++++-----------------------------------
 4 files changed, 35 insertions(+), 160 deletions(-)

diff --git a/libexec/rc/rc.conf b/libexec/rc/rc.conf
index ada9094360f6..e8f146807f33 100644
--- a/libexec/rc/rc.conf
+++ b/libexec/rc/rc.conf
@@ -157,7 +157,6 @@ firewall_script="/etc/rc.firewall" # Which script to run to set up the firewall
 firewall_type="UNKNOWN"		# Firewall type (see /etc/rc.firewall)
 firewall_quiet="NO"		# Set to YES to suppress rule display
 firewall_logging="NO"		# Set to YES to enable events logging
-firewall_logif="NO"		# Set to YES to create logging-pseudo interface
 firewall_flags=""		# Flags passed to ipfw when type is a file
 firewall_coscripts=""		# List of executables/scripts to run after
 				# firewall starts/stops
diff --git a/libexec/rc/rc.d/ipfw b/libexec/rc/rc.d/ipfw
index 6d6f7577828f..a9b05ee11132 100755
--- a/libexec/rc/rc.d/ipfw
+++ b/libexec/rc/rc.d/ipfw
@@ -85,16 +85,6 @@ ipfw_start()
 		echo 'Firewall logging enabled.'
 		${SYSCTL} net.inet.ip.fw.verbose=1 >/dev/null
 	fi
-	if checkyesno firewall_logif; then
-		if ! ifconfig ipfw0 >/dev/null 2>&1; then
-			ifconfig ipfw0 create
-			echo 'Firewall logging pseudo-interface (ipfw0)' \
-			    'created.'
-		else
-			echo 'Firewall logging pseudo-interface (ipfw0)' \
-			    'already created.'
-		fi
-	fi
 }
 
 ipfw_poststart()
diff --git a/sbin/ipfw/ipfw.8 b/sbin/ipfw/ipfw.8
index 249bd195b4de..789512e5cc1e 100644
--- a/sbin/ipfw/ipfw.8
+++ b/sbin/ipfw/ipfw.8
@@ -701,28 +701,13 @@ Unless per-rule log destination is specified by
 .Cm logdst Ar logdst_spec
 option (see below), packets are logged in two ways: if the sysctl variable
 .Va net.inet.ip.fw.verbose
-is set to 0 (default), one can use
+is set to 0 (default), one can use the
 .Xr bpf 4
-attached to the
-.Li ipfw0
-pseudo interface.
-This pseudo interface can be created manually after a system
-boot by using the following command:
-.Bd -literal -offset indent
-# ifconfig ipfw0 create
-.Ed
-.Pp
-Or, automatically at boot time by adding the following
-line to the
-.Xr rc.conf 5
-file:
-.Bd -literal -offset indent
-firewall_logif="YES"
-.Ed
-.Pp
+tap named
+.Li ipfw0 .
 There is zero overhead when no
 .Xr bpf 4
-is attached to the pseudo interface.
+listener is attached to the tap.
 .Pp
 If
 .Va net.inet.ip.fw.verbose
@@ -3676,16 +3661,11 @@ reply to the sent ICMP message.
 Default value is
 .Ar 60 .
 .It Cm log
-Turn on logging of all handled packets via BPF through
-.Ar ipfwlog0
-interface.
-.Ar ipfwlog0
-is a pseudo interface and can be created after a boot manually with
-.Cm ifconfig
-command.
+Turn on logging of all handled packets via BPF tap named
+.Ar ipfwlog0 .
 Note that it has different purpose than
 .Ar ipfw0
-interface.
+tap.
 Translators sends to BPF an additional information with each packet.
 With
 .Cm tcpdump
@@ -3744,7 +3724,7 @@ contains mapping how IPv6 addresses should be translated to IPv4 addresses.
 .It Cm log
 Turn on logging of all handled packets via BPF through
 .Ar ipfwlog0
-interface.
+tap.
 .It Cm -log
 Turn off logging of all handled packets via BPF.
 .It Cm allow_private
@@ -3793,7 +3773,7 @@ This IPv6 prefix should be configured on a remote NAT64 translator.
 .It Cm log
 Turn on logging of all handled packets via BPF through
 .Ar ipfwlog0
-interface.
+tap.
 .It Cm -log
 Turn off logging of all handled packets via BPF.
 .It Cm allow_private
diff --git a/sys/netpfil/ipfw/ip_fw_bpf.c b/sys/netpfil/ipfw/ip_fw_bpf.c
index 155e269214ec..68f31ca59b2e 100644
--- a/sys/netpfil/ipfw/ip_fw_bpf.c
+++ b/sys/netpfil/ipfw/ip_fw_bpf.c
@@ -1,6 +1,7 @@
 /*-
  * Copyright (c) 2016 Yandex LLC
  * Copyright (c) 2016 Andrey V. Elsukov <ae@FreeBSD.org>
+ * Copyright (c) 2025 Gleb Smirnoff <glebius@FreeBSD.org>
  *
  * Redistribution and use in source and binary forms, with or without
  * modification, are permitted provided that the following conditions
@@ -29,14 +30,11 @@
 #include <sys/mbuf.h>
 #include <sys/kernel.h>
 #include <sys/lock.h>
+#include <sys/rwlock.h>
 #include <sys/socket.h>
 #include <net/ethernet.h>
 #include <net/if.h>
 #include <net/if_pflog.h>
-#include <net/if_var.h>
-#include <net/if_clone.h>
-#include <net/if_private.h>
-#include <net/if_types.h>
 #include <net/vnet.h>
 #include <net/bpf.h>
 
@@ -45,158 +43,66 @@
 #include <netinet/ip_var.h>
 #include <netpfil/ipfw/ip_fw_private.h>
 
-VNET_DEFINE_STATIC(struct ifnet *, log_if);
-VNET_DEFINE_STATIC(struct ifnet *, pflog_if);
-VNET_DEFINE_STATIC(struct if_clone *, ipfw_cloner);
-VNET_DEFINE_STATIC(struct if_clone *, ipfwlog_cloner);
-#define	V_ipfw_cloner		VNET(ipfw_cloner)
-#define	V_ipfwlog_cloner	VNET(ipfwlog_cloner)
-#define	V_log_if		VNET(log_if)
-#define	V_pflog_if		VNET(pflog_if)
-
-static const char ipfwname[] = "ipfw";
-static const char ipfwlogname[] = "ipfwlog";
-
-static int
-ipfw_bpf_ioctl(struct ifnet *ifp, u_long cmd, caddr_t addr)
-{
-
-	return (EINVAL);
-}
-
-static int
-ipfw_bpf_output(struct ifnet *ifp, struct mbuf *m,
-	const struct sockaddr *dst, struct route *ro)
+static bool
+bpf_ipfw_chkdir(void *arg __unused, const struct mbuf *m, int dir)
 {
-
-	if (m != NULL)
-		FREE_PKT(m);
-	return (0);
+	return ((dir == BPF_D_IN && m_rcvif(m) == NULL) ||
+	    (dir == BPF_D_OUT && m_rcvif(m) != NULL));
 }
 
-static void
-ipfw_clone_destroy(struct ifnet *ifp)
-{
-
-	if (ifp->if_hdrlen == ETHER_HDR_LEN)
-		V_log_if = NULL;
-	else
-		V_pflog_if = NULL;
+static const struct bif_methods bpf_ipfw_methods = {
+	.bif_chkdir = bpf_ipfw_chkdir,
+};
 
-	NET_EPOCH_WAIT();
-	bpfdetach(ifp);
-	if_detach(ifp);
-	if_free(ifp);
-}
+static const char ipfwname[] = "ipfw0";
+static const char ipfwlogname[] = "ipfwlog0";
 
-static int
-ipfw_clone_create(struct if_clone *ifc, int unit, caddr_t params)
-{
-	struct ifnet *ifp;
-
-	ifp = if_alloc(IFT_PFLOG);
-	if_initname(ifp, ipfwname, unit);
-	ifp->if_flags = IFF_UP | IFF_SIMPLEX | IFF_MULTICAST;
-	ifp->if_mtu = 65536;
-	ifp->if_ioctl = ipfw_bpf_ioctl;
-	ifp->if_output = ipfw_bpf_output;
-	ifp->if_hdrlen = ETHER_HDR_LEN;
-	if_attach(ifp);
-	bpfattach(ifp, DLT_EN10MB, ETHER_HDR_LEN);
-	if (V_log_if != NULL) {
-		bpfdetach(ifp);
-		if_detach(ifp);
-		if_free(ifp);
-		return (EEXIST);
-	}
-	V_log_if = ifp;
-	return (0);
-}
-
-static int
-ipfwlog_clone_create(struct if_clone *ifc, int unit, caddr_t params)
-{
-	struct ifnet *ifp;
-
-	ifp = if_alloc(IFT_PFLOG);
-	if_initname(ifp, ipfwlogname, unit);
-	ifp->if_flags = IFF_UP | IFF_SIMPLEX | IFF_MULTICAST;
-	ifp->if_mtu = 65536;
-	ifp->if_ioctl = ipfw_bpf_ioctl;
-	ifp->if_output = ipfw_bpf_output;
-	ifp->if_hdrlen = PFLOG_HDRLEN;
-	if_attach(ifp);
-	bpfattach(ifp, DLT_PFLOG, PFLOG_HDRLEN);
-	if (V_pflog_if != NULL) {
-		bpfdetach(ifp);
-		if_detach(ifp);
-		if_free(ifp);
-		return (EEXIST);
-	}
-	V_pflog_if = ifp;
-	return (0);
-}
+VNET_DEFINE_STATIC(struct bpf_if *, bpf_en10mb);
+VNET_DEFINE_STATIC(struct bpf_if *, bpf_pflog);
+#define	V_bpf_en10mb	VNET(bpf_en10mb)
+#define	V_bpf_pflog	VNET(bpf_pflog)
 
 void
 ipfw_bpf_tap(u_char *pkt, u_int pktlen)
 {
-	struct ifnet *ifp = V_log_if;
-
-	NET_EPOCH_ASSERT();
-	if (ifp != NULL)
-		BPF_TAP(ifp, pkt, pktlen);
+	bpf_tap(V_bpf_en10mb, pkt, pktlen);
 }
 
 void
 ipfw_bpf_mtap(struct mbuf *m)
 {
-	struct ifnet *ifp = V_log_if;
-
-	NET_EPOCH_ASSERT();
-	if (ifp != NULL)
-		BPF_MTAP(ifp, m);
+	bpf_mtap(V_bpf_en10mb, m);
 }
 
 void
 ipfw_bpf_mtap2(void *data, u_int dlen, struct mbuf *m)
 {
-	struct ifnet *logif;
-
-	NET_EPOCH_ASSERT();
 	switch (dlen) {
 	case (ETHER_HDR_LEN):
-		logif = V_log_if;
+		bpf_mtap2(V_bpf_en10mb, data, dlen, m);
 		break;
 	case (PFLOG_HDRLEN):
-		logif = V_pflog_if;
+		bpf_mtap2(V_bpf_pflog, data, dlen, m);
 		break;
 	default:
-#ifdef INVARIANTS
-		panic("%s: unsupported len %d", __func__, dlen);
-#endif
-		logif = NULL;
+		MPASS(0);
 	}
-
-	if (logif != NULL)
-		BPF_MTAP2(logif, data, dlen, m);
 }
 
 void
 ipfw_bpf_init(int first __unused)
 {
 
-	V_log_if = NULL;
-	V_pflog_if = NULL;
-	V_ipfw_cloner = if_clone_simple(ipfwname, ipfw_clone_create,
-	    ipfw_clone_destroy, 0);
-	V_ipfwlog_cloner = if_clone_simple(ipfwlogname, ipfwlog_clone_create,
-	    ipfw_clone_destroy, 0);
+	V_bpf_en10mb = bpf_attach(ipfwname, DLT_EN10MB, ETHER_HDR_LEN,
+	    &bpf_ipfw_methods, NULL);
+	V_bpf_pflog = bpf_attach(ipfwlogname, DLT_PFLOG, PFLOG_HDRLEN,
+	    &bpf_ipfw_methods, NULL);
 }
 
 void
 ipfw_bpf_uninit(int last __unused)
 {
 
-	if_clone_detach(V_ipfw_cloner);
-	if_clone_detach(V_ipfwlog_cloner);
+	bpf_detach(V_bpf_en10mb);
+	bpf_detach(V_bpf_pflog);
 }