git: c64d42caebb7 - stable/14 - ipfw: rework 32-bit KBI detection

Andrey V. Elsukov <[email protected]>
Newsgroups gmane.os.freebsd.devel.cvs.src
Message-ID <6a7ada2b.25e1f.260afbfb__9590.09423838637$1786436165$gmane$org@gitrepo.freebsd.org>
The branch stable/14 has been updated by ae:

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

commit c64d42caebb77f927777408f5a284a7313df1fc4
Author:     Boris Lytochkin <[email protected]>
AuthorDate: 2026-08-11 08:03:16 +0000
Commit:     Andrey V. Elsukov <[email protected]>
CommitDate: 2026-08-11 08:03:16 +0000

    ipfw: rework 32-bit KBI detection
    
    When we run 14.X jail on 15.X host system previous implementation could
    not correctly detect 32-bit KBI due to jails can overwrite osreldate.
    
    Add special handling for this case and use detection using
    IP_FW_DUMP_SOPTCODES and IP_FW_XGET sockopts version.
    
    Reported by:    Vova Grebenschikov
    Fixes:  704ec5e68c44
    MFC after:      3 days
    Differential Revision:  https://reviews.freebsd.org/D56616
---
 sbin/ipfw/ipfw2.c | 102 ++++++++++++++++++++++++++++++++++++++++++++++++++++++
 sbin/ipfw/ipfw2.h |   1 +
 sbin/ipfw/main.c  |  14 ++++----
 3 files changed, 110 insertions(+), 7 deletions(-)

diff --git a/sbin/ipfw/ipfw2.c b/sbin/ipfw/ipfw2.c
index 56e5b0640135..a8a70dccf125 100644
--- a/sbin/ipfw/ipfw2.c
+++ b/sbin/ipfw/ipfw2.c
@@ -33,6 +33,7 @@
 #include <jail.h>
 #include <netdb.h>
 #include <pwd.h>
+#include <osreldate.h>
 #include <stdio.h>
 #include <stdarg.h>
 #include <stdint.h>
@@ -5829,6 +5830,107 @@ ipfw_internal_handler(int ac, char *av[])
 	}
 }
 
