]> git.saurik.com Git - apple/network_cmds.git/blobdiff - ipfw.tproj/ipfw.c
network_cmds-176.tar.gz
[apple/network_cmds.git] / ipfw.tproj / ipfw.c
index 7992ec47bfa8bb7d3f7018851e40f8d48760ee56..f173a81bc4d2cc1dd70cfe582d1c8acc161168a8 100644 (file)
@@ -1,3 +1,27 @@
+/*
+ * Copyright (c) 2002 Apple Computer, Inc. All rights reserved.
+ *
+ * @APPLE_LICENSE_HEADER_START@
+ * 
+ * Copyright (c) 1999-2003 Apple Computer, Inc.  All Rights Reserved.
+ * 
+ * This file contains Original Code and/or Modifications of Original Code
+ * as defined in and that are subject to the Apple Public Source License
+ * Version 2.0 (the 'License'). You may not use this file except in
+ * compliance with the License. Please obtain a copy of the License at
+ * http://www.opensource.apple.com/apsl/ and read it before using this
+ * file.
+ * 
+ * The Original Code and all software distributed under the License are
+ * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
+ * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
+ * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
+ * Please see the License for the specific language governing rights and
+ * limitations under the License.
+ * 
+ * @APPLE_LICENSE_HEADER_END@
+ */
 /*
  * Copyright (c) 1996 Alex Nash, Paul Traina, Poul-Henning Kamp
  * Copyright (c) 1994 Ugen J.S.Antsilevich
  *
  * NEW command line interface for IP firewall facility
  *
- * $Id: ipfw.c,v 1.2 2000/06/07 04:22:47 lindak Exp $
- *
  */
 
-#include <sys/types.h>
-#include <sys/queue.h>
+
+
+#include <sys/param.h>
+#include <sys/mbuf.h>
 #include <sys/socket.h>
 #include <sys/sockio.h>
+#include <sys/sysctl.h>
 #include <sys/time.h>
 #include <sys/wait.h>
+#include <sys/ioctl.h>
 
 #include <ctype.h>
 #include <err.h>
 #include <errno.h>
+#include <grp.h>
 #include <limits.h>
 #include <netdb.h>
+#include <pwd.h>
 #include <signal.h>
 #include <stdio.h>
 #include <stdlib.h>
 #include <stdarg.h>
 #include <string.h>
-#include <sysexits.h>
-#include <time.h>
 #include <unistd.h>
+#include <sysexits.h>
 
 #include <net/if.h>
 #include <netinet/in.h>
 #include <netinet/in_systm.h>
-#include <netinet/ip_var.h>
 #include <netinet/ip.h>
 #include <netinet/ip_icmp.h>
 #include <netinet/ip_fw.h>
 #include <net/route.h> /* def. of struct route */
-#include <sys/param.h>
-#include <sys/mbuf.h>
+#ifdef DUMMYNET
 #include <netinet/ip_dummynet.h>
+#endif /* DUMMYNET */
 #include <netinet/tcp.h>
 #include <arpa/inet.h>
 
-int            lineno = -1;
-
-int            s;                              /* main RAW socket         */
-int            do_resolv=0;                    /* Would try to resolve all */
-int            do_acct=0;                      /* Show packet/byte count  */
-int            do_time=0;                      /* Show time stamps        */
-int            do_quiet=0;                     /* Be quiet in add and flush  */
-int            do_force=0;                     /* Don't ask for confirmation */
-int            do_pipe=0;                      /* this cmd refers to a pipe */
+int            s,                      /* main RAW socket         */
+               do_resolv,              /* Would try to resolve all */
+               do_acct,                /* Show packet/byte count  */
+               do_time,                /* Show time stamps        */
+               do_quiet,               /* Be quiet in add and flush  */
+               do_force,               /* Don't ask for confirmation */
+               #ifdef DUMMYNET
+               do_pipe,                /* this cmd refers to a pipe */
+               #endif /* DUMMYNET */
+               do_sort,                /* field to sort results (0 = no) */
+               verbose;
 
 struct icmpcode {
        int     code;
@@ -86,7 +114,7 @@ static struct icmpcode icmpcodes[] = {
       { ICMP_UNREACH_TOSHOST,          "toshost" },
       { ICMP_UNREACH_FILTER_PROHIB,    "filter-prohib" },
       { ICMP_UNREACH_HOST_PRECEDENCE,  "host-precedence" },
-      { ICMP_UNREACH_PRECEDENCE_CUTOFF,        "precedence-cutoff" },
+      { ICMP_UNREACH_PRECEDENCE_CUTOFF,                "precedence-cutoff" },
       { 0, NULL }
 };
 
@@ -95,22 +123,22 @@ static void show_usage(const char *fmt, ...);
 static int
 mask_bits(struct in_addr m_ad)
 {
-       int h_fnd=0,h_num=0,i;
+       int h_fnd = 0, h_num = 0, i;
        u_long mask;
 
-       mask=ntohl(m_ad.s_addr);
-       for (i=0;i<sizeof(u_long)*CHAR_BIT;i++) {
+       mask = ntohl(m_ad.s_addr);
+       for (i = 0; i < sizeof(u_long) * CHAR_BIT; i++) {
                if (mask & 1L) {
-                       h_fnd=1;
+                       h_fnd = 1;
                        h_num++;
                } else {
                        if (h_fnd)
                                return -1;
                }
-               mask=mask>>1;
+               mask = mask >> 1;
        }
        return h_num;
-}                         
+}
 
 static void
 print_port(prot, port, comma)
@@ -123,6 +151,10 @@ print_port(prot, port, comma)
        const char *protocol;
        int printed = 0;
 
+       if (!strcmp(comma, ":")) {
+               printf("%s0x%04x", comma, port);
+               return;
+       }
        if (do_resolv) {
                pe = getprotobynumber(prot);
                if (pe)
@@ -135,9 +167,9 @@ print_port(prot, port, comma)
                        printf("%s%s", comma, se->s_name);
                        printed = 1;
                }
-       } 
+       }
        if (!printed)
-               printf("%s%d",comma,port);
+               printf("%s%d", comma, port);
 }
 
 static void
