The branch stable/14 has been updated by ae:

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

commit 704ec5e68c44f08d83f3b0daa315c6143338863f
Author:     Boris Lytochkin <[email protected]>
AuthorDate: 2026-02-10 11:50:20 +0000
Commit:     Andrey V. Elsukov <[email protected]>
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 <[email protected]>
+ * 
+ * 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 ***

Reply via email to