+/*
+ * Detect 32 bit ipfw KBI by presence of XGET v=1.
+ *
+ * 32-bit KBI was introduced in 1500034. Report 32-bit KBI for osreldate equal
+ * or greater than 1500034. For lower values, jailed status must be checked to
+ * make sure getosreldate() returned a real value as jail init can be
+ * instructed to override this value (see jail(8)). In case we're in a jail,
+ * use ipfw socket to detect 32-bit KBI using ophandler probes.
+ *
+ * Return:
+ *	 2 - 32-bit opcode KBI detected despite of getosreldate() retval
+ *	 1 - 32-bit opcode KBI detected
+ *	 0 - 16-bit opcode KBI detected
+ *	-1 - an error occurred
+ */
+
+int
+ipfw_detect_u32_kbi(void)
+{
+	ipfw_obj_lheader *hdr = NULL;
+	ipfw_sopt_info *info;
+	socklen_t len;
+	size_t need;
+	uint32_t i;
+	int s, opver, ret = -1;
+
+	if (getosreldate() >= 1500034)
+		return (1);
+
+	/* Make more checks for lower osreldate values */
+	s = 0;
+	need = sizeof(s);
+	sysctlbyname("security.jail.jailed", &s, &need, NULL, 0);
+
+	/* We're not in a jail, value from getosreldate() is real */
+	if (s == 0)
+		return (0);
+
+	/*
+	 * We're in a jail, osreldate may be altered. Use ipfw socket to
+	 * decide.
+	 */
+	s = socket(AF_INET, SOCK_RAW, IPPROTO_RAW);
+	if (s < 0)
+		return (-1);
+
+	/*
+	 * ipfw code @ RELENG_15 can register 61 sockopt handlers.
+	 * Pre-allocate enough to evade realloc()
+	 */
+	need = sizeof(ipfw_obj_lheader) + (64 * sizeof(ipfw_sopt_info));
+
+	opver = 0;
+	for (i = 4; i >= 0; i--) {
+		hdr = realloc(hdr, need);
+		memset(hdr, 0, need);
+		if (hdr == NULL)
+			break;
+
+		hdr->opheader.opcode  = IP_FW_DUMP_SOPTCODES;
+		hdr->opheader.version = opver;
+		hdr->size = need;
+
+		/* Check DUMP_SOPTCODES v=1 existance */
+		len = need;
+		if (getsockopt(s, IPPROTO_IP, IP_FW3, hdr, &len) != 0) {
+			if (errno == ENOMEM) {
+				need = hdr->size;
+				continue;
+			}
+			/* Does not exist. 32-bit KBI? */
+			if (errno == EINVAL && opver == 0) {
+				opver = 1;
+				continue;
+			}
+			/* Report an error */
+			ret = -1;
+			break;
+		}
+		/* Fetched soptcodes successfully */
+		info = (ipfw_sopt_info *)(hdr + 1);
+		for (i = 0; i < hdr->count; i++) {
+			if (info[i].opcode != IP_FW_XGET)
+				continue;
+			if (info[i].version == 0) {
+				ret = 0;
+				break;
+			}
+			if (info[i].version == 1) {
+				ret = 2;
+				break;
+			}
+		}
+		break;
+	}
+
+	free(hdr);
+	close(s);
+	return (ret);
+}
+
 static int
 ipfw_get_tracked_ifaces(ipfw_obj_lheader **polh)
 {
diff --git a/sbin/ipfw/ipfw2.h b/sbin/ipfw/ipfw2.h
index 2137719296f9..58b73a627bd7 100644
--- a/sbin/ipfw/ipfw2.h
+++ b/sbin/ipfw/ipfw2.h
@@ -454,6 +454,7 @@ int fill_ext6hdr(struct _ipfw_insn *cmd, char *av);
 void bp_flush(struct buf_pr *b);
 void fill_table(struct _ipfw_insn *cmd, char *av, uint8_t opcode,
     struct tidx *tstate);
+int ipfw_detect_u32_kbi(void);
 
 /* tables.c */
 struct _ipfw_obj_ctlv;
diff --git a/sbin/ipfw/main.c b/sbin/ipfw/main.c
index 3d5cfc96af46..2ad521bdbea4 100644
--- a/sbin/ipfw/main.c
+++ b/sbin/ipfw/main.c
@@ -18,7 +18,6 @@
  * Command line interface for IP firewall facility
  */
 
-#include <sys/stat.h>
 #include <sys/wait.h>
 #include <ctype.h>
 #include <err.h>
@@ -31,8 +30,6 @@
 #include <unistd.h>
 #include <libgen.h>
 
-#include <osreldate.h>
-
 #include "ipfw2.h"
 
 static void
@@ -673,6 +670,7 @@ ipfw_readfile(int ac, char *av[])
 int
 main(int ac, char *av[])
 {
+	int ret;
 #if defined(_WIN32) && defined(TCC)
 	{
 		WSADATA wsaData;
@@ -697,17 +695,19 @@ main(int ac, char *av[])
 	 * KBI-incompatibility detected, check for availability of ipfw/dnctl15
 	 * binaries and run them instead
 	 */
-	if (getosreldate() >= 1500000) {
+	ret = ipfw_detect_u32_kbi();
+	if (ret > 0) {
 		const char *releng15_progname;
-		int ret;
 
 		if (g_co.prog == cmdline_prog_ipfw)
 			releng15_progname = "/sbin/ipfw15";
 		else
 			releng15_progname = "/sbin/dnctl15";
 
-		printf("WARNING! KBI incompatibility for ipfw is detected,"
-		    " trying to run %s.\n", releng15_progname);
+		if (ret == 1)
+			printf("WARNING! KBI incompatibility for ipfw is"
+			    " detected, trying to run %s.\n",
+			    releng15_progname);
 
 		if ((ret = execv(releng15_progname, av)) < 0) {
 			printf("execv(%s) error: %s\n", releng15_progname,
lmpx.com only provides a reader for public news (NNTP) servers. It is not affiliated with the servers or forums shown here and is not responsible for the content of articles, which is written by their respective authors.