@@ -147,10 +179,10 @@ print_iface(char *key, union ip_fw_if *un, int byname)
 
        if (byname) {
                strncpy(ifnb, un->fu_via_if.name, FW_IFNLEN);
-               ifnb[FW_IFNLEN]='\0';
+               ifnb[FW_IFNLEN] = '\0';
                if (un->fu_via_if.unit == -1)
                        printf(" %s %s*", key, ifnb);
-               else 
+               else
                        printf(" %s %s%d", key, ifnb, un->fu_via_if.unit);
        } else if (un->fu_via_ip.s_addr != 0) {
                printf(" %s %s", key, inet_ntoa(un->fu_via_ip));
@@ -183,29 +215,36 @@ show_ipfw(struct ip_fw *chain, int pcwidth, int bcwidth)
        int ndp = IP_FW_GETNDSTP(chain);
 
        if (do_resolv)
-               setservent(1/*stayopen*/);
+               setservent(1/*stay open*/);
 
        printf("%05u ", chain->fw_number);
 
-       if (do_acct) 
-               printf("%*qu %*qu ",pcwidth,chain->fw_pcnt,bcwidth,chain->fw_bcnt);
+       if (do_acct)
+               printf("%*qu %*qu ", pcwidth, chain->fw_pcnt, bcwidth, chain->fw_bcnt);
 
-       if (do_time)
-       {
-               if (chain->timestamp)
-               {
+       if (do_time) {
+               if (chain->timestamp) {
                        char timestr[30];
 
                        strcpy(timestr, ctime((time_t *)&chain->timestamp));
                        *strchr(timestr, '\n') = '\0';
                        printf("%s ", timestr);
+               } else {
+                       printf("                         ");
                }
-               else
-                       printf("                         ");
+       }
+       if (chain->fw_flg == IP_FW_F_CHECK_S) {
+               printf("check-state\n");
+               goto done;
        }
 
-       switch (chain->fw_flg & IP_FW_F_COMMAND)
-       {
+       if (chain->fw_flg & IP_FW_F_RND_MATCH) {
+               double d = 1.0 * (int)(chain->pipe_ptr);
+               d = 1 - (d / 0x7fffffff);
+               printf("prob %f ", d);
+       }
+
+       switch (chain->fw_flg & IP_FW_F_COMMAND) {
                case IP_FW_F_ACCEPT:
                        printf("allow");
                        break;
@@ -224,9 +263,13 @@ show_ipfw(struct ip_fw *chain, int pcwidth, int bcwidth)
                case IP_FW_F_SKIPTO:
                        printf("skipto %u", chain->fw_skipto_rule);
                        break;
-                case IP_FW_F_PIPE:
-                        printf("pipe %u", chain->fw_skipto_rule);
-                        break ;
+               
+               case IP_FW_F_PIPE:
+                       printf("pipe %u", chain->fw_skipto_rule);
+                       break;
+               case IP_FW_F_QUEUE:
+                       printf("queue %u", chain->fw_skipto_rule);
+                       break;
                case IP_FW_F_REJECT:
                        if (chain->fw_reject_code == IP_FW_REJECT_RST)
                                printf("reset");
@@ -243,9 +286,12 @@ show_ipfw(struct ip_fw *chain, int pcwidth, int bcwidth)
                default:
                        errx(EX_OSERR, "impossible");
        }
-   
-       if (chain->fw_flg & IP_FW_F_PRN)
+
+       if (chain->fw_flg & IP_FW_F_PRN) {
                printf(" log");
+               if (chain->fw_logamount)
+                       printf(" logamount %d", chain->fw_logamount);
+       }
 
        pe = getprotobynumber(chain->fw_prot);
        if (pe)
@@ -253,87 +299,118 @@ show_ipfw(struct ip_fw *chain, int pcwidth, int bcwidth)
        else
                printf(" %u", chain->fw_prot);
 
-       printf(" from %s", chain->fw_flg & IP_FW_F_INVSRC ? "not " : "");
-
-       adrt=ntohl(chain->fw_smsk.s_addr);
-       if (adrt==ULONG_MAX && do_resolv) {
-               adrt=(chain->fw_src.s_addr);
-               he=gethostbyaddr((char *)&adrt,sizeof(u_long),AF_INET);
-               if (he==NULL) {
-                       printf(inet_ntoa(chain->fw_src));
-               } else
-                       printf("%s",he->h_name);
+       if (chain->fw_flg & IP_FW_F_SME) {
+               printf(" from me");
        } else {
-               if (adrt!=ULONG_MAX) {
-                       mb=mask_bits(chain->fw_smsk);
-                       if (mb == 0) {
-                               printf("any");
-                       } else {
-                               if (mb > 0) {
-                                       printf(inet_ntoa(chain->fw_src));
-                                       printf("/%d",mb);
+               printf(" from %s", chain->fw_flg & IP_FW_F_INVSRC ? "not " : "");
+
+               adrt = ntohl(chain->fw_smsk.s_addr);
+               if (adrt == ULONG_MAX && do_resolv) {
+                       adrt = (chain->fw_src.s_addr);
+                       he = gethostbyaddr((char *)&adrt,
+                           sizeof(u_long), AF_INET);
+                       if (he == NULL) {
+                               printf("%s", inet_ntoa(chain->fw_src));
+                       } else
+                               printf("%s", he->h_name);
+               } else {
+                       if (adrt != ULONG_MAX) {
+                               mb = mask_bits(chain->fw_smsk);
+                               if (mb == 0) {
+                                       printf("any");
                                } else {
-                                       printf(inet_ntoa(chain->fw_src));
-                                       printf(":");
-                                       printf(inet_ntoa(chain->fw_smsk));
+                                       if (mb > 0) {
+                                               printf("%s", inet_ntoa(chain->fw_src));
+                                               printf("/%d", mb);
+                                       } else {
+                                               printf("%s", inet_ntoa(chain->fw_src));
+                                               printf(":");
+                                               printf("%s", inet_ntoa(chain->fw_smsk));
+                                       }
                                }
-                       }
-               } else
-                       printf(inet_ntoa(chain->fw_src));
+                       } else
+                               printf("%s", inet_ntoa(chain->fw_src));
+               }
        }
 
        if (chain->fw_prot == IPPROTO_TCP || chain->fw_prot == IPPROTO_UDP) {
                comma = " ";
                for (i = 0; i < nsp; i++) {
                        print_port(chain->fw_prot, chain->fw_uar.fw_pts[i], comma);
-                       if (i==0 && (chain->fw_flg & IP_FW_F_SRNG))
+                       if (i == 0 && (chain->fw_flg & IP_FW_F_SRNG))
                                comma = "-";
+                       else if (i == 0 && (chain->fw_flg & IP_FW_F_SMSK))
+                               comma = ":";
                        else
                                comma = ",";
                }
        }
 
-       printf(" to %s", chain->fw_flg & IP_FW_F_INVDST ? "not " : "");
-
-       adrt=ntohl(chain->fw_dmsk.s_addr);
-       if (adrt==ULONG_MAX && do_resolv) {
-               adrt=(chain->fw_dst.s_addr);
-               he=gethostbyaddr((char *)&adrt,sizeof(u_long),AF_INET);
-               if (he==NULL) {
-                       printf(inet_ntoa(chain->fw_dst));
-               } else
-                       printf("%s",he->h_name);
+       if (chain->fw_flg & IP_FW_F_DME) {
+               printf(" to me");
        } else {
-               if (adrt!=ULONG_MAX) {
-                       mb=mask_bits(chain->fw_dmsk);
-                       if (mb == 0) {
-                               printf("any");
-                       } else {
-                               if (mb > 0) {
-                                       printf(inet_ntoa(chain->fw_dst));
-                                       printf("/%d",mb);
+               printf(" to %s", chain->fw_flg & IP_FW_F_INVDST ? "not " : "");
+
+               adrt = ntohl(chain->fw_dmsk.s_addr);
+               if (adrt == ULONG_MAX && do_resolv) {
+                       adrt = (chain->fw_dst.s_addr);
+                       he = gethostbyaddr((char *)&adrt,
+                           sizeof(u_long), AF_INET);
+                       if (he == NULL) {
+                               printf("%s", inet_ntoa(chain->fw_dst));
+                       } else
+                               printf("%s", he->h_name);
+               } else {
+                       if (adrt != ULONG_MAX) {
+                               mb = mask_bits(chain->fw_dmsk);
+                               if (mb == 0) {
+                                       printf("any");
                                } else {
-                                       printf(inet_ntoa(chain->fw_dst));
-                                       printf(":");
-                                       printf(inet_ntoa(chain->fw_dmsk));
+                                       if (mb > 0) {
+                                               printf("%s", inet_ntoa(chain->fw_dst));
+                                               printf("/%d", mb);
+                                       } else {
+                                               printf("%s", inet_ntoa(chain->fw_dst));
+                                               printf(":");
+                                               printf("%s", inet_ntoa(chain->fw_dmsk));
+                                       }
                                }
-                       }
-               } else
-                       printf(inet_ntoa(chain->fw_dst));
+                       } else
+                               printf("%s", inet_ntoa(chain->fw_dst));
+               }
        }
 
        if (chain->fw_prot == IPPROTO_TCP || chain->fw_prot == IPPROTO_UDP) {
                comma = " ";
                for (i = 0; i < ndp; i++) {
                        print_port(chain->fw_prot, chain->fw_uar.fw_pts[nsp+i], comma);
-                       if (i==0 && (chain->fw_flg & IP_FW_F_DRNG))
+                       if (i == 0 && (chain->fw_flg & IP_FW_F_DRNG))
                                comma = "-";
+                       else if (i == 0 && (chain->fw_flg & IP_FW_F_DMSK))
+                               comma = ":";
                        else
                                comma = ",";
                }
        }
 
+       if (chain->fw_flg & IP_FW_F_UID) {
+               struct passwd *pwd = getpwuid(chain->fw_uid);
+
+               if (pwd)
+                       printf(" uid %s", pwd->pw_name);
+               else
+                       printf(" uid %u", chain->fw_uid);
+       }
+
+       if (chain->fw_flg & IP_FW_F_KEEP_S) {
+               if (chain->next_rule_ptr)
+                       printf(" keep-state %d", (int)chain->next_rule_ptr);
+               else
+                       printf(" keep-state");
+       }
        /* Direction */
+       if (chain->fw_flg & IP_FW_BRIDGED)
+               printf(" bridged");
        if ((chain->fw_flg & IP_FW_F_IN) && !(chain->fw_flg & IP_FW_F_OUT))
                printf(" in");
        if (!(chain->fw_flg & IP_FW_F_IN) && (chain->fw_flg & IP_FW_F_OUT))
@@ -371,9 +448,9 @@ show_ipfw(struct ip_fw *chain, int pcwidth, int bcwidth)
                if (chain->fw_ipnopt & IP_FW_IPOPT_RR)   PRINTOPT("!rr");
                if (chain->fw_ipopt  & IP_FW_IPOPT_TS)   PRINTOPT("ts");
                if (chain->fw_ipnopt & IP_FW_IPOPT_TS)   PRINTOPT("!ts");
-       } 
+       }
 
-       if (chain->fw_tcpf & IP_FW_TCPF_ESTAB) 
+       if (chain->fw_ipflg & IP_FW_IF_TCPEST)
                printf(" established");
        else if (chain->fw_tcpf == IP_FW_TCPF_SYN &&
            chain->fw_tcpnf == IP_FW_TCPF_ACK)
@@ -383,7 +460,7 @@ show_ipfw(struct ip_fw *chain, int pcwidth, int bcwidth)
 #define PRINTFLG(x)    {if (_flg_printed) printf(",");\
                        printf(x); _flg_printed = 1;}
 
-               printf(" tcpflg ");
+               printf(" tcpflags ");
                if (chain->fw_tcpf  & IP_FW_TCPF_FIN)  PRINTFLG("fin");
                if (chain->fw_tcpnf & IP_FW_TCPF_FIN)  PRINTFLG("!fin");
                if (chain->fw_tcpf  & IP_FW_TCPF_SYN)  PRINTFLG("syn");
@@ -396,7 +473,25 @@ show_ipfw(struct ip_fw *chain, int pcwidth, int bcwidth)
                if (chain->fw_tcpnf & IP_FW_TCPF_ACK)  PRINTFLG("!ack");
                if (chain->fw_tcpf  & IP_FW_TCPF_URG)  PRINTFLG("urg");
                if (chain->fw_tcpnf & IP_FW_TCPF_URG)  PRINTFLG("!urg");
-       } 
+       }
+       if (chain->fw_tcpopt || chain->fw_tcpnopt) {
+               int     _opt_printed = 0;
+#define PRINTTOPT(x)   {if (_opt_printed) printf(",");\
+                       printf(x); _opt_printed = 1;}
+
+               printf(" tcpoptions ");
+               if (chain->fw_tcpopt  & IP_FW_TCPOPT_MSS)  PRINTTOPT("mss");
+               if (chain->fw_tcpnopt & IP_FW_TCPOPT_MSS)  PRINTTOPT("!mss");
+               if (chain->fw_tcpopt  & IP_FW_TCPOPT_WINDOW)  PRINTTOPT("window");
+               if (chain->fw_tcpnopt & IP_FW_TCPOPT_WINDOW)  PRINTTOPT("!window");
+               if (chain->fw_tcpopt  & IP_FW_TCPOPT_SACK)  PRINTTOPT("sack");
+               if (chain->fw_tcpnopt & IP_FW_TCPOPT_SACK)  PRINTTOPT("!sack");
+               if (chain->fw_tcpopt  & IP_FW_TCPOPT_TS)  PRINTTOPT("ts");
+               if (chain->fw_tcpnopt & IP_FW_TCPOPT_TS)  PRINTTOPT("!ts");
+               if (chain->fw_tcpopt  & IP_FW_TCPOPT_CC)  PRINTTOPT("cc");
+               if (chain->fw_tcpnopt & IP_FW_TCPOPT_CC)  PRINTTOPT("!cc");
+       }
+
        if (chain->fw_flg & IP_FW_F_ICMPBIT) {
                int type_index;
                int first = 1;
@@ -404,98 +499,243 @@ show_ipfw(struct ip_fw *chain, int pcwidth, int bcwidth)
                printf(" icmptype");
 
                for (type_index = 0; type_index < IP_FW_ICMPTYPES_DIM * sizeof(unsigned) * 8; ++type_index)
-                       if (chain->fw_uar.fw_icmptypes[type_index / (sizeof(unsigned) * 8)] & 
+                       if (chain->fw_uar.fw_icmptypes[type_index / (sizeof(unsigned) * 8)] &
                                (1U << (type_index % (sizeof(unsigned) * 8)))) {
                                printf("%c%d", first == 1 ? ' ' : ',', type_index);
                                first = 0;
                        }
        }
        printf("\n");
+done:
        if (do_resolv)
                endservent();
 }
 
+#ifdef DUMMYNET
+int
+sort_q(const void *pa, const void *pb)
+{
+       int rev = (do_sort < 0);
+       int field = rev ? -do_sort : 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);
+}
+
+static void
+list_queues(struct dn_flow_set *fs, struct dn_flow_queue *q)
+{
+       int l;
+
+       printf("    mask: 0x%02x 0x%08x/0x%04x -> 0x%08x/0x%04x\n",
+       fs->flow_mask.proto,
+       fs->flow_mask.src_ip, fs->flow_mask.src_port,
+       fs->flow_mask.dst_ip, fs->flow_mask.dst_port);
+    if (fs->rq_elements == 0)
+       return;
+
+       printf("BKT Prot ___Source IP/port____ "
+          "____Dest. IP/port____ Tot_pkt/bytes Pkt/Byte Drp\n");
+       if (do_sort != 0)
+               heapsort(q, fs->rq_elements, sizeof(*q), sort_q);
+       for (l = 0; l < fs->rq_elements; l++) {
+               struct in_addr ina;
+               struct protoent *pe;
+
+               ina.s_addr = htonl(q[l].id.src_ip);
+               printf("%3d ", q[l].hash_slot);
+               pe = getprotobynumber(q[l].id.proto);
+               if (pe)
+                       printf("%-4s ", pe->p_name);
+               else
+                       printf("%4u ", q[l].id.proto);
+               printf("%15s/%-5d ", inet_ntoa(ina), q[l].id.src_port);
+               ina.s_addr = htonl(q[l].id.dst_ip);
+               printf("%15s/%-5d ",
+               inet_ntoa(ina), q[l].id.dst_port);
+               printf("%4qu %8qu %2u %4u %3u\n",
+               q[l].tot_pkts, q[l].tot_bytes,
+               q[l].len, q[l].len_bytes, q[l].drops);
+               if (verbose)
+                       printf("   S %20qd  F %20qd\n", q[l].S, q[l].F);
+       }
+}
+
+static void
+print_flowset_parms(struct dn_flow_set *fs, char *prefix)
+{
+       int l;
+       char qs[30];
+       char plr[30];
+       char red[90];  /* Display RED parameters */
+
+       l = fs->qsize;
+       if (fs->flags_fs & DN_QSIZE_IS_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)
+               sprintf(plr, "plr %f", 1.0*fs->plr/(double)(0x7fffffff));
+       else
+               plr[0]='\0';
+       if (fs->flags_fs & DN_IS_RED)  /* RED parameters */
+               sprintf(red,
+                   "\n   %cRED w_q %f min_th %d max_th %d max_p %f",
+                   (fs->flags_fs & DN_IS_GENTLE_RED)? 'G' : ' ', 
+                   1.0 * fs->w_q / (double)(1 << SCALE_RED), 
+                   SCALE_VAL(fs->min_th), 
+                   SCALE_VAL(fs->max_th),
+                   1.0 * fs->max_p / (double)(1 << SCALE_RED) ) ;
+       else
+               sprintf(red, "droptail");
+
+       printf("%s %s%s %d queues (%d buckets) %s\n", prefix, qs, plr,
+           fs->rq_elements, fs->rq_size, red);
+}
+#endif /* DUMMYNET */
+
 static void
 list(ac, av)
        int     ac;
        char    **av;
 {
        struct ip_fw *rules;
+       #ifdef DUMMYNET
        struct dn_pipe *pipes;
+       #endif /* DUMMYNET */
        void *data = NULL;
        int pcwidth = 0;
        int bcwidth = 0;
        int n, num = 0;
+       int nbytes;
 
        /* get rules or pipes from kernel, resizing array as necessary */
        {
+               #ifdef DUMMYNET
                const int unit = do_pipe ? sizeof(*pipes) : sizeof(*rules);
                const int ocmd = do_pipe ? IP_DUMMYNET_GET : IP_FW_GET;
-               int nalloc = 0;
-               int nbytes;
-
-               while (num >= nalloc) {
+               #else /* DUMMYNET */
+               const int unit = sizeof(*rules);
+               const int ocmd = IP_FW_GET;
+               #endif /* DUMMYNET */
+               int nalloc = unit;
+               nbytes = nalloc;
+
+               while (nbytes >= nalloc) {
                        nalloc = nalloc * 2 + 200;
-                       nbytes = nalloc * unit;
+                       nbytes = nalloc;
                        if ((data = realloc(data, nbytes)) == NULL)
                                err(EX_OSERR, "realloc");
+                       rules = data;
+                       rules->version = IP_FW_CURRENT_API_VERSION;
                        if (getsockopt(s, IPPROTO_IP, ocmd, data, &nbytes) < 0)
+                               #ifdef DUMMYNET
                                err(EX_OSERR, "getsockopt(IP_%s_GET)",
                                    do_pipe ? "DUMMYNET" : "FW");
-                       num = nbytes / unit;
+                               #else /* DUMMYNET */
+                               err(EX_OSERR, "getsockopt(IP_FW_GET)");
+                               #endif /* DUMMYNET */
                }
        }
 
        /* display requested pipes */
+       #ifdef DUMMYNET
        if (do_pipe) {
-           u_long rulenum;
-
-           pipes = (struct dn_pipe *) data;
-           if (ac > 0)
-               rulenum = strtoul(*av++, NULL, 10);
-           else
-               rulenum = 0 ;
-           for (n = 0; n < num; n++) {
-               struct dn_pipe *const p = &pipes[n];
-               double b = p->bandwidth ;
-               char buf[30] ;
-               char qs[30] ;
-               char plr[30] ;
-               int l ;
-
-               if (rulenum != 0 && rulenum != p->pipe_nr)
-                       continue;
-               if (b == 0)
-                   sprintf(buf, "unlimited");
-               else if (b >= 1000000)
-                   sprintf(buf, "%7.3f Mbit/s", b/1000000 );
-               else if (b >= 1000)
-                   sprintf(buf, "%7.3f Kbit/s", b/1000 );
-               else
-                   sprintf(buf, "%7.3f bit/s ", b );
-
-               if ( (l = p->queue_size_bytes) != 0 ) {
-                   if (l >= 8192)
-                       sprintf(qs,"%d KB", l / 1024);
-                   else
-                       sprintf(qs,"%d B", l);
-               } else
-                   sprintf(qs,"%3d sl.", p->queue_size);
-               if (p->plr)
-                   sprintf(plr,"plr %f", 1.0*p->plr/(double)(0x7fffffff));
+               u_long rulenum;
+               void *next = data;
+               struct dn_pipe *p = (struct dn_pipe *) data;
+               struct dn_flow_set *fs;
+               struct dn_flow_queue *q;
+               int l;
+
+               if (ac > 0)
+                       rulenum = strtoul(*av++, NULL, 10);
                else
-                   plr[0]='\0';
+                       rulenum = 0;
+               for (; nbytes >= sizeof(*p); p = (struct dn_pipe *)next) {
+                       double b = p->bandwidth;
+                       char buf[30];
+                       char prefix[80];
 
-               printf("%05d: %s %4d ms %s %s -- %d pkts (%d B) %d drops\n",
-                   p->pipe_nr, buf, p->delay, qs, plr,
-                   p->r_len, p->r_len_bytes, p->r_drops);
-           }
-           free(data);
-           return;
+                       if (p->next != (struct dn_pipe *)DN_IS_PIPE)
+                               break;
+                       l = sizeof(*p) + p->fs.rq_elements * sizeof(*q);
+                       next = (void *)p  + l;
+                       nbytes -= l;
+                       q = (struct dn_flow_queue *)(p+1);
+
+                       if (rulenum != 0 && rulenum != p->pipe_nr)
+                               continue;
+                       if (p->if_name[0] != '\0')
+                               sprintf(buf, "%s", p->if_name);
+                       else if (b == 0)
+                               sprintf(buf, "unlimited");
+                       else if (b >= 1000000)
+                               sprintf(buf, "%7.3f Mbit/s", b/1000000);
+                       else if (b >= 1000)
+                               sprintf(buf, "%7.3f Kbit/s", b/1000);
+                       else
+                               sprintf(buf, "%7.3f bit/s ", b);
+
+                       sprintf(prefix, "%05d: %s %4d ms ",
+                           p->pipe_nr, buf, p->delay);
+                       print_flowset_parms(&(p->fs), prefix);
+                       if (verbose)
+                               printf("   V %20qd\n", p->V >> MY_M);
+                       list_queues(&(p->fs), q);
+               }
+               fs = (struct dn_flow_set *) next;
+               for (; nbytes >= sizeof(*fs); fs = (struct dn_flow_set *)next) {
+                       char prefix[80];
+
+                       if (fs->next != (struct dn_flow_set *)DN_IS_QUEUE)
+                               break;
+                       l = sizeof(*fs) + fs->rq_elements * sizeof(*q);
+                       next = (void *)fs  + l;
+                       nbytes -= l;
+                       q = (struct dn_flow_queue *)(fs+1);
+                       sprintf(prefix, "q%05d: weight %d pipe %d ",
+                           fs->fs_nr, fs->weight, fs->parent_nr);
+                       print_flowset_parms(fs, prefix);
+                       list_queues(fs, q);
+               }
+               free(data);
+               return;
        }
+       #endif /* DUMMYNET */
 
-       /* if showing stats, figure out column widths ahead of time */
        rules = (struct ip_fw *) data;
+       /* determine num more accurately */
+       num = 0;
+       while (rules[num].fw_number < 65535)
+               num++;
+       num++; /* counting starts from 0 ... */
+       /* if showing stats, figure out column widths ahead of time */
        if (do_acct) {
                for (n = 0; n < num; n++) {
                        struct ip_fw *const r = &rules[n];
@@ -556,6 +796,45 @@ list(ac, av)
                if (exitval != EX_OK)
                        exit(exitval);
        }
+       /*
+        * show dynamic rules
+       */
+       if (num * sizeof (rules[0]) != nbytes) {
+               struct ipfw_dyn_rule *d =
+                   (struct ipfw_dyn_rule *)&rules[num];
+               struct in_addr a;
+               struct protoent *pe;
+
+            printf("## Dynamic rules:\n");
+            for (;; d++) {
+                printf("%05d %qu %qu (T %d, # %d) ty %d",
+                    (int)(d->chain),
+                    d->pcnt, d->bcnt,
+                    d->expire,
+                    d->bucket,
+                    d->type);
+               pe = getprotobynumber(d->id.proto);
+               if (pe)
+                       printf(" %s,", pe->p_name);
+               else
+                       printf(" %u,", d->id.proto);
+                a.s_addr = htonl(d->id.src_ip);
+                printf(" %s", inet_ntoa(a));
+                printf(" %d", d->id.src_port);
+                switch (d->type) {
+                default: /* bidir, no mask */
+                    printf(" <->");
+                    break;
+                }
+                a.s_addr = htonl(d->id.dst_ip);
+                printf(" %s", inet_ntoa(a));
+                printf(" %d", d->id.dst_port);
+                printf("\n");
+                if (d->next == NULL)
+                    break;
+            }
+        }
+
        free(data);
 }
 
@@ -572,20 +851,33 @@ show_usage(const char *fmt, ...)
                warnx("error: %s", buf);
        }
        fprintf(stderr, "usage: ipfw [options]\n"
-"    flush\n"
+#ifdef DUMMYNET
+"    [pipe] flush\n"
+#endif /* DUMMYNET */
 "    add [number] rule\n"
-"    delete number ...\n"
-"    list [number ...]\n"
-"    show [number ...]\n"
+#ifdef DUMMYNET
+"    [pipe] delete number ...\n"
+"    [pipe] list [number ...]\n"
+"    [pipe] show [number ...]\n"
+#endif /* DUMMYNET */
 "    zero [number ...]\n"
-"  rule:  action proto src dst extras...\n"
+"    resetlog [number ...]\n"
+#ifdef DUMMYNET
+"    pipe number config [pipeconfig]\n"
+#endif /* DUMMYNET */
+"  rule: [prob <match_probability>] action proto src dst extras...\n"
 "    action:\n"
 "      {allow|permit|accept|pass|deny|drop|reject|unreach code|\n"
-"       reset|count|skipto num|divert port|tee port|fwd ip} [log]\n"
+"       reset|count|skipto num|divert port|tee port|fwd ip|\n"
+#ifdef DUMMYNET
+"       pipe num"
+#endif /* DUMMYNET */
+"} [log [logamount count]]\n"
 "    proto: {ip|tcp|udp|icmp|<number>}\n"
-"    src: from [not] {any|ip[{/bits|:mask}]} [{port|port-port},[port],...]\n"
-"    dst: to [not] {any|ip[{/bits|:mask}]} [{port|port-port},[port],...]\n"
+"    src: from [not] {me|any|ip[{/bits|:mask}]} [{port|port-port},[port],...]\n"
+"    dst: to [not] {me|any|ip[{/bits|:mask}]} [{port|port-port},[port],...]\n"
 "  extras:\n"
+"    uid {user id}\n"
 "    fragment     (may not be used with ports or tcpflags)\n"
 "    in\n"
 "    out\n"
@@ -593,7 +885,21 @@ show_usage(const char *fmt, ...)
 "    {established|setup}\n"
 "    tcpflags [!]{syn|fin|rst|ack|psh|urg},...\n"
 "    ipoptions [!]{ssrr|lsrr|rr|ts},...\n"
-"    icmptypes {type[,type]}...\n");
+"    tcpoptions [!]{mss|window|sack|ts|cc},...\n"
+"    icmptypes {type[,type]}...\n"
+#ifdef DUMMYNET
+"  pipeconfig:\n"
+"    {bw|bandwidth} <number>{bit/s|Kbit/s|Mbit/s|Bytes/s|KBytes/s|MBytes/s}\n"
+"    {bw|bandwidth} interface_name\n"
+"    delay <milliseconds>\n"
+"    queue <size>{packets|Bytes|KBytes}\n"
+"    plr <fraction>\n"
+"    mask {all| [dst-ip|src-ip|dst-port|src-port|proto] <number>}\n"
+"    buckets <number>}\n"
+"    {red|gred} <fraction>/<number>/<number>/<fraction>\n"
+"    droptail\n"
+#endif /* DUMMYNET */
+);
 
        exit(EX_USAGE);
 }
