Skip site navigation (1)Skip section navigation (2)
Date:      Tue, 10 Feb 2026 12:07:09 +0000
From:      Andrey V. Elsukov <ae@FreeBSD.org>
To:        src-committers@FreeBSD.org, dev-commits-src-all@FreeBSD.org, dev-commits-src-branches@FreeBSD.org
Cc:        Boris Lytochkin <lytboris@gmail.com>
Subject:   git: 704ec5e68c44 - stable/14 - ipfw: add ability to run ipfw(8) binary with 15.0+ kernel module
Message-ID:  <698b1f6d.32357.78a6474f@gitrepo.freebsd.org>

index | next in thread | raw e-mail

The branch stable/14 has been updated by ae:

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

commit 704ec5e68c44f08d83f3b0daa315c6143338863f
Author:     Boris Lytochkin <lytboris@gmail.com>
AuthorDate: 2026-02-10 11:50:20 +0000
Commit:     Andrey V. Elsukov <ae@FreeBSD.org>
CommitDate: 2026-02-10 11:50:20 +0000

    ipfw: add ability to run ipfw(8) binary with 15.0+ kernel module
    
    After D46183 the KBI was changed and this made the upgrade procedure
    to 15.0+ version a bit difficult, because the old binary can not load
    firewall rules when the new kernel is loaded.
    
    This commit adds the sbin/ipfw15 binary that uses new KBI, and then
    original sbin/ipfw can detect new KBI and run the new binary instead.
    
    PR:             291562
    Reviewed by:    jhb, glebius
    Fixes:          4a77657cbc01
    MFC after:      3 days
    Differential Revision:  https://reviews.freebsd.org/D54763
---
 sbin/Makefile                                   |    1 +
 sbin/ipfw/main.c                                |   26 +
 sbin/ipfw15/Makefile                            |   23 +
 sbin/ipfw15/Makefile.depend                     |   19 +
 sbin/ipfw15/altq.c                              |  152 +
 sbin/ipfw15/dummynet.c                          | 2016 ++++++++
 sbin/ipfw15/include15/alias15.h                 |  259 +
 sbin/ipfw15/include15/netinet/ip_dummynet15.h   |  284 ++
 sbin/ipfw15/include15/netinet/ip_fw15.h         | 1172 +++++
 sbin/ipfw15/include15/netinet6/ip_fw_nat64_15.h |  212 +
 sbin/ipfw15/include15/netinet6/ip_fw_nptv6_15.h |   52 +
 sbin/ipfw15/ip_fw15.h                           | 1172 +++++
 sbin/ipfw15/ipfw.8                              | 5094 +++++++++++++++++++
 sbin/ipfw15/ipfw2.c                             | 6129 +++++++++++++++++++++++
 sbin/ipfw15/ipfw2.h                             |  470 ++
 sbin/ipfw15/ipv6.c                              |  519 ++
 sbin/ipfw15/main.c                              |  716 +++
 sbin/ipfw15/nat.c                               | 1196 +++++
 sbin/ipfw15/nat64clat.c                         |  536 ++
 sbin/ipfw15/nat64lsn.c                          |  901 ++++
 sbin/ipfw15/nat64stl.c                          |  552 ++
 sbin/ipfw15/nptv6.c                             |  452 ++
 sbin/ipfw15/tables.c                            | 2096 ++++++++
 23 files changed, 24049 insertions(+)

diff --git a/sbin/Makefile b/sbin/Makefile
index cbfff38cc626..3afe1c6d1d1e 100644
--- a/sbin/Makefile
+++ b/sbin/Makefile
@@ -78,6 +78,7 @@ SUBDIR.${MK_HAST}+=	hastd
 SUBDIR.${MK_INET6}+=	rtsol
 SUBDIR.${MK_IPFILTER}+=	ipf
 SUBDIR.${MK_IPFW}+=	ipfw
+SUBDIR.${MK_IPFW}+=	ipfw15
 SUBDIR.${MK_IPFW}+=	natd
 SUBDIR.${MK_OPENSSL}+=	decryptcore
 SUBDIR.${MK_PF}+=	pfctl
diff --git a/sbin/ipfw/main.c b/sbin/ipfw/main.c
index 1e5f4fbafc1d..3d5cfc96af46 100644
--- a/sbin/ipfw/main.c
+++ b/sbin/ipfw/main.c
@@ -18,6 +18,7 @@
  * Command line interface for IP firewall facility
  */
 
+#include <sys/stat.h>
 #include <sys/wait.h>
 #include <ctype.h>
 #include <err.h>
@@ -30,6 +31,8 @@
 #include <unistd.h>
 #include <libgen.h>
 
+#include <osreldate.h>
+
 #include "ipfw2.h"
 
 static void