@@ -603,16 +909,13 @@ lookup_host (host, ipaddr)
        char *host;
        struct in_addr *ipaddr;
 {
-       struct hostent *he = gethostbyname(host);
-
-    if (!he) {
-        if (inet_aton(host, ipaddr))
-            return(0);
-        else
-            return(-1);
-    }
-       *ipaddr = *(struct in_addr *)he->h_addr_list[0];
+       struct hostent *he;
 
+       if (!inet_aton(host, ipaddr)) {
+               if ((he = gethostbyname(host)) == NULL)
+                       return(-1);
+               *ipaddr = *(struct in_addr *)he->h_addr_list[0];
+       }
        return(0);
 }
 
@@ -626,22 +929,22 @@ fill_ip(ipno, mask, acp, avp)
        char **av = *avp;
        char *p = 0, md = 0;
 
-       if (ac && !strncmp(*av,"any",strlen(*av))) {
+       if (ac && !strncmp(*av, "any", strlen(*av))) {
                ipno->s_addr = mask->s_addr = 0; av++; ac--;
        } else {
                p = strchr(*av, '/');
-               if (!p) 
+               if (!p)
                        p = strchr(*av, ':');
                if (p) {
                        md = *p;
-                       *p++ = '\0'; 
+                       *p++ = '\0';
                }
 
                if (lookup_host(*av, ipno) != 0)
                        show_usage("hostname ``%s'' unknown", *av);
                switch (md) {
                        case ':':
-                               if (!inet_aton(p,mask))
+                               if (!inet_aton(p, mask))
                                        show_usage("bad netmask ``%s''", p);
                                break;
                        case '/':
@@ -697,29 +1000,50 @@ add_port(cnt, ptr, off, port)
 }
 
 static int
-lookup_port(const char *arg, int test, int nodash)
+lookup_port(const char *arg, int proto, int test, int nodash)
 {
        int             val;
        char            *earg, buf[32];
        struct servent  *s;
+       char            *p, *q;
 
        snprintf(buf, sizeof(buf), "%s", arg);
-       buf[strcspn(arg, nodash ? "-," : ",")] = 0;
+
+       for (p = q = buf; *p; *q++ = *p++) {
+               if (*p == '\\') {
+                       if (*(p+1))
+                               p++;
+               } else {
+                       if (*p == ',' || (nodash && *p == '-'))
+                               break;
+               }
+       }
+       *q = '\0';
+
        val = (int) strtoul(buf, &earg, 0);
        if (!*buf || *earg) {
+               char *protocol = NULL;
+
+               if (proto != 0) {
+                       struct protoent *pe = getprotobynumber(proto);
+
+                       if (pe)
+                               protocol = pe->p_name;
+               }
+
                setservent(1);
-               if ((s = getservbyname(buf, NULL))) {
+               if ((s = getservbyname(buf, protocol))) {
                        val = htons(s->s_port);
                } else {
                        if (!test) {
-                               errx(EX_DATAERR, "unknown port ``%s''", arg);
+                               errx(EX_DATAERR, "unknown port ``%s''", buf);
                        }
                        val = -1;
                }
        } else {
                if (val < 0 || val > 0xffff) {
                        if (!test) {
-                               errx(EX_DATAERR, "port ``%s'' out of range", arg);
+                               errx(EX_DATAERR, "port ``%s'' out of range", buf);
                        }
                        val = -1;
                }
@@ -727,25 +1051,43 @@ lookup_port(const char *arg, int test, int nodash)
        return(val);
 }
 
+/*
+ * return: 0 normally, 1 if first pair is a range,
+ * 2 if first pair is a port+mask
+ */
 static int
-fill_port(cnt, ptr, off, arg)
-       u_short *cnt, *ptr, off;
-       char *arg;
+fill_port(u_short *cnt, u_short *ptr, u_short off, char *arg, int proto)
 {
        char *s;
        int initial_range = 0;
 
-       s = arg + strcspn(arg, "-,");   /* first port name can't have a dash */
+       for (s = arg; *s && *s != ',' && *s != '-' && *s != ':'; s++) {
+               if (*s == '\\' && *(s+1))
+                       s++;
+       }
+       if (*s == ':') {
+               *s++ = '\0';
+               if (strchr(arg, ','))
+                       errx(EX_USAGE, "port/mask must be first in list");
+               add_port(cnt, ptr, off, *arg ? lookup_port(arg, proto, 0, 0) : 0x0000);
+               arg = s;
+               s = strchr(arg,',');
+               if (s)
+                       *s++ = '\0';
+               add_port(cnt, ptr, off, *arg ? lookup_port(arg, proto, 0, 0) : 0xffff);
+               arg = s;
+               initial_range = 2;
+       } else
        if (*s == '-') {
                *s++ = '\0';
                if (strchr(arg, ','))
                        errx(EX_USAGE, "port range must be first in list");
-               add_port(cnt, ptr, off, *arg ? lookup_port(arg, 0, 0) : 0x0000);
+               add_port(cnt, ptr, off, *arg ? lookup_port(arg, proto, 0, 0) : 0x0000);
                arg = s;
                s = strchr(arg,',');
                if (s)
                        *s++ = '\0';
-               add_port(cnt, ptr, off, *arg ? lookup_port(arg, 0, 0) : 0xffff);
+               add_port(cnt, ptr, off, *arg ? lookup_port(arg, proto, 0, 0) : 0xffff);
                arg = s;
                initial_range = 1;
        }
@@ -753,16 +1095,14 @@ fill_port(cnt, ptr, off, arg)
                s = strchr(arg,',');
                if (s)
                        *s++ = '\0';
-               add_port(cnt, ptr, off, lookup_port(arg, 0, 0));
+               add_port(cnt, ptr, off, lookup_port(arg, proto, 0, 0));
                arg = s;
        }
        return initial_range;
 }
 
 static void
-fill_tcpflag(set, reset, vp)
-       u_char *set, *reset;
-       char **vp;
+fill_tcpflag(u_char *set, u_char *reset, char **vp)
 {
        char *p = *vp,*q;
        u_char *d;
@@ -788,7 +1128,7 @@ fill_tcpflag(set, reset, vp)
                        d = set;
                }
                q = strchr(p, ',');
-               if (q) 
+               if (q)
                        *q++ = '\0';
                for (i = 0; i < sizeof(flags) / sizeof(flags[0]); ++i)
                        if (!strncmp(p, flags[i].name, strlen(p))) {
@@ -801,6 +1141,45 @@ fill_tcpflag(set, reset, vp)
        }
 }
 
+static void
+fill_tcpopts(u_char *set, u_char *reset, char **vp)
+{
+       char *p = *vp,*q;
+       u_char *d;
+
+       while (p && *p) {
+               struct tpcopts {
+                       char * name;
+                       u_char value;
+               } opts[] = {
+                       { "mss", IP_FW_TCPOPT_MSS },
+                       { "window", IP_FW_TCPOPT_WINDOW  },
+                       { "sack", IP_FW_TCPOPT_SACK },
+                       { "ts", IP_FW_TCPOPT_TS },
+                       { "cc", IP_FW_TCPOPT_CC },
+               };
+               int i;
+
+               if (*p == '!') {
+                       p++;
+                       d = reset;
+               } else {
+                       d = set;
+               }
+               q = strchr(p, ',');
+               if (q)
+                       *q++ = '\0';
+               for (i = 0; i < sizeof(opts) / sizeof(opts[0]); ++i)
+                       if (!strncmp(p, opts[i].name, strlen(p))) {
+                               *d |= opts[i].value;
+                               break;
+                       }
+               if (i == sizeof(opts) / sizeof(opts[0]))
+                       show_usage("invalid tcp option ``%s''", p);
+               p = q;
+       }
+}
+
 static void
 fill_ipopt(u_char *set, u_char *reset, char **vp)
 {
@@ -815,12 +1194,12 @@ fill_ipopt(u_char *set, u_char *reset, char **vp)
                        d = set;
                }
                q = strchr(p, ',');
-               if (q) 
+               if (q)
                        *q++ = '\0';
-               if (!strncmp(p,"ssrr",strlen(p))) *d |= IP_FW_IPOPT_SSRR;
-               if (!strncmp(p,"lsrr",strlen(p))) *d |= IP_FW_IPOPT_LSRR;
-               if (!strncmp(p,"rr",strlen(p)))   *d |= IP_FW_IPOPT_RR;
-               if (!strncmp(p,"ts",strlen(p)))   *d |= IP_FW_IPOPT_TS;
+               if (!strncmp(p, "ssrr", strlen(p))) *d |= IP_FW_IPOPT_SSRR;
+               if (!strncmp(p, "lsrr", strlen(p))) *d |= IP_FW_IPOPT_LSRR;
+               if (!strncmp(p, "rr", strlen(p)))   *d |= IP_FW_IPOPT_RR;
+               if (!strncmp(p, "ts", strlen(p)))   *d |= IP_FW_IPOPT_TS;
                p = q;
        }
 }