@@ -690,6 +693,29 @@ main(int ac, char *av[])
 	else
 		g_co.prog = cmdline_prog_ipfw;
 
+	/*
+	 * KBI-incompatibility detected, check for availability of ipfw/dnctl15
+	 * binaries and run them instead
+	 */
+	if (getosreldate() >= 1500000) {
+		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 = execv(releng15_progname, av)) < 0) {
+			printf("execv(%s) error: %s\n", releng15_progname,
+			    strerror(errno));
+		}
+		return (ret);
+	}
+
 	/*
 	 * If the last argument is an absolute pathname, interpret it
 	 * as a file to be preprocessed.
diff --git a/sbin/ipfw15/Makefile b/sbin/ipfw15/Makefile
new file mode 100644
index 000000000000..b28b365caa84
--- /dev/null
+++ b/sbin/ipfw15/Makefile
@@ -0,0 +1,23 @@
+.include <src.opts.mk>
+
+PACKAGE=ipfw15
+PROG=	ipfw15
+
+CFLAGS+=	-I ${.CURDIR}/include15
+
+LINKS=	${BINDIR}/ipfw15 ${BINDIR}/dnctl15
+MK_MAN=	no
+
+SRCS=	ipfw2.c dummynet.c ipv6.c main.c nat.c tables.c
+SRCS+=	nat64clat.c nat64lsn.c nat64stl.c nptv6.c
+
+.if ${MK_PF} != "no"
+SRCS+=	altq.c
+CFLAGS+=-DPF
+.endif
+
+LIBADD=	jail util
+
+.include <bsd.prog.mk>
+
+CWARNFLAGS+= -Wno-cast-align
diff --git a/sbin/ipfw15/Makefile.depend b/sbin/ipfw15/Makefile.depend
new file mode 100644
index 000000000000..774db391da26
--- /dev/null
+++ b/sbin/ipfw15/Makefile.depend
@@ -0,0 +1,19 @@
+# Autogenerated - do NOT edit!
+
+DIRDEPS = \
+	include \
+	include/arpa \
+	include/xlocale \
+	lib/${CSU_DIR} \
+	lib/libalias/libalias \
+	lib/libc \
+	lib/libcompiler_rt \
+	lib/libjail \
+	lib/libutil \
+
+
+.include <dirdeps.mk>
+
+.if ${DEP_RELDIR} == ${_DEP_RELDIR}
+# local dependencies - needed for -jN in clean tree
+.endif
diff --git a/sbin/ipfw15/altq.c b/sbin/ipfw15/altq.c
new file mode 100644
index 000000000000..a49f450e046c
--- /dev/null
+++ b/sbin/ipfw15/altq.c
@@ -0,0 +1,152 @@
+/*-
+ * Copyright (c) 2002-2003 Luigi Rizzo
+ * Copyright (c) 1996 Alex Nash, Paul Traina, Poul-Henning Kamp
+ * Copyright (c) 1994 Ugen J.S.Antsilevich
+ *
+ * Idea and grammar partially left from:
+ * Copyright (c) 1993 Daniel Boulet
+ *
+ * Redistribution and use in source forms, with and without modification,
+ * are permitted provided that this entire comment appears intact.
+ *
+ * Redistribution in binary form may occur without any restrictions.
+ * Obviously, it would be nice if you gave credit where credit is due
+ * but requiring it would be too onerous.
+ *
+ * This software is provided ``AS IS'' without any warranties of any kind.
+ *
+ * NEW command line interface for IP firewall facility
+ *
+ * altq interface
+ */
+
+#define PFIOC_USE_LATEST
+
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <sys/sockio.h>
+
+#include "ipfw2.h"
+
+#include <err.h>
+#include <errno.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sysexits.h>
+#include <unistd.h>
+#include <fcntl.h>
+
+#include <net/if.h>		/* IFNAMSIZ */
+#include <net/pfvar.h>
+#include <netinet/in.h>	/* in_addr */
+#include <netinet/ip_fw15.h>
+
+/*
+ * Map between current altq queue id numbers and names.
+ */
+static TAILQ_HEAD(, pf_altq) altq_entries =
+	TAILQ_HEAD_INITIALIZER(altq_entries);
+
+void
+altq_set_enabled(int enabled)
+{
+	int pffd;
+
+	pffd = open("/dev/pf", O_RDWR);
+	if (pffd == -1)
+		err(EX_UNAVAILABLE,
+		    "altq support opening pf(4) control device");
+	if (enabled) {
+		if (ioctl(pffd, DIOCSTARTALTQ) != 0 && errno != EEXIST)
+			err(EX_UNAVAILABLE, "enabling altq");
+	} else {
+		if (ioctl(pffd, DIOCSTOPALTQ) != 0 && errno != ENOENT)
+			err(EX_UNAVAILABLE, "disabling altq");
+	}
+	close(pffd);
+}
+
+static void
+altq_fetch(void)
+{
+	struct pfioc_altq pfioc;
+	struct pf_altq *altq;
+	int pffd;
+	unsigned int mnr;
+	static int altq_fetched = 0;
+
+	if (altq_fetched)
+		return;
+	altq_fetched = 1;
+	pffd = open("/dev/pf", O_RDONLY);
+	if (pffd == -1) {
+		warn("altq support opening pf(4) control device");
+		return;
+	}
+	bzero(&pfioc, sizeof(pfioc));
+	pfioc.version = PFIOC_ALTQ_VERSION;
+	if (ioctl(pffd, DIOCGETALTQS, &pfioc) != 0) {
+		warn("altq support getting queue list");
+		close(pffd);
+		return;
+	}
+	mnr = pfioc.nr;
+	for (pfioc.nr = 0; pfioc.nr < mnr; pfioc.nr++) {
+		if (ioctl(pffd, DIOCGETALTQ, &pfioc) != 0) {
+			if (errno == EBUSY)
+				break;
+			warn("altq support getting queue list");
+			close(pffd);
+			return;
+		}
+		if (pfioc.altq.qid == 0)
+			continue;
+		altq = safe_calloc(1, sizeof(*altq));
+		*altq = pfioc.altq;
+		TAILQ_INSERT_TAIL(&altq_entries, altq, entries);
+	}
+	close(pffd);
+}
+
+u_int32_t
+altq_name_to_qid(const char *name)
+{
+	struct pf_altq *altq;
+
+	altq_fetch();
+	TAILQ_FOREACH(altq, &altq_entries, entries)
+		if (strcmp(name, altq->qname) == 0)
+			break;
+	if (altq == NULL)
+		errx(EX_DATAERR, "altq has no queue named `%s'", name);
+	return altq->qid;
+}
+
+static const char *
+altq_qid_to_name(u_int32_t qid)
+{
+	struct pf_altq *altq;
+
+	altq_fetch();
+	TAILQ_FOREACH(altq, &altq_entries, entries)
+		if (qid == altq->qid)
+			break;
+	if (altq == NULL)
+		return NULL;
+	return altq->qname;
+}
+
+void
+print_altq_cmd(struct buf_pr *bp, const ipfw_insn_altq *altqptr)
+{
+	if (altqptr) {
+		const char *qname;
+
+		qname = altq_qid_to_name(altqptr->qid);
+		if (qname == NULL)
+			bprintf(bp, " altq ?<%u>", altqptr->qid);
+		else
+			bprintf(bp, " altq %s", qname);
+	}
+}
diff --git a/sbin/ipfw15/dummynet.c b/sbin/ipfw15/dummynet.c
new file mode 100644
index 000000000000..0d8132f6e249
--- /dev/null
+++ b/sbin/ipfw15/dummynet.c
@@ -0,0 +1,2016 @@
+/*-
+ * Codel/FQ_Codel and PIE/FQ_PIE Code:
+ * Copyright (C) 2016 Centre for Advanced Internet Architectures,
+ *  Swinburne University of Technology, Melbourne, Australia.
+ * Portions of this code were made possible in part by a gift from 
+ *  The Comcast Innovation Fund.
+ * Implemented by Rasool Al-Saadi <ralsaadi@swin.edu.au>
+ * 
+ * Copyright (c) 2002-2003,2010 Luigi Rizzo
+ *
+ * Redistribution and use in source forms, with and without modification,
+ * are permitted provided that this entire comment appears intact.
+ *
+ * Redistribution in binary form may occur without any restrictions.
+ * Obviously, it would be nice if you gave credit where credit is due
+ * but requiring it would be too onerous.
+ *
+ * This software is provided ``AS IS'' without any warranties of any kind.
+ *
+ * dummynet support
+ */
+
+#define NEW_AQM
+#include <sys/limits.h>
+#include <sys/param.h>
+#include <sys/socket.h>
+/* XXX there are several sysctl leftover here */
+#include <sys/sysctl.h>
+
+#include "ipfw2.h"
+
+#ifdef NEW_AQM
+#include <stdint.h>
+#endif
+
+#include <ctype.h>
+#include <err.h>
+#include <errno.h>
+#include <libutil.h>
+#include <netdb.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sysexits.h>
+
+#include <net/if.h>
+#include <netinet/in.h>
+#include <netinet/ip_fw15.h>
+#include <netinet/ip_dummynet15.h>
+#include <arpa/inet.h>	/* inet_ntoa */
+
+
+static struct _s_x dummynet_params[] = {
+	{ "plr",		TOK_PLR },
+	{ "noerror",		TOK_NOERROR },
+	{ "buckets",		TOK_BUCKETS },
+	{ "dst-ip",		TOK_DSTIP },
+	{ "src-ip",		TOK_SRCIP },
+	{ "dst-port",		TOK_DSTPORT },
+	{ "src-port",		TOK_SRCPORT },
+	{ "proto",		TOK_PROTO },
+	{ "weight",		TOK_WEIGHT },
+	{ "lmax",		TOK_LMAX },
+	{ "maxlen",		TOK_LMAX },
+	{ "all",		TOK_ALL },
+	{ "mask",		TOK_MASK }, /* alias for both */
+	{ "sched_mask",		TOK_SCHED_MASK },
+	{ "flow_mask",		TOK_FLOW_MASK },
+	{ "droptail",		TOK_DROPTAIL },
+	{ "ecn",		TOK_ECN },
+	{ "red",		TOK_RED },
+	{ "gred",		TOK_GRED },
+#ifdef NEW_AQM
+	{ "codel",		TOK_CODEL}, /* Codel AQM */
+	{ "fq_codel",	TOK_FQ_CODEL}, /* FQ-Codel  */
+	{ "pie",		TOK_PIE}, /* PIE AQM */
+	{ "fq_pie",		TOK_FQ_PIE}, /* FQ-PIE */
+#endif
+	{ "bw",			TOK_BW },
+	{ "bandwidth",		TOK_BW },
+	{ "delay",		TOK_DELAY },
+	{ "link",		TOK_LINK },
+	{ "pipe",		TOK_PIPE },
+	{ "queue",		TOK_QUEUE },
+	{ "flowset",		TOK_FLOWSET },
+	{ "sched",		TOK_SCHED },
+	{ "pri",		TOK_PRI },
+	{ "priority",		TOK_PRI },
+	{ "type",		TOK_TYPE },
+	{ "flow-id",		TOK_FLOWID},
+	{ "dst-ipv6",		TOK_DSTIP6},
+	{ "dst-ip6",		TOK_DSTIP6},
+	{ "src-ipv6",		TOK_SRCIP6},
+	{ "src-ip6",		TOK_SRCIP6},
+	{ "profile",		TOK_PROFILE},
+	{ "burst",		TOK_BURST},
+	{ "dummynet-params",	TOK_NULL },
+	{ NULL, 0 }	/* terminator */
+};
+
+#ifdef NEW_AQM
+/* AQM/extra sched parameters  tokens*/
+static struct _s_x aqm_params[] = {
+	{ "target",		TOK_TARGET},
+	{ "interval",		TOK_INTERVAL},
+	{ "limit",		TOK_LIMIT},
+	{ "flows",		TOK_FLOWS},
+	{ "quantum",		TOK_QUANTUM},
+	{ "ecn",		TOK_ECN},
+	{ "noecn",		TOK_NO_ECN},
+	{ "tupdate",		TOK_TUPDATE},
+	{ "max_burst",		TOK_MAX_BURST},
+	{ "max_ecnth",	TOK_MAX_ECNTH},
+	{ "alpha",		TOK_ALPHA},
+	{ "beta",		TOK_BETA},
+	{ "capdrop",	TOK_CAPDROP},
+	{ "nocapdrop",	TOK_NO_CAPDROP},
+	{ "onoff",	TOK_ONOFF},
+	{ "dre",	TOK_DRE},
+	{ "ts",	TOK_TS},
+	{ "derand",	TOK_DERAND},
+	{ "noderand",	TOK_NO_DERAND},
+	{ NULL, 0 }	/* terminator */
+};
+#endif
+
+#define O_NEXT(p, len) ((void *)((char *)p + len))
+
+static void
+oid_fill(struct dn_id *oid, int len, int type, uintptr_t id)
+{
+	oid->len = len;
+	oid->type = type;
+	oid->subtype = 0;
+	oid->id = id;
+}
+
+/* make room in the buffer and move the pointer forward */
+static void *
+o_next(struct dn_id **o, int len, int type)
+{
+	struct dn_id *ret = *o;
+	oid_fill(ret, len, type, 0);
+	*o = O_NEXT(*o, len);
+	return ret;
+}
+
+#ifdef NEW_AQM
+
+/* Codel flags */
+enum {
+	CODEL_ECN_ENABLED = 1
+};
+
+/* PIE flags, from PIE kernel module */
+enum {
+	PIE_ECN_ENABLED = 1,
+	PIE_CAPDROP_ENABLED = 2,
+	PIE_ON_OFF_MODE_ENABLED = 4,
+	PIE_DEPRATEEST_ENABLED = 8,
+	PIE_DERAND_ENABLED = 16
+};
+
+#define PIE_FIX_POINT_BITS 13
+#define PIE_SCALE (1L<<PIE_FIX_POINT_BITS)
+
+/* integer to time */
+static void
+us_to_time(int t, char *strt)
+{
+	if (t < 0)
+		strt[0]='\0';
+	else if ( t==0 )
+		sprintf(strt,"%d", t);
+	else if (t< 1000)
+		sprintf(strt,"%dus", t);
+	else if (t < 1000000) 
+		sprintf(strt,"%gms", (float) t / 1000);
+	else
+		sprintf(strt,"%gfs", (float) t / 1000000);
+}
+
+/*
+ * returns -1 if s is not a valid time, otherwise, return time in us
+ */
+static long
+time_to_us(const char *s)
+{
+	int i, dots = 0;
+	int len = strlen(s);
+	char strt[16]="", stru[16]="";
+	
+	if (len>15)
+		return -1;
+	for (i = 0; i<len && (isdigit(s[i]) || s[i]=='.') ; i++)
+		if (s[i]=='.') {
+			if (dots)
+				return -1;
+			else
+				dots++;
+		}
+
+	if (!i)
+		return -1;
+	strncpy(strt, s, i);
+	if (i<len)
+		strcpy(stru, s+i);
+	else
+		strcpy(stru, "ms");
+	
+	if (!strcasecmp(stru, "us"))
+		return atol(strt);
+	if (!strcasecmp(stru, "ms"))
+		return (strtod(strt, NULL) * 1000);
+	if (!strcasecmp(stru, "s"))
+		return (strtod(strt, NULL)*1000000);
+
+	return -1;
+}
+
+ 
+/* Get AQM or scheduler extra parameters  */
+static void
+get_extra_parms(uint32_t nr, char *out, int subtype)
+{ 
+	struct dn_extra_parms *ep;
+	int ret;
+	char strt1[15], strt2[15], strt3[15];
+	u_int l;
+
+	/* prepare the request */
+	l = sizeof(struct dn_extra_parms);
+	ep = safe_calloc(1, l);
+	memset(ep, 0, sizeof(*ep));
+	*out = '\0';
+
+	oid_fill(&ep->oid, l, DN_CMD_GET, DN_API_VERSION);
+	ep->oid.len = l;
+	ep->oid.subtype = subtype;
+	ep->nr = nr;
+
+	ret = do_cmd(-IP_DUMMYNET3, ep, (uintptr_t)&l);
+	if (ret) {
+		free(ep);
+		errx(EX_DATAERR, "Error getting extra parameters\n");
+	}
+
+	switch (subtype) {
+	case DN_AQM_PARAMS:
+		if( !strcasecmp(ep->name, "codel")) {
+			us_to_time(ep->par[0], strt1);
+			us_to_time(ep->par[1], strt2);
+			l = sprintf(out, " AQM CoDel target %s interval %s",
+				strt1, strt2);
+			if (ep->par[2] & CODEL_ECN_ENABLED)
+				l = sprintf(out + l, " ECN");
+			else
+				l += sprintf(out + l, " NoECN");
+		} else if( !strcasecmp(ep->name, "pie")) {
+			us_to_time(ep->par[0], strt1);
+			us_to_time(ep->par[1], strt2);
+			us_to_time(ep->par[2], strt3);
+			l = sprintf(out, " AQM type PIE target %s tupdate %s alpha "
+					"%g beta %g max_burst %s max_ecnth %.3g",
+					strt1,
+					strt2,
+					ep->par[4] / (float) PIE_SCALE,
+					ep->par[5] / (float) PIE_SCALE,
+					strt3,
+					ep->par[3] / (float) PIE_SCALE
+				);
+				
+			if (ep->par[6] & PIE_ECN_ENABLED)
+				l += sprintf(out + l, " ECN");
+			else
+				l += sprintf(out + l, " NoECN");
+			if (ep->par[6] & PIE_CAPDROP_ENABLED)
+				l += sprintf(out + l, " CapDrop");
+			else
+				l += sprintf(out + l, " NoCapDrop");
+			if (ep->par[6] & PIE_ON_OFF_MODE_ENABLED)
+				l += sprintf(out + l, " OnOff");
+			if (ep->par[6] & PIE_DEPRATEEST_ENABLED)
+				l += sprintf(out + l, " DRE");
+			else
+				l += sprintf(out + l, " TS");
+			if (ep->par[6] & PIE_DERAND_ENABLED)
+				l += sprintf(out + l, " Derand");
+			else
+				l += sprintf(out + l, " NoDerand");
+		}
+		break;
+
+	case	DN_SCH_PARAMS:
+		if (!strcasecmp(ep->name,"FQ_CODEL")) {
+			us_to_time(ep->par[0], strt1);
+			us_to_time(ep->par[1], strt2);
+			l = sprintf(out," FQ_CODEL target %s interval %s"
+				" quantum %jd limit %jd flows %jd",
+				strt1, strt2,
+				(intmax_t) ep->par[3],
+				(intmax_t) ep->par[4],
+				(intmax_t) ep->par[5]
+				);
+			if (ep->par[2] & CODEL_ECN_ENABLED)
+				l += sprintf(out + l, " ECN");
+			else
+				l += sprintf(out + l, " NoECN");
+			l += sprintf(out + l, "\n");
+		} else 	if (!strcasecmp(ep->name,"FQ_PIE")) {
+			us_to_time(ep->par[0], strt1);
+			us_to_time(ep->par[1], strt2);
+			us_to_time(ep->par[2], strt3);
+			l = sprintf(out, "  FQ_PIE target %s tupdate %s alpha "
+				"%g beta %g max_burst %s max_ecnth %.3g"
+				" quantum %jd limit %jd flows %jd",
+				strt1,
+				strt2,
+				ep->par[4] / (float) PIE_SCALE,
+				ep->par[5] / (float) PIE_SCALE,
+				strt3,
+				ep->par[3] / (float) PIE_SCALE,
+				(intmax_t) ep->par[7],
+				(intmax_t) ep->par[8],
+				(intmax_t) ep->par[9]
+			);
+			
+			if (ep->par[6] & PIE_ECN_ENABLED)
+				l += sprintf(out + l, " ECN");
+			else
+				l += sprintf(out + l, " NoECN");
+			if (ep->par[6] & PIE_CAPDROP_ENABLED)
+				l += sprintf(out + l, " CapDrop");
+			else
+				l += sprintf(out + l, " NoCapDrop");
+			if (ep->par[6] & PIE_ON_OFF_MODE_ENABLED)
+				l += sprintf(out + l, " OnOff");
+			if (ep->par[6] & PIE_DEPRATEEST_ENABLED)
+				l += sprintf(out + l, " DRE");
+			else
+				l += sprintf(out + l, " TS");
+			if (ep->par[6] & PIE_DERAND_ENABLED)
+				l += sprintf(out + l, " Derand");
+			else
+				l += sprintf(out + l, " NoDerand");
+			l += sprintf(out + l, "\n");
+		}
+		break;
+	}
+
+	free(ep);
+}
+#endif
+
+
+#if 0
+static int
+sort_q(void *arg, const void *pa, const void *pb)
+{
+	int rev = (co.do_sort < 0);
+	int field = rev ? -co.do_sort : co.do_sort;
+	long long res = 0;
+	const struct dn_flow_queue *a = pa;
+	const struct dn_flow_queue *b = pb;
+
+	switch (field) {
+	case 1: /* pkts */
+		res = a->len - b->len;
+		break;
+	case 2: /* bytes */
+		res = a->len_bytes - b->len_bytes;
+		break;
+
+	case 3: /* tot pkts */
+		res = a->tot_pkts - b->tot_pkts;
+		break;
+
+	case 4: /* tot bytes */
+		res = a->tot_bytes - b->tot_bytes;
+		break;
+	}
+	if (res < 0)
+		res = -1;
+	if (res > 0)
+		res = 1;
+	return (int)(rev ? res : -res);
+}
+#endif
+
+/* print a mask and header for the subsequent list of flows */
+static void
+print_mask(struct ipfw_flow_id *id)
+{
+	if (!IS_IP6_FLOW_ID(id)) {
+		printf("    "
+		    "mask: %s 0x%02x 0x%08x/0x%04x -> 0x%08x/0x%04x\n",
+		    id->extra ? "queue," : "",
+		    id->proto,
+		    id->src_ip, id->src_port,
+		    id->dst_ip, id->dst_port);
+	} else {
+		char buf[255];
+		printf("\n        mask: %sproto: 0x%02x, flow_id: 0x%08x,  ",
+		    id->extra ? "queue," : "",
+		    id->proto, id->flow_id6);
+		inet_ntop(AF_INET6, &(id->src_ip6), buf, sizeof(buf));
+		printf("%s/0x%04x -> ", buf, id->src_port);
+		inet_ntop(AF_INET6, &(id->dst_ip6), buf, sizeof(buf));
+		printf("%s/0x%04x\n", buf, id->dst_port);
+	}
+}
+
+static void
+print_header(struct ipfw_flow_id *id)
+{
+	if (!IS_IP6_FLOW_ID(id))
+		printf("BKT Prot ___Source IP/port____ "
+		    "____Dest. IP/port____ "
+		    "Tot_pkt/bytes Pkt/Byte Drp\n");
+	else
+		printf("BKT ___Prot___ _flow-id_ "
+		    "______________Source IPv6/port_______________ "
+		    "_______________Dest. IPv6/port_______________ "
+		    "Tot_pkt/bytes Pkt/Byte Drp\n");
+}
+
+static void
+list_flow(struct buf_pr *bp, struct dn_flow *ni)
+{
+	char buff[255];
+	struct protoent *pe = NULL;
+	struct in_addr ina;
+	struct ipfw_flow_id *id = &ni->fid;
+
+	pe = getprotobynumber(id->proto);
+		/* XXX: Should check for IPv4 flows */
+	bprintf(bp, "%3u%c", (ni->oid.id) & 0xff,
+		id->extra ? '*' : ' ');
+	if (!IS_IP6_FLOW_ID(id)) {
+		if (pe)
+			bprintf(bp, "%-4s ", pe->p_name);
+		else
+			bprintf(bp, "%4u ", id->proto);
+		ina.s_addr = htonl(id->src_ip);
+		bprintf(bp, "%15s/%-5d ",
+		    inet_ntoa(ina), id->src_port);
+		ina.s_addr = htonl(id->dst_ip);
+		bprintf(bp, "%15s/%-5d ",
+		    inet_ntoa(ina), id->dst_port);
+	} else {
+		/* Print IPv6 flows */
+		if (pe != NULL)
+			bprintf(bp, "%9s ", pe->p_name);
+		else
+			bprintf(bp, "%9u ", id->proto);
+		bprintf(bp, "%7d  %39s/%-5d ", id->flow_id6,
+		    inet_ntop(AF_INET6, &(id->src_ip6), buff, sizeof(buff)),
+		    id->src_port);
+		bprintf(bp, " %39s/%-5d ",
+		    inet_ntop(AF_INET6, &(id->dst_ip6), buff, sizeof(buff)),
+		    id->dst_port);
+	}
+	pr_u64(bp, &ni->tot_pkts, 4);
+	pr_u64(bp, &ni->tot_bytes, 8);
+	bprintf(bp, "%2u %4u %3u",
+	    ni->length, ni->len_bytes, ni->drops);
+}
+
+static void
+print_flowset_parms(struct dn_fs *fs, char *prefix)
+{
+	int l;
+	char qs[30];
+	char plr[40];
+	char red[200];	/* Display RED parameters */
+
+	l = fs->qsize;
+	if (fs->flags & DN_QSIZE_BYTES) {
+		if (l >= 8192)
+			sprintf(qs, "%d KB", l / 1024);
+		else
+			sprintf(qs, "%d B", l);
+	} else
+		sprintf(qs, "%3d sl.", l);
+	if (fs->plr[0] || fs->plr[1]) {
+		if (fs->plr[1] == 0)
+			sprintf(plr, "plr %f",
+				1.0 * fs->plr[0] / (double)(0x7fffffff));
+		else
+			sprintf(plr, "plr %f,%f,%f,%f",
+				1.0 * fs->plr[0] / (double)(0x7fffffff),
+				1.0 * fs->plr[1] / (double)(0x7fffffff),
+				1.0 * fs->plr[2] / (double)(0x7fffffff),
+				1.0 * fs->plr[3] / (double)(0x7fffffff));
+	} else
+		plr[0] = '\0';
+
+	if (fs->flags & DN_IS_RED) {	/* RED parameters */
+		sprintf(red,
+		    "\n\t %cRED w_q %f min_th %d max_th %d max_p %f",
+		    (fs->flags & DN_IS_GENTLE_RED) ? 'G' : ' ',
+		    1.0 * fs->w_q / (double)(1 << SCALE_RED),
+		    fs->min_th,
+		    fs->max_th,
+		    1.0 * fs->max_p / (double)(1 << SCALE_RED));
+		if (fs->flags & DN_IS_ECN)
+			strlcat(red, " (ecn)", sizeof(red));
+#ifdef NEW_AQM
+	/* get AQM parameters */
+	} else if (fs->flags & DN_IS_AQM) {
+			get_extra_parms(fs->fs_nr, red, DN_AQM_PARAMS);
+#endif
+	} else
+		sprintf(red, "droptail");
+
+	if (prefix[0]) {
+	    printf("%s %s%s %d queues (%d buckets) %s\n",
+		prefix, qs, plr, fs->oid.id, fs->buckets, red);
+	    prefix[0] = '\0';
+	} else {
+	    printf("q%05d %s%s %d flows (%d buckets) sched %d "
+			"weight %d lmax %d pri %d %s\n",
+		fs->fs_nr, qs, plr, fs->oid.id, fs->buckets,
+		fs->sched_nr, fs->par[0], fs->par[1], fs->par[2], red);
+	    if (fs->flags & DN_HAVE_MASK)
+		print_mask(&fs->flow_mask);
+	}
+}
+
+static void
+print_extra_delay_parms(struct dn_profile *p)
+{
+	double loss;
+	if (p->samples_no <= 0)
+		return;
+
+	loss = p->loss_level;
+	loss /= p->samples_no;
+	printf("\t profile: name \"%s\" loss %f samples %d\n",
+		p->name, loss, p->samples_no);
+}
+
+static void
+flush_buf(char *buf)
+{
+	if (buf[0])
+		printf("%s\n", buf);
+	buf[0] = '\0';
+}
+
+/*
+ * generic list routine. We expect objects in a specific order, i.e.
+ * PIPES AND SCHEDULERS:
+ *	link; scheduler; internal flowset if any; instances
+ * we can tell a pipe from the number.
+ *
+ * FLOWSETS:
+ *	flowset; queues;
+ * link i (int queue); scheduler i; si(i) { flowsets() : queues }
+ */
+static void
+list_pipes(struct dn_id *oid, struct dn_id *end)
+{
+    char buf[160];	/* pending buffer */
+    int toPrint = 1;	/* print header */
+    struct buf_pr bp;
+
+    buf[0] = '\0';
+    bp_alloc(&bp, 4096);
+    for (; oid != end; oid = O_NEXT(oid, oid->len)) {
+	if (oid->len < sizeof(*oid))
+		errx(1, "invalid oid len %d\n", oid->len);
+
+	switch (oid->type) {
+	default:
+	    flush_buf(buf);
+	    printf("unrecognized object %d size %d\n", oid->type, oid->len);
+	    break;
+	case DN_TEXT: /* list of attached flowsets */
+	    {
+		int i, l;
+		struct {
+			struct dn_id id;
+			uint32_t p[0];
+		} *d = (void *)oid;
+		l = (oid->len - sizeof(*oid))/sizeof(d->p[0]);
+		if (l == 0)
+		    break;
+		printf("   Children flowsets: ");
+		for (i = 0; i < l; i++)
+			printf("%u ", d->p[i]);
+		printf("\n");
+		break;
+	    }
+	case DN_CMD_GET:
+	    if (g_co.verbose)
+		printf("answer for cmd %d, len %d\n", oid->type, oid->id);
+	    break;
+	case DN_SCH: {
+	    struct dn_sch *s = (struct dn_sch *)oid;
+	    flush_buf(buf);
+	    printf(" sched %d type %s flags 0x%x %d buckets %d active\n",
+			s->sched_nr,
+			s->name, s->flags, s->buckets, s->oid.id);
+#ifdef NEW_AQM
+		char parms[200];
+		get_extra_parms(s->sched_nr, parms, DN_SCH_PARAMS);
+		printf("%s",parms);
+#endif
+	    if (s->flags & DN_HAVE_MASK)
+		print_mask(&s->sched_mask);
+	    }
+	    break;
+
+	case DN_FLOW:
+	    if (toPrint != 0) {
+		    print_header(&((struct dn_flow *)oid)->fid);
+		    toPrint = 0;
+	    }
+	    list_flow(&bp, (struct dn_flow *)oid);
+	    printf("%s\n", bp.buf);
+	    bp_flush(&bp);
+	    break;
+
+	case DN_LINK: {
+	    struct dn_link *p = (struct dn_link *)oid;
+	    double b = p->bandwidth;
+	    char bwbuf[30];
+	    char burst[5 + 7];
+
+	    /* This starts a new object so flush buffer */
+	    flush_buf(buf);
+	    /* data rate */
+	    if (b == 0)
+		sprintf(bwbuf, "unlimited     ");
+	    else if (b >= 1000000000)
+		sprintf(bwbuf, "%7.3f Gbit/s", b/1000000000);
+	    else if (b >= 1000000)
+		sprintf(bwbuf, "%7.3f Mbit/s", b/1000000);
+	    else if (b >= 1000)
+		sprintf(bwbuf, "%7.3f Kbit/s", b/1000);
+	    else
+		sprintf(bwbuf, "%7.3f bit/s ", b);
+
+	    if (humanize_number(burst, sizeof(burst), p->burst,
+		    "", HN_AUTOSCALE, 0) < 0 || g_co.verbose)
+		sprintf(burst, "%d", (int)p->burst);
+	    sprintf(buf, "%05d: %s %4d ms burst %s",
+		p->link_nr % DN_MAX_ID, bwbuf, p->delay, burst);
+	    }
+	    break;
+
+	case DN_FS:
+	    print_flowset_parms((struct dn_fs *)oid, buf);
+	    break;
*** 23275 LINES SKIPPED ***


home | help

Want to link to this message? Use this
URL: <https://mail-archive.FreeBSD.org/cgi/mid.cgi?698b1f6d.32357.78a6474f>