@@ -837,57 +1216,75 @@ fill_icmptypes(types, vp, fw_flg)
        {
                unsigned long icmptype;
 
-               if ( *c == ',' )
+               if (*c == ',')
                        ++c;
 
                icmptype = strtoul(c, &c, 0);
 
-               if ( *c != ',' && *c != '\0' )
+               if (*c != ',' && *c != '\0')
                        show_usage("invalid ICMP type");
 
                if (icmptype >= IP_FW_ICMPTYPES_DIM * sizeof(unsigned) * 8)
                        show_usage("ICMP type out of range");
 
-               types[icmptype / (sizeof(unsigned) * 8)] |= 
+               types[icmptype / (sizeof(unsigned) * 8)] |=
                        1 << (icmptype % (sizeof(unsigned) * 8));
                *fw_flg |= IP_FW_F_ICMPBIT;
        }
 }
 
 static void
-delete(ac,av)
+delete(ac, av)
        int ac;
        char **av;
 {
        struct ip_fw rule;
+       #ifdef DUMMYNET
        struct dn_pipe pipe;
+       #endif /* DUMMYNET */
        int i;
        int exitval = EX_OK;
 
        memset(&rule, 0, sizeof rule);
+       #ifdef DUMMYNET
        memset(&pipe, 0, sizeof pipe);
+       #endif /* DUMMYNET */
+       rule.version = IP_FW_CURRENT_API_VERSION;
 
        av++; ac--;
 
        /* Rule number */
-       while (ac && isdigit(**av)) {
-            if (do_pipe) {
-                pipe.pipe_nr = atoi(*av); av++; ac--;
-                i = setsockopt(s, IPPROTO_IP, IP_DUMMYNET_DEL,
-                    &pipe, sizeof pipe);
-                if (i) {
-                    exitval = 1;
-                    warn("rule %u: setsockopt(%s)", pipe.pipe_nr, "IP_DUMMYNET_DEL");
-                }
-            } else {
-               rule.fw_number = atoi(*av); av++; ac--;
-               i = setsockopt(s, IPPROTO_IP, IP_FW_DEL, &rule, sizeof rule);
-               if (i) {
-                       exitval = EX_UNAVAILABLE;
-                       warn("rule %u: setsockopt(%s)", rule.fw_number, "IP_FW_DEL");
+       while (ac && isdigit(**av))
+       {
+           i = atoi(*av); av++; ac--;
+
+               #ifdef DUMMYNET
+               if (do_pipe)
+               {
+                       if (do_pipe == 1)       pipe.pipe_nr = i;
+                       else                            pipe.fs.fs_nr = i;
+
+                       i = setsockopt(s, IPPROTO_IP, IP_DUMMYNET_DEL, &pipe, sizeof pipe);
+                       if (i)
+                       {
+                               exitval = 1;
+                               warn("rule %u: setsockopt(%s)", do_pipe == 1 ? pipe.pipe_nr : pipe.fs.fs_nr,
+                                       "IP_DUMMYNET_DEL");
+                       }
+               }
+               else
+               #endif /* DUMMYNET */
+               {
+                       rule.fw_number = i;
+                       i = setsockopt(s, IPPROTO_IP, IP_FW_DEL, &rule, sizeof rule);
+                       if (i)
+                       {
+                               exitval = EX_UNAVAILABLE;
+                               warn("rule %u: setsockopt(%s)", rule.fw_number, "IP_FW_DEL");
+                       }
                }
        }
-       }
+
        if (exitval != EX_OK)
                exit(exitval);
 }
@@ -901,7 +1298,7 @@ verify_interface(union ip_fw_if *ifu)
         *      If a unit was specified, check for that exact interface.
         *      If a wildcard was specified, check for unit 0.
         */
-       snprintf(ifr.ifr_name, sizeof(ifr.ifr_name), "%s%d", 
+       snprintf(ifr.ifr_name, sizeof(ifr.ifr_name), "%s%d",
                         ifu->fu_via_if.name,
                         ifu->fu_via_if.unit == -1 ? 0 : ifu->fu_via_if.unit);
 
@@ -937,71 +1334,278 @@ fill_iface(char *which, union ip_fw_if *ifu, int *byname, int ac, char *arg)
                *byname = 0;
 }
 
+#ifdef DUMMYNET
 static void
 config_pipe(int ac, char **av)
 {
        struct dn_pipe pipe;
-        int i ;
-        char *end ;
+        int i;
+        char *end;
+
         memset(&pipe, 0, sizeof pipe);
+
         av++; ac--;
         /* Pipe number */
         if (ac && isdigit(**av)) {
-                pipe.pipe_nr = atoi(*av); av++; ac--;
+           i = atoi(*av); av++; ac--;
+           if (do_pipe == 1)
+               pipe.pipe_nr = i;
+           else
+               pipe.fs.fs_nr = i;
         }
         while (ac > 1) {
-            if (!strncmp(*av,"bw",strlen(*av)) ||
-                ! strncmp(*av,"bandwidth",strlen(*av))) {
-                pipe.bandwidth = strtoul(av[1], &end, 0);
-                if (*end == 'K')
-                        end++, pipe.bandwidth *= 1000 ;
-                else if (*end == 'M')
-                        end++, pipe.bandwidth *= 1000000 ;
-                if (*end == 'B')
-                        pipe.bandwidth *= 8 ;
-                av+=2; ac-=2;
-            } else if (!strncmp(*av,"delay",strlen(*av)) ) {
-                pipe.delay = strtoul(av[1], NULL, 0);
-                av+=2; ac-=2;
-            } else if (!strncmp(*av,"plr",strlen(*av)) ) {
-                
+            if (!strncmp(*av, "plr", strlen(*av))) {
+
                 double d = strtod(av[1], NULL);
-                pipe.plr = (int)(d*0x7fffffff) ;
+               if (d > 1)
+                   d = 1;
+               else if (d < 0)
+                   d = 0;
+                pipe.fs.plr = (int)(d*0x7fffffff);
                 av+=2; ac-=2;
-            } else if (!strncmp(*av,"queue",strlen(*av)) ) {
-                end = NULL ;
-                pipe.queue_size = strtoul(av[1], &end, 0);
-                if (*end == 'K') {
-                    pipe.queue_size_bytes = pipe.queue_size*1024 ;
-                    pipe.queue_size = 0 ;
-                } else if (*end == 'B') {
-                    pipe.queue_size_bytes = pipe.queue_size ;
-                    pipe.queue_size = 0 ;
+            } else if (!strncmp(*av, "queue", strlen(*av))) {
+                end = NULL;
+                pipe.fs.qsize = strtoul(av[1], &end, 0);
+                if (*end == 'K' || *end == 'k') {
+                   pipe.fs.flags_fs |= DN_QSIZE_IS_BYTES;
+                    pipe.fs.qsize *= 1024;
+                } else if (*end == 'B' || !strncmp(end, "by", 2)) {
+                   pipe.fs.flags_fs |= DN_QSIZE_IS_BYTES;
                 }
                 av+=2; ac-=2;
+           } else if (!strncmp(*av, "buckets", strlen(*av))) {
+               pipe.fs.rq_size = strtoul(av[1], NULL, 0);
+               av+=2; ac-=2;
+           } else if (!strncmp(*av, "mask", strlen(*av))) {
+                /* per-flow queue, mask is dst_ip, dst_port,
+                 * src_ip, src_port, proto measured in bits
+                 */
+                u_int32_t a;
+                u_int32_t *par = NULL;
+
+                pipe.fs.flow_mask.dst_ip = 0;
+                pipe.fs.flow_mask.src_ip = 0;
+                pipe.fs.flow_mask.dst_port = 0;
+                pipe.fs.flow_mask.src_port = 0;
+                pipe.fs.flow_mask.proto = 0;
+                end = NULL;
+                av++; ac--;
+                if (ac >= 1 && !strncmp(*av, "all", strlen(*av))) {
+                    /* special case -- all bits are significant */
+                    pipe.fs.flow_mask.dst_ip = ~0;
+                    pipe.fs.flow_mask.src_ip = ~0;
+                    pipe.fs.flow_mask.dst_port = ~0;
+                    pipe.fs.flow_mask.src_port = ~0;
+                    pipe.fs.flow_mask.proto = ~0;
+                    pipe.fs.flags_fs |= DN_HAVE_FLOW_MASK;
+                    av++; ac--;
+                } else {
+                  for (;;) {
+                    if (ac < 1)
+                        break;
+                    if (!strncmp(*av, "dst-ip", strlen(*av)))
+                        par = &(pipe.fs.flow_mask.dst_ip);
+                    else if (!strncmp(*av, "src-ip", strlen(*av)))
+                        par = &(pipe.fs.flow_mask.src_ip);
+                    else if (!strncmp(*av, "dst-port", strlen(*av)))
+                        (u_int16_t *)par = &(pipe.fs.flow_mask.dst_port);
+                    else if (!strncmp(*av, "src-port", strlen(*av)))
+                        (u_int16_t *)par = &(pipe.fs.flow_mask.src_port);
+                    else if (!strncmp(*av, "proto", strlen(*av)))
+                        (u_int8_t *)par = &(pipe.fs.flow_mask.proto);
+                    else
+                        break;
+                    if (ac < 2)
+                        show_usage("mask: %s value missing", *av);
+                    if (*av[1] == '/') {
+                        a = strtoul(av[1]+1, &end, 0);
+                        if (a == 32) /* special case... */
+                            a = ~0;
+                        else
+                            a = (1 << a) - 1;
+                        fprintf(stderr, " mask is 0x%08x\n", a);
+                    } else
+                        a = strtoul(av[1], &end, 0);
+                    if ((u_int16_t *)par == &(pipe.fs.flow_mask.src_port) ||
+                         (u_int16_t *)par == &(pipe.fs.flow_mask.dst_port)) {
+                        if (a >= (1<<16))
+                            show_usage("mask: %s must be 16 bit, not 0x%08x",
+                                *av, a);
+                        *((u_int16_t *)par) = (u_int16_t) a;
+                    } else if ((u_int8_t *)par == &(pipe.fs.flow_mask.proto)) {
+                        if (a >= (1<<8))
+                            show_usage("mask: %s must be 8 bit, not 0x%08x",
+                                *av, a);
+                        *((u_int8_t *)par) = (u_int8_t) a;
+                    } else
+                        *par = a;
+                    if (a != 0)
+                        pipe.fs.flags_fs |= DN_HAVE_FLOW_MASK;
+                    av += 2; ac -= 2;
+                  } /* end for */
+                }
+           } else if (!strncmp(*av, "red", strlen(*av)) ||
+                   !strncmp(*av, "gred", strlen(*av))) { /* RED enabled */
+               pipe.fs.flags_fs |= DN_IS_RED;
+               if (*av[0] == 'g')
+                   pipe.fs.flags_fs |= DN_IS_GENTLE_RED;
+               if ((end = strsep(&av[1],"/"))) {
+                   double w_q = strtod(end, NULL);
+                   if (w_q > 1 || w_q <= 0)
+                       show_usage("w_q %f must be 0 < x <= 1", w_q);
+                   pipe.fs.w_q = (int) (w_q * (1 << SCALE_RED));
+               }
+               if ((end = strsep(&av[1],"/"))) {
+                   pipe.fs.min_th = strtoul(end, &end, 0);
+                   if (*end == 'K' || *end == 'k')
+                       pipe.fs.min_th *= 1024;
+               }
+               if ((end = strsep(&av[1],"/"))) {
+                   pipe.fs.max_th = strtoul(end, &end, 0);
+                   if (*end == 'K' || *end == 'k')
+                       pipe.fs.max_th *= 1024;
+               }
+               if ((end = strsep(&av[1],"/"))) {
+                   double max_p = strtod(end, NULL);
+                   if (max_p > 1 || max_p <= 0)
+                       show_usage("max_p %f must be 0 < x <= 1", max_p);
+                   pipe.fs.max_p = (int) (max_p * (1 << SCALE_RED));
+               }
+               av+=2; ac-=2;
+           } else if (!strncmp(*av, "droptail", strlen(*av))) { /* DROPTAIL */
+               pipe.fs.flags_fs &= ~(DN_IS_RED|DN_IS_GENTLE_RED);
+               av+=1; ac-=1;
+            } else {
+               if (do_pipe == 1) {
+                   /* some commands are only good for pipes. */
+                   if (!strncmp(*av, "bw", strlen(*av)) ||
+                           ! strncmp(*av, "bandwidth", strlen(*av))) {
+                       if (av[1][0] >= 'a' && av[1][0] <= 'z') {
+                           int l = sizeof(pipe.if_name)-1;
+                           /* interface name */
+                           strncpy(pipe.if_name, av[1], l);
+                           pipe.if_name[l] = '\0';
+                           pipe.bandwidth = 0;
+                       } else {
+                           pipe.if_name[0] = '\0';
+                           pipe.bandwidth = strtoul(av[1], &end, 0);
+                           if (*end == 'K' || *end == 'k')
+                               end++, pipe.bandwidth *= 1000;
+                           else if (*end == 'M')
+                               end++, pipe.bandwidth *= 1000000;
+                           if (*end == 'B' || !strncmp(end, "by", 2))
+                               pipe.bandwidth *= 8;
+                       }
+                       av+=2; ac-=2;
+                   } else if (!strncmp(*av, "delay", strlen(*av))) {
+                       pipe.delay = strtoul(av[1], NULL, 0);
+                       av+=2; ac-=2;
+            } else
+                       show_usage("unrecognised pipe option ``%s''", *av);
+               } else { /* this refers to a queue */
+                   if (!strncmp(*av, "weight", strlen(*av))) {
+                       pipe.fs.weight = strtoul(av[1], &end, 0);
+                       av += 2;
+                       ac -= 2;
+                   } else if (!strncmp(*av, "pipe", strlen(*av))) {
+                       pipe.fs.parent_nr = strtoul(av[1], &end, 0);
+                       av += 2;
+                       ac -= 2;
             } else
                 show_usage("unrecognised option ``%s''", *av);
         }
-        if (pipe.pipe_nr == 0 )
-            show_usage("pipe_nr %d be > 0", pipe.pipe_nr);
-        if (pipe.queue_size > 100 )
-            show_usage("queue size %d must be 2 <= x <= 100", pipe.queue_size);
-        if (pipe.delay > 10000 )
+           }
+        }
+       if (do_pipe == 1) {
+        if (pipe.pipe_nr == 0)
+               show_usage("pipe_nr %d must be > 0", pipe.pipe_nr);
+        if (pipe.delay > 10000)
             show_usage("delay %d must be < 10000", pipe.delay);
+       } else { /* do_pipe == 2, queue */
+           if (pipe.fs.parent_nr == 0)
+               show_usage("pipe %d must be > 0", pipe.fs.parent_nr);
+           if (pipe.fs.weight >100)
+               show_usage("weight %d must be <= 100", pipe.fs.weight);
+       }
+       if (pipe.fs.flags_fs & DN_QSIZE_IS_BYTES) {
+           if (pipe.fs.qsize > 1024*1024)
+               show_usage("queue size %d, must be < 1MB",
+                   pipe.fs.qsize);
+       } else {
+           if (pipe.fs.qsize > 100)
+               show_usage("queue size %d, must be 2 <= x <= 100",
+                   pipe.fs.qsize);
+       }
+       if (pipe.fs.flags_fs & DN_IS_RED) {
+           if (pipe.fs.min_th >= pipe.fs.max_th)
+               show_usage("min_th %d must be < than max_th %d",
+                       pipe.fs.min_th, pipe.fs.max_th);
+           if (pipe.fs.max_th == 0)
+               show_usage("max_th must be > 0");
+           if (pipe.bandwidth) {
+               size_t len;
+               int lookup_depth, avg_pkt_size;
+               double s, idle, weight, w_q;
+               struct clockinfo clock;
+               int t;
+
+               len = sizeof(int);
+               if (sysctlbyname("net.inet.ip.dummynet.red_lookup_depth",
+                           &lookup_depth, &len, NULL, 0) == -1)
+
+                   errx(1, "sysctlbyname(\"%s\")",
+                           "net.inet.ip.dummynet.red_lookup_depth");
+               if (lookup_depth == 0)
+                   show_usage("net.inet.ip.dummynet.red_lookup_depth must"
+                           "greater than zero");
+
+               len = sizeof(int);
+               if (sysctlbyname("net.inet.ip.dummynet.red_avg_pkt_size",
+                           &avg_pkt_size, &len, NULL, 0) == -1)
+
+                   errx(1, "sysctlbyname(\"%s\")",
+                           "net.inet.ip.dummynet.red_avg_pkt_size");
+               if (avg_pkt_size == 0)
+                   show_usage("net.inet.ip.dummynet.red_avg_pkt_size must"
+                               "greater than zero");
+
+               len = sizeof(struct clockinfo);
+               if (sysctlbyname("kern.clockrate",
+                       &clock, &len, NULL, 0) == -1)
+                   errx(1, "sysctlbyname(\"%s\")", "kern.clockrate");
+
+               /* ticks needed for sending a medium-sized packet */
+               s = clock.hz * avg_pkt_size * 8 / pipe.bandwidth;
+
+               /*
+                * max idle time (in ticks) before avg queue size becomes 0.
+                * NOTA:  (3/w_q) is approx the value x so that
+                * (1-w_q)^x < 10^-3.
+                */
+               w_q = ((double) pipe.fs.w_q) / (1 << SCALE_RED);
+               idle = s * 3. / w_q;
+               pipe.fs.lookup_step = (int) idle / lookup_depth;
+               if (!pipe.fs.lookup_step)
+                   pipe.fs.lookup_step = 1;
+               weight = 1 - w_q;
+               for (t = pipe.fs.lookup_step; t > 0; --t)
+                   weight *= weight;
+               pipe.fs.lookup_weight = (int) (weight * (1 << SCALE_RED));
+           }
+       }
 #if 0
         printf("configuring pipe %d bw %d delay %d size %d\n",
                 pipe.pipe_nr, pipe.bandwidth, pipe.delay, pipe.queue_size);
 #endif
-        i = setsockopt(s,IPPROTO_IP, IP_DUMMYNET_CONFIGURE, &pipe,sizeof pipe);
+        i = setsockopt(s,IPPROTO_IP, IP_DUMMYNET_CONFIGURE, &pipe, sizeof pipe);
         if (i)
                 err(1, "setsockopt(%s)", "IP_DUMMYNET_CONFIGURE");
-                
+
 }
+#endif /* DUMMYNET */
 
 static void
-add(ac,av)
+add(ac, av)
        int ac;
        char **av;
 {
@@ -1010,8 +1614,9 @@ add(ac,av)
        u_char proto;
        struct protoent *pe;
        int saw_xmrc = 0, saw_via = 0;
-       
+
        memset(&rule, 0, sizeof rule);
+       rule.version = IP_FW_CURRENT_API_VERSION;
 
        av++; ac--;
 
@@ -1021,21 +1626,42 @@ add(ac,av)
        }
 
        /* Action */
+       if (ac > 1 && !strncmp(*av, "prob", strlen(*av))) {
+               double d = strtod(av[1], NULL);
+               if (d <= 0 || d > 1)
+                       show_usage("illegal match prob. %s", av[1]);
+               if (d != 1) { /* 1 means always match */
+                       rule.fw_flg |= IP_FW_F_RND_MATCH;
+                       /* we really store dont_match probability */
+                       (long)rule.pipe_ptr = (long)((1 - d) * 0x7fffffff);
+               }
+               av += 2; ac -= 2;
+       }
+
        if (ac == 0)
                show_usage("missing action");
-       if (!strncmp(*av,"accept",strlen(*av))
-                   || !strncmp(*av,"pass",strlen(*av))
-                   || !strncmp(*av,"allow",strlen(*av))
-                   || !strncmp(*av,"permit",strlen(*av))) {
+       if (!strncmp(*av, "accept", strlen(*av))
+                   || !strncmp(*av, "pass" ,strlen(*av))
+                   || !strncmp(*av, "allow", strlen(*av))
+                   || !strncmp(*av, "permit", strlen(*av))) {
                rule.fw_flg |= IP_FW_F_ACCEPT; av++; ac--;
-       } else if (!strncmp(*av,"count",strlen(*av))) {
+       } else if (!strncmp(*av, "count", strlen(*av))) {
                rule.fw_flg |= IP_FW_F_COUNT; av++; ac--;
-        } else if (!strncmp(*av,"pipe",strlen(*av))) {
+        }
+               #ifdef DUMMYNET
+        else if (!strncmp(*av, "pipe", strlen(*av))) {
                 rule.fw_flg |= IP_FW_F_PIPE; av++; ac--;
                 if (!ac)
                         show_usage("missing pipe number");
                 rule.fw_divert_port = strtoul(*av, NULL, 0); av++; ac--;
-       } else if (!strncmp(*av,"divert",strlen(*av))) {
+        } else if (!strncmp(*av, "queue", strlen(*av))) {
+                rule.fw_flg |= IP_FW_F_QUEUE; av++; ac--;
+                if (!ac)
+                        show_usage("missing queue number");
+                rule.fw_divert_port = strtoul(*av, NULL, 0); av++; ac--;
+       }
+       #endif /* DUMMYNET */
+       else if (!strncmp(*av, "divert", strlen(*av))) {
                rule.fw_flg |= IP_FW_F_DIVERT; av++; ac--;
                if (!ac)
                        show_usage("missing %s port", "divert");
@@ -1049,7 +1675,7 @@ add(ac,av)
                        else
                                show_usage("illegal %s port", "divert");
                }
-       } else if (!strncmp(*av,"tee",strlen(*av))) {
+       } else if (!strncmp(*av, "tee", strlen(*av))) {
                rule.fw_flg |= IP_FW_F_TEE; av++; ac--;
                if (!ac)
                        show_usage("missing %s port", "tee divert");
@@ -1063,11 +1689,8 @@ add(ac,av)
                        else
                                show_usage("illegal %s port", "tee divert");
                }
-#ifndef IPFW_TEE_IS_FINALLY_IMPLEMENTED
-               err(EX_USAGE, "the ``tee'' action is not implemented");
-#endif
-       } else if (!strncmp(*av,"fwd",strlen(*av)) ||
-                  !strncmp(*av,"forward",strlen(*av))) {
+       } else if (!strncmp(*av, "fwd", strlen(*av)) ||
+                  !strncmp(*av, "forward", strlen(*av))) {
                struct in_addr dummyip;
                char *pp;
                rule.fw_flg |= IP_FW_F_FWD; av++; ac--;
@@ -1082,46 +1705,64 @@ add(ac,av)
                if(pp != NULL)
                {
                        *(pp++) = '\0';
-                       rule.fw_fwd_ip.sin_port = lookup_port(pp, 1, 1);
-                       if(rule.fw_fwd_ip.sin_port == (unsigned int)-1)
-                               show_usage("illegal forwarding port");
+                       i = lookup_port(pp, 0, 1, 0);
+                       if (i == -1)
+                               show_usage("illegal forwarding port ``%s''", pp);
+                       else
+                               rule.fw_fwd_ip.sin_port = (u_short)i;
                }
                fill_ip(&(rule.fw_fwd_ip.sin_addr), &dummyip, &ac, &av);
                if (rule.fw_fwd_ip.sin_addr.s_addr == 0)
                        show_usage("illegal forwarding IP address");
 
-       } else if (!strncmp(*av,"skipto",strlen(*av))) {
+       } else if (!strncmp(*av, "skipto", strlen(*av))) {
                rule.fw_flg |= IP_FW_F_SKIPTO; av++; ac--;
                if (!ac)
                        show_usage("missing skipto rule number");
                rule.fw_skipto_rule = strtoul(*av, NULL, 0); av++; ac--;
-       } else if ((!strncmp(*av,"deny",strlen(*av))
-                   || !strncmp(*av,"drop",strlen(*av)))) {
+       } else if ((!strncmp(*av, "deny", strlen(*av))
+                   || !strncmp(*av, "drop", strlen(*av)))) {
                rule.fw_flg |= IP_FW_F_DENY; av++; ac--;
-       } else if (!strncmp(*av,"reject",strlen(*av))) {
+       } else if (!strncmp(*av, "reject", strlen(*av))) {
                rule.fw_flg |= IP_FW_F_REJECT; av++; ac--;
                rule.fw_reject_code = ICMP_UNREACH_HOST;
-       } else if (!strncmp(*av,"reset",strlen(*av))) {
+       } else if (!strncmp(*av, "reset", strlen(*av))) {
                rule.fw_flg |= IP_FW_F_REJECT; av++; ac--;
                rule.fw_reject_code = IP_FW_REJECT_RST; /* check TCP later */
-       } else if (!strncmp(*av,"unreach",strlen(*av))) {
+       } else if (!strncmp(*av, "unreach", strlen(*av))) {
                rule.fw_flg |= IP_FW_F_REJECT; av++; ac--;
                fill_reject_code(&rule.fw_reject_code, *av); av++; ac--;
+       } else if (!strncmp(*av, "check-state", strlen(*av))) {
+               rule.fw_flg |= IP_FW_F_CHECK_S; av++; ac--;
+               goto done;
        } else {
                show_usage("invalid action ``%s''", *av);
        }
 
        /* [log] */
-       if (ac && !strncmp(*av,"log",strlen(*av))) {
+       if (ac && !strncmp(*av, "log", strlen(*av))) {
                rule.fw_flg |= IP_FW_F_PRN; av++; ac--;
        }
+       if (ac && !strncmp(*av, "logamount", strlen(*av))) {
+               if (!(rule.fw_flg & IP_FW_F_PRN))
+                       show_usage("``logamount'' not valid without ``log''");
+               ac--; av++;
+               if (!ac)
+                       show_usage("``logamount'' requires argument");
+               rule.fw_logamount = atoi(*av);
+               if (rule.fw_logamount < 0)
+                       show_usage("``logamount'' argument must be positive");
+               if (rule.fw_logamount == 0)
+                       rule.fw_logamount = -1;
+               ac--; av++;
+       }
 
        /* protocol */
        if (ac == 0)
                show_usage("missing protocol");
        if ((proto = atoi(*av)) > 0) {
                rule.fw_prot = proto; av++; ac--;
-       } else if (!strncmp(*av,"all",strlen(*av))) {
+       } else if (!strncmp(*av, "all", strlen(*av))) {
                rule.fw_prot = IPPROTO_IP; av++; ac--;
        } else if ((pe = getprotobyname(*av)) != NULL) {
                rule.fw_prot = pe->p_proto; av++; ac--;
@@ -1135,48 +1776,66 @@ add(ac,av)
                show_usage("``reset'' is only valid for tcp packets");
 
        /* from */
-       if (ac && !strncmp(*av,"from",strlen(*av))) { av++; ac--; }
+       if (ac && !strncmp(*av, "from", strlen(*av))) { av++; ac--; }
        else
                show_usage("missing ``from''");
 
-       if (ac && !strncmp(*av,"not",strlen(*av))) {
+       if (ac && !strncmp(*av, "not", strlen(*av))) {
                rule.fw_flg |= IP_FW_F_INVSRC;
                av++; ac--;
        }
        if (!ac)
                show_usage("missing arguments");
 
-       fill_ip(&rule.fw_src, &rule.fw_smsk, &ac, &av);
+       if (ac && !strncmp(*av, "me", strlen(*av))) {
+               rule.fw_flg |= IP_FW_F_SME;
+               av++; ac--;
+       } else {
+               fill_ip(&rule.fw_src, &rule.fw_smsk, &ac, &av);
+       }
 
-       if (ac && (isdigit(**av) || lookup_port(*av, 1, 1) >= 0)) {
+       if (ac && (isdigit(**av) || lookup_port(*av, rule.fw_prot, 1, 1) >= 0)) {
                u_short nports = 0;
+               int retval;
 
-               if (fill_port(&nports, rule.fw_uar.fw_pts, 0, *av))
+               retval = fill_port(&nports, rule.fw_uar.fw_pts, 0, *av, rule.fw_prot);
+               if (retval == 1)
                        rule.fw_flg |= IP_FW_F_SRNG;
+               else if (retval == 2)
+                       rule.fw_flg |= IP_FW_F_SMSK;
                IP_FW_SETNSRCP(&rule, nports);
                av++; ac--;
        }
 
        /* to */
-       if (ac && !strncmp(*av,"to",strlen(*av))) { av++; ac--; }
+       if (ac && !strncmp(*av, "to", strlen(*av))) { av++; ac--; }
        else
                show_usage("missing ``to''");
 
-       if (ac && !strncmp(*av,"not",strlen(*av))) {
+       if (ac && !strncmp(*av, "not", strlen(*av))) {
                rule.fw_flg |= IP_FW_F_INVDST;
                av++; ac--;
        }
        if (!ac)
                show_usage("missing arguments");
 
-       fill_ip(&rule.fw_dst, &rule.fw_dmsk, &ac, &av);
+       if (ac && !strncmp(*av, "me", strlen(*av))) {
+               rule.fw_flg |= IP_FW_F_DME;
+               av++; ac--;
+       } else {
+               fill_ip(&rule.fw_dst, &rule.fw_dmsk, &ac, &av);
+       }
 
-       if (ac && (isdigit(**av) || lookup_port(*av, 1, 1) >= 0)) {
+       if (ac && (isdigit(**av) || lookup_port(*av, rule.fw_prot, 1, 1) >= 0)) {
                u_short nports = 0;
+               int retval;
 
-               if (fill_port(&nports,
-                   rule.fw_uar.fw_pts, IP_FW_GETNSRCP(&rule), *av))
+               retval = fill_port(&nports,
+                   rule.fw_uar.fw_pts, IP_FW_GETNSRCP(&rule), *av, rule.fw_prot);
+               if (retval == 1)
                        rule.fw_flg |= IP_FW_F_DRNG;
+               else if (retval == 2)
+                       rule.fw_flg |= IP_FW_F_DMSK;
                IP_FW_SETNDSTP(&rule, nports);
                av++; ac--;
        }
@@ -1188,15 +1847,51 @@ add(ac,av)
        }
 
        while (ac) {
-               if (!strncmp(*av,"in",strlen(*av))) { 
+               if (!strncmp(*av, "uid", strlen(*av))) {
+                       struct passwd *pwd;
+                       char *end;
+                       uid_t uid;
+
+                       rule.fw_flg |= IP_FW_F_UID;
+                       ac--; av++;
+                       if (!ac)
+                               show_usage("``uid'' requires argument");
+
+                       uid = strtoul(*av, &end, 0);
+                       if (*end == '\0')
+                               pwd = getpwuid(uid);
+                       else
+                               pwd = getpwnam(*av);
+                       if (pwd == NULL)
+                               show_usage("uid \"%s\" is nonexistant", *av);
+                       rule.fw_uid = pwd->pw_uid;
+                       ac--; av++;
+                       continue;
+               }
+               if (!strncmp(*av, "in", strlen(*av))) {
                        rule.fw_flg |= IP_FW_F_IN;
                        av++; ac--; continue;
                }
-               if (!strncmp(*av,"out",strlen(*av))) { 
+                if (!strncmp(*av, "keep-state", strlen(*av))) {
+                        u_long type;
+                        rule.fw_flg |= IP_FW_F_KEEP_S;
+
+                        av++; ac--;
+                        if (ac > 0 && (type = atoi(*av)) != 0) {
+                            (int)rule.next_rule_ptr = type;
+                            av++; ac--;
+                        }
+                        continue;
+                }
+                if (!strncmp(*av, "bridged", strlen(*av))) {
+                        rule.fw_flg |= IP_FW_BRIDGED;
+                        av++; ac--; continue;
+                }
+               if (!strncmp(*av, "out", strlen(*av))) {
                        rule.fw_flg |= IP_FW_F_OUT;
                        av++; ac--; continue;
                }
-               if (ac && !strncmp(*av,"xmit",strlen(*av))) {
+               if (ac && !strncmp(*av, "xmit", strlen(*av))) {
                        union ip_fw_if ifu;
                        int byname;
 
@@ -1206,7 +1901,7 @@ badviacombo:
                                    " with ``xmit'' and ``recv''");
                        }
                        saw_xmrc = 1;
-                       av++; ac--; 
+                       av++; ac--;
                        fill_iface("xmit", &ifu, &byname, ac, *av);
                        rule.fw_out_if = ifu;
                        rule.fw_flg |= IP_FW_F_OIFACE;
@@ -1214,14 +1909,14 @@ badviacombo:
                                rule.fw_flg |= IP_FW_F_OIFNAME;
                        av++; ac--; continue;
                }
-               if (ac && !strncmp(*av,"recv",strlen(*av))) {
+               if (ac && !strncmp(*av, "recv", strlen(*av))) {
                        union ip_fw_if ifu;
                        int byname;
 
                        if (saw_via)
                                goto badviacombo;
                        saw_xmrc = 1;
-                       av++; ac--; 
+                       av++; ac--;
                        fill_iface("recv", &ifu, &byname, ac, *av);
                        rule.fw_in_if = ifu;
                        rule.fw_flg |= IP_FW_F_IIFACE;
@@ -1229,14 +1924,14 @@ badviacombo:
                                rule.fw_flg |= IP_FW_F_IIFNAME;
                        av++; ac--; continue;
                }
-               if (ac && !strncmp(*av,"via",strlen(*av))) {
+               if (ac && !strncmp(*av, "via", strlen(*av))) {
                        union ip_fw_if ifu;
                        int byname = 0;
 
                        if (saw_xmrc)
                                goto badviacombo;
                        saw_via = 1;
-                       av++; ac--; 
+                       av++; ac--;
                        fill_iface("via", &ifu, &byname, ac, *av);
                        rule.fw_out_if = rule.fw_in_if = ifu;
                        if (byname)
@@ -1244,12 +1939,12 @@ badviacombo:
                                    (IP_FW_F_IIFNAME | IP_FW_F_OIFNAME);
                        av++; ac--; continue;
                }
-               if (!strncmp(*av,"fragment",strlen(*av))) {
+               if (!strncmp(*av, "fragment", strlen(*av))) {
                        rule.fw_flg |= IP_FW_F_FRAG;
                        av++; ac--; continue;
                }
-               if (!strncmp(*av,"ipoptions",strlen(*av))) { 
-                       av++; ac--; 
+               if (!strncmp(*av, "ipoptions", strlen(*av))) {
+                       av++; ac--;
                        if (!ac)
                                show_usage("missing argument"
                                    " for ``ipoptions''");
@@ -1257,26 +1952,36 @@ badviacombo:
                        av++; ac--; continue;
                }
                if (rule.fw_prot == IPPROTO_TCP) {
-                       if (!strncmp(*av,"established",strlen(*av))) { 
-                               rule.fw_tcpf  |= IP_FW_TCPF_ESTAB;
+                       if (!strncmp(*av, "established", strlen(*av))) {
+                               rule.fw_ipflg |= IP_FW_IF_TCPEST;
                                av++; ac--; continue;
                        }
-                       if (!strncmp(*av,"setup",strlen(*av))) { 
+                       if (!strncmp(*av, "setup", strlen(*av))) {
                                rule.fw_tcpf  |= IP_FW_TCPF_SYN;
                                rule.fw_tcpnf  |= IP_FW_TCPF_ACK;
                                av++; ac--; continue;
                        }
-                       if (!strncmp(*av,"tcpflags",strlen(*av))) { 
-                               av++; ac--; 
+                       if (!strncmp(*av, "tcpflags", strlen(*av)) ||
+                           !strncmp(*av, "tcpflgs", strlen(*av))) {
+                               av++; ac--;
                                if (!ac)
                                        show_usage("missing argument"
                                            " for ``tcpflags''");
                                fill_tcpflag(&rule.fw_tcpf, &rule.fw_tcpnf, av);
                                av++; ac--; continue;
                        }
+                       if (!strncmp(*av, "tcpoptions", strlen(*av)) ||
+                           !strncmp(*av, "tcpopts", strlen(*av))) {
+                               av++; ac--;
+                               if (!ac)
+                                       show_usage("missing argument"
+                                           " for ``tcpoptions''");
+                               fill_tcpopts(&rule.fw_tcpopt, &rule.fw_tcpnopt, av);
+                               av++; ac--; continue;
+                       }
                }
                if (rule.fw_prot == IPPROTO_ICMP) {
-                       if (!strncmp(*av,"icmptypes",strlen(*av))) {
+                       if (!strncmp(*av, "icmptypes", strlen(*av))) {
                                av++; ac--;
                                if (!ac)
                                        show_usage("missing argument"
@@ -1310,12 +2015,24 @@ badviacombo:
                if (rule.fw_nports)
                        show_usage("can't mix 'frag' and port specifications");
        }
-
+       if (rule.fw_flg & IP_FW_F_PRN) {
+               if (!rule.fw_logamount) {
+                       size_t len = sizeof(int);
+
+                       if (sysctlbyname("net.inet.ip.fw.verbose_limit",
+                           &rule.fw_logamount, &len, NULL, 0) == -1)
+                               errx(1, "sysctlbyname(\"%s\")",
+                                   "net.inet.ip.fw.verbose_limit");
+               } else if (rule.fw_logamount == -1)
+                       rule.fw_logamount = 0;
+               rule.fw_loghighest = rule.fw_logamount;
+       }
+done:
+       i = sizeof(rule);
+       if (getsockopt(s, IPPROTO_IP, IP_FW_ADD, &rule, &i) == -1)
+               err(EX_UNAVAILABLE, "getsockopt(%s)", "IP_FW_ADD");
        if (!do_quiet)
                show_ipfw(&rule, 10, 10);
-       i = setsockopt(s, IPPROTO_IP, IP_FW_ADD, &rule, sizeof rule);
-       if (i)
-               err(EX_UNAVAILABLE, "setsockopt(%s)", "IP_FW_ADD");
 }
 
 static void
@@ -1323,19 +2040,21 @@ zero (ac, av)
        int ac;
        char **av;
 {
+       struct ip_fw rule;
+       memset(&rule, 0, sizeof rule);
+       rule.version = IP_FW_CURRENT_API_VERSION;
+
        av++; ac--;
 
        if (!ac) {
                /* clear all entries */
-               if (setsockopt(s,IPPROTO_IP,IP_FW_ZERO,NULL,0)<0)
+               if (setsockopt(s, IPPROTO_IP, IP_FW_ZERO, &rule, sizeof rule) < 0)
                        err(EX_UNAVAILABLE, "setsockopt(%s)", "IP_FW_ZERO");
                if (!do_quiet)
                        printf("Accounting cleared.\n");
        } else {
-               struct ip_fw rule;
                int failed = EX_OK;
 
-               memset(&rule, 0, sizeof rule);
                while (ac) {
                        /* Rule number */
                        if (isdigit(**av)) {
@@ -1357,16 +2076,56 @@ zero (ac, av)
        }
 }
 
+static void
+resetlog (ac, av)
+       int ac;
+       char **av;
+{
+       struct ip_fw rule;
+       memset(&rule, 0, sizeof rule);
+       rule.version = IP_FW_CURRENT_API_VERSION;
+
+       av++; ac--;
+
+       if (!ac) {
+               /* clear all entries */
+               if (setsockopt(s, IPPROTO_IP, IP_FW_RESETLOG, &rule, sizeof rule) < 0)
+                       err(EX_UNAVAILABLE, "setsockopt(%s)", "IP_FW_RESETLOG");
+               if (!do_quiet)
+                       printf("Logging counts reset.\n");
+       } else {
+               int failed = EX_OK;
+
+               while (ac) {
+                       /* Rule number */
+                       if (isdigit(**av)) {
+                               rule.fw_number = atoi(*av); av++; ac--;
+                               if (setsockopt(s, IPPROTO_IP,
+                                   IP_FW_RESETLOG, &rule, sizeof rule)) {
+                                       warn("rule %u: setsockopt(%s)", rule.fw_number,
+                                                "IP_FW_RESETLOG");
+                                       failed = EX_UNAVAILABLE;
+                               }
+                               else if (!do_quiet)
+                                       printf("Entry %d logging count reset\n",
+                                           rule.fw_number);
+                       } else
+                               show_usage("invalid rule number ``%s''", *av);
+               }
+               if (failed != EX_OK)
+                       exit(failed);
+       }
+}
+
 static int
-ipfw_main(ac,av)
+ipfw_main(ac, av)
        int     ac;
        char    **av;
 {
 
        int             ch;
-       extern int      optreset; /* XXX should be declared in <unistd.h> */
 
-       if ( ac == 1 ) {
+       if (ac == 1) {
                show_usage(NULL);
        }
 
@@ -1374,22 +2133,28 @@ ipfw_main(ac,av)
        do_force = !isatty(STDIN_FILENO);
 
        optind = optreset = 1;
-       while ((ch = getopt(ac, av, "afqtN")) != -1)
+       while ((ch = getopt(ac, av, "s:afqtvN")) != -1)
        switch(ch) {
+               case 's': /* sort */
+                       do_sort= atoi(optarg);
+                       break;
                case 'a':
-                       do_acct=1;
+                       do_acct = 1;
                        break;
                case 'f':
-                       do_force=1;
+                       do_force = 1;
                        break;
                case 'q':
-                       do_quiet=1;
+                       do_quiet = 1;
                        break;
                case 't':
-                       do_time=1;
+                       do_time = 1;
+                       break;
+               case 'v': /* verbose */
+                       verbose++;
                        break;
                case 'N':
-                       do_resolv=1;
+                       do_resolv = 1;
                        break;
                default:
                        show_usage(NULL);
@@ -1397,72 +2162,111 @@ ipfw_main(ac,av)
 
        ac -= optind;
        if (*(av+=optind)==NULL) {
-                show_usage("Bad arguments");
+                show_usage("bad arguments");
        }
 
-        if (!strncmp(*av, "pipe", strlen(*av))) {
-                do_pipe = 1 ;
-                ac-- ;
-                av++ ;
-        }
+       #ifdef DUMMYNET
+       if (!strncmp(*av, "pipe", strlen(*av))) {
+               do_pipe = 1;
+               ac--;
+               av++;
+       } else if (!strncmp(*av, "queue", strlen(*av))) {
+               do_pipe = 2;
+               ac--;
+               av++;
+       }
        if (!ac) {
                show_usage("pipe requires arguments");
        }
-        /* allow argument swapping */
-        if (ac > 1 && *av[0]>='0' && *av[0]<='9') {
-                char *p = av[0] ;
-                av[0] = av[1] ;
-                av[1] = p ;
-        }
+       #endif /* DUMMYNET */
+
+       /* allow argument swapping */
+       if (ac > 1 && *av[0] >= '0' && *av[0] <= '9') {
+               char *p = av[0];
+               av[0] = av[1];
+               av[1] = p;
+       }
        if (!strncmp(*av, "add", strlen(*av))) {
-               add(ac,av);
-        } else if (do_pipe && !strncmp(*av, "config", strlen(*av))) {
-                config_pipe(ac,av);
-       } else if (!strncmp(*av, "delete", strlen(*av))) {
-               delete(ac,av);
+               add(ac, av);
+       }
+       #ifdef DUMMYNET
+       else if (do_pipe && !strncmp(*av, "config", strlen(*av))) {
+               config_pipe(ac, av);
+       }
+       #endif /* DUMMYNET */
+       else if (!strncmp(*av, "delete", strlen(*av))) {
+               delete(ac, av);
        } else if (!strncmp(*av, "flush", strlen(*av))) {
                int do_flush = 0;
 
-               if ( do_force || do_quiet )
+               if (do_force || do_quiet)
                        do_flush = 1;
                else {
                        int c;
 
                        /* Ask the user */
                        printf("Are you sure? [yn] ");
+                       fflush(stdout);
                        do {
-                               fflush(stdout);
                                c = toupper(getc(stdin));
                                while (c != '\n' && getc(stdin) != '\n')
                                        if (feof(stdin))
                                                return (0);
                        } while (c != 'Y' && c != 'N');
                        printf("\n");
-                       if (c == 'Y') 
+                       if (c == 'Y')
                                do_flush = 1;
                }
-               if ( do_flush ) {
-                       if (setsockopt(s,IPPROTO_IP,IP_FW_FLUSH,NULL,0) < 0)
-                               err(EX_UNAVAILABLE, "setsockopt(%s)", "IP_FW_FLUSH");
+               if (do_flush) {
+                       int error = 0;
+
+                       #ifdef DUMMYNET
+                       if (do_pipe)
+                       {
+                               error = setsockopt(s, IPPROTO_IP, IP_DUMMYNET_FLUSH, NULL, 0);
+                       }
+                       else
+                       #endif /* DUMMYNET */
+                       {
+                               struct ip_fw rule;
+                               memset(&rule, 0, sizeof rule);
+                               rule.version = IP_FW_CURRENT_API_VERSION;
+                               error = setsockopt(s, IPPROTO_IP, IP_FW_FLUSH, &rule, sizeof rule);
+                       }
+
+                       if (error < 0)
+                       #ifdef DUMMYNET
+                               err(EX_UNAVAILABLE, "setsockopt(IP_%s_FLUSH)",
+                                       do_pipe ? "DUMMYNET" : "FW");
+                       #else /* DUMMYNET */
+                               err(EX_UNAVAILABLE, "setsockopt(IP_FW_FLUSH)");
+                       #endif /* DUMMYNET */
                        if (!do_quiet)
+                       #ifdef DUMMYNET
+                               printf("Flushed all %s.\n",
+                                   do_pipe ? "pipes" : "rules");
+                       #else /* DUMMYNET */
                                printf("Flushed all rules.\n");
+                       #endif /* DUMMYNET */
                }
        } else if (!strncmp(*av, "zero", strlen(*av))) {
-               zero(ac,av);
+               zero(ac, av);
+       } else if (!strncmp(*av, "resetlog", strlen(*av))) {
+               resetlog(ac, av);
        } else if (!strncmp(*av, "print", strlen(*av))) {
-               list(--ac,++av);
+               list(--ac, ++av);
        } else if (!strncmp(*av, "list", strlen(*av))) {
-               list(--ac,++av);
+               list(--ac, ++av);
        } else if (!strncmp(*av, "show", strlen(*av))) {
                do_acct++;
-               list(--ac,++av);
+               list(--ac, ++av);
        } else {
-               show_usage("Bad arguments");
+               show_usage("bad arguments");
        }
        return 0;
 }
 
-int 
+int
 main(ac, av)
        int     ac;
        char    **av;
@@ -1472,17 +2276,23 @@ main(ac, av)
        char    buf[BUFSIZ];
        char    *a, *p, *args[MAX_ARGS], *cmd = NULL;
        char    linename[10];
-       int     i, c, qflag, pflag, status;
+       int     i, c, lineno, qflag, pflag, status;
        FILE    *f = NULL;
        pid_t   preproc = 0;
 
-       s = socket( AF_INET, SOCK_RAW, IPPROTO_RAW );
-       if ( s < 0 )
+       s = socket(AF_INET, SOCK_RAW, IPPROTO_RAW);
+       if (s < 0)
                err(EX_UNAVAILABLE, "socket");
 
-       setbuf(stdout,0);
+       setbuf(stdout, 0);
 
-       if (ac > 1 && access(av[ac - 1], R_OK) == 0) {
+       /*
+        * this is a nasty check on the last argument!!!
+        * If there happens to be a filename matching a keyword in the current
+        * directory, things will fail miserably.
+        */
+
+       if (ac > 1 && av[ac - 1][0] == '/' && access(av[ac - 1], R_OK) == 0) {
                qflag = pflag = i = 0;
                lineno = 0;
 
@@ -1546,8 +2356,8 @@ main(ac, av)
 
                        case 0:
                                /* child */
-                               if (dup2(fileno(f), 0) == -1 ||
-                                   dup2(pipedes[1], 1) == -1)
+                               if (dup2(fileno(f), 0) == -1
+                                   || dup2(pipedes[1], 1) == -1)
                                        err(EX_OSERR, "dup2()");
                                fclose(f);
                                close(pipedes[1]);
@@ -1578,36 +2388,36 @@ main(ac, av)
                                continue;
                        if ((p = strchr(buf, '#')) != NULL)
                                *p = '\0';
-                       i=1;
-                       if (qflag) args[i++]="-q";
+                       i = 1;
+                       if (qflag)
+                               args[i++] = "-q";
                        for (a = strtok(buf, WHITESP);
                            a && i < MAX_ARGS; a = strtok(NULL, WHITESP), i++)
                                args[i] = a;
                        if (i == (qflag? 2: 1))
                                continue;
                        if (i == MAX_ARGS)
-                               errx(EX_USAGE, "%s: too many arguments", linename);
+                               errx(EX_USAGE, "%s: too many arguments",
+                                   linename);
                        args[i] = NULL;
 
-                       ipfw_main(i, args); 
+                       ipfw_main(i, args);
                }
                fclose(f);
                if (pflag) {
-                       if (waitpid(preproc, &status, 0) != -1) {
-                               if (WIFEXITED(status)) {
-                                       if (WEXITSTATUS(status) != EX_OK)
-                                               errx(EX_UNAVAILABLE,
-                                                    "preprocessor exited with status %d",
-                                                    WEXITSTATUS(status));
-                               } else if (WIFSIGNALED(status)) {
-                                       errx(EX_UNAVAILABLE,
-                                            "preprocessor exited with signal %d",
-                                            WTERMSIG(status));
-                               }
-                       }
+                       if (waitpid(preproc, &status, 0) == -1)
+                               errx(EX_OSERR, "waitpid()");
+                       if (WIFEXITED(status) && WEXITSTATUS(status) != EX_OK)
+                               errx(EX_UNAVAILABLE,
+                                   "preprocessor exited with status %d",
+                                   WEXITSTATUS(status));
+                       else if (WIFSIGNALED(status))
+                               errx(EX_UNAVAILABLE,
+                                   "preprocessor exited with signal %d",
+                                   WTERMSIG(status));
                }
-
-       } else
-               ipfw_main(ac,av);
+       } else {
+               ipfw_main(ac, av);
+       }
        return EX_OK;
 }