Let ipfw_chk() return IP_FW_{PASS,DENY,DUMMYNET,TEE,DIVERT} and the caller
authorSepherosa Ziehau <sephe@dragonflybsd.org>
Sun, 7 Sep 2008 10:03:45 +0000 (10:03 +0000)
committerSepherosa Ziehau <sephe@dragonflybsd.org>
Sun, 7 Sep 2008 10:03:45 +0000 (10:03 +0000)
proceeds according to the return value in well strutured switch block.  The
additional information related to the return value (e.g. pipe/queue number
of IP_FW_DUMMYNET) is saved in ip_fw_args.cookie.

Idea-from: FreeBSD

sys/net/if_ethersubr.c
sys/net/ipfw/ip_fw2.c
sys/net/ipfw/ip_fw2.h
sys/netinet/ip_input.c
sys/netinet/ip_output.c

index a08e550..0d06956 100644 (file)
@@ -32,7 +32,7 @@
  *
  *     @(#)if_ethersubr.c      8.1 (Berkeley) 6/10/93
  * $FreeBSD: src/sys/net/if_ethersubr.c,v 1.70.2.33 2003/04/28 15:45:53 archie Exp $
- * $DragonFly: src/sys/net/if_ethersubr.c,v 1.85 2008/08/23 08:26:04 sephe Exp $
+ * $DragonFly: src/sys/net/if_ethersubr.c,v 1.86 2008/09/07 10:03:44 sephe Exp $
  */
 
 #include "opt_atalk.h"
@@ -469,9 +469,10 @@ static boolean_t
 ether_ipfw_chk(struct mbuf **m0, struct ifnet *dst, struct ip_fw **rule,
               const struct ether_header *eh)
 {
-       struct ether_header save_eh = *eh;      /* might be a ptr in m */
+       struct ether_header save_eh = *eh;      /* might be a ptr in *m0 */
        struct ip_fw_args args;
        struct m_tag *mtag;
+       struct mbuf *m;
        int i;
 
        if (*rule != NULL && fw_one_pass)
@@ -507,18 +508,26 @@ ether_ipfw_chk(struct mbuf **m0, struct ifnet *dst, struct ip_fw **rule,
        *m0 = args.m;
        *rule = args.rule;
 
-       if ((i & IP_FW_PORT_DENY_FLAG) || *m0 == NULL)  /* drop */
+       if (*m0 == NULL)
                return FALSE;
 
-       if (i == 0)                                     /* a PASS rule.  */
+       switch (i) {
+       case IP_FW_PASS:
                return TRUE;
 
-       if (i & IP_FW_PORT_DYNT_FLAG) {
+       case IP_FW_DIVERT:
+       case IP_FW_TEE:
+       case IP_FW_DENY:
                /*
-                * Pass the pkt to dummynet, which consumes it.
+                * XXX at some point add support for divert/forward actions.
+                * If none of the above matches, we have to drop the pkt.
                 */
-               struct mbuf *m;
+               return FALSE;
 
+       case IP_FW_DUMMYNET:
+               /*
+                * Pass the pkt to dummynet, which consumes it.
+                */
                m = *m0;        /* pass the original to dummynet */
                *m0 = NULL;     /* and nothing back to the caller */
 
@@ -526,15 +535,13 @@ ether_ipfw_chk(struct mbuf **m0, struct ifnet *dst, struct ip_fw **rule,
                if (m == NULL)
                        return FALSE;
 
-               ip_fw_dn_io_ptr(m, (i & 0xffff),
-                       dst ? DN_TO_ETH_OUT: DN_TO_ETH_DEMUX, &args);
+               ip_fw_dn_io_ptr(m, args.cookie,
+                               dst ? DN_TO_ETH_OUT: DN_TO_ETH_DEMUX, &args);
                return FALSE;
+
+       default:
+               panic("unknown ipfw return value: %d\n", i);
        }
-       /*
-        * XXX at some point add support for divert/forward actions.
-        * If none of the above matches, we have to drop the pkt.
-        */
-       return FALSE;
 }
 
 static void
index 17a0c59..ea0950b 100644 (file)
@@ -23,7 +23,7 @@
  * SUCH DAMAGE.
  *
  * $FreeBSD: src/sys/netinet/ip_fw2.c,v 1.6.2.12 2003/04/08 10:42:32 maxim Exp $
- * $DragonFly: src/sys/net/ipfw/ip_fw2.c,v 1.78 2008/08/27 14:00:45 sephe Exp $
+ * $DragonFly: src/sys/net/ipfw/ip_fw2.c,v 1.79 2008/09/07 10:03:45 sephe Exp $
  */
 
 #define        DEB(x)
@@ -1626,7 +1626,7 @@ ipfw_chk(struct ip_fw_args *args)
        struct ifnet *oif = args->oif;
 
        struct ip_fw *f = NULL;         /* matching rule */
-       int retval = 0;
+       int retval = IP_FW_PASS;
        struct m_tag *mtag;
        struct divert_info *divinfo;
 
@@ -1673,7 +1673,7 @@ ipfw_chk(struct ip_fw_args *args)
        struct ipfw_context *ctx = ipfw_ctx[mycpuid];
 
        if (m->m_pkthdr.fw_flags & IPFW_MBUF_GENERATED)
-               return 0;       /* accept */
+               return IP_FW_PASS;      /* accept */
 
        if (args->eh == NULL ||         /* layer 3 packet */
            (m->m_pkthdr.len >= sizeof(struct ip) &&
@@ -1762,18 +1762,18 @@ after_ip_checks:
                 * the caller.
                 */
                if (fw_one_pass)
-                       return 0;
+                       return IP_FW_PASS;
 
                /* This rule is being/has been flushed */
                if (ipfw_flushing)
-                       return IP_FW_PORT_DENY_FLAG;
+                       return IP_FW_DENY;
 
                KASSERT(args->rule->cpuid == mycpuid,
                        ("rule used on cpu%d\n", mycpuid));
 
                /* This rule was deleted */
                if (args->rule->rule_flags & IPFW_RULE_F_INVALID)
-                       return IP_FW_PORT_DENY_FLAG;
+                       return IP_FW_DENY;
 
                f = args->rule->next_rule;
                if (f == NULL)
@@ -1797,15 +1797,15 @@ after_ip_checks:
                if (args->eh == NULL && skipto != 0) {
                        /* No skipto during rule flushing */
                        if (ipfw_flushing)
-                               return IP_FW_PORT_DENY_FLAG;
+                               return IP_FW_DENY;
 
                        if (skipto >= IPFW_DEFAULT_RULE)
-                               return(IP_FW_PORT_DENY_FLAG); /* invalid */
+                               return IP_FW_DENY; /* invalid */
 
                        while (f && f->rulenum <= skipto)
                                f = f->next;
                        if (f == NULL)  /* drop packet */
-                               return(IP_FW_PORT_DENY_FLAG);
+                               return IP_FW_DENY;
                } else if (ipfw_flushing) {
                        /* Rules are being flushed; skip to default rule */
                        f = ctx->ipfw_default_rule;
@@ -2188,7 +2188,8 @@ check_body:
                         *   the packet must be dropped ('goto done' after
                         *   setting retval).  If static rules are changed
                         *   during the state installation, the packet will
-                        *   be dropped ('return IP_FW_PORT_DENY_FLAG').
+                        *   be dropped and rule's stats will not beupdated
+                        *   ('return IP_FW_DENY').
                         *
                         * O_PROBE_STATE and O_CHECK_STATE: these opcodes
                         *   cause a lookup of the state table, and a jump
@@ -2200,7 +2201,8 @@ check_body:
                         *   further instances of these opcodes are
                         *   effectively NOPs.  If static rules are changed
                         *   during the state looking up, the packet will
-                        *   be dropped ('return IP_FW_PORT_DENY_FLAG').
+                        *   be dropped and rule's stats will not be updated
+                        *   ('return IP_FW_DENY').
                         */
                        case O_LIMIT:
                        case O_KEEP_STATE:
@@ -2215,13 +2217,13 @@ check_body:
                                if (install_state(f,
                                    (ipfw_insn_limit *)cmd, args, &deny)) {
                                        if (deny)
-                                               return IP_FW_PORT_DENY_FLAG;
+                                               return IP_FW_DENY;
 
-                                       retval = IP_FW_PORT_DENY_FLAG;
+                                       retval = IP_FW_DENY;
                                        goto done; /* error/limit violation */
                                }
                                if (deny)
-                                       return IP_FW_PORT_DENY_FLAG;
+                                       return IP_FW_DENY;
                                match = 1;
                                break;
 
@@ -2243,7 +2245,7 @@ check_body:
                                                L3HDR(struct tcphdr, ip) : NULL,
                                                ip_len, &deny);
                                        if (deny)
-                                               return IP_FW_PORT_DENY_FLAG;
+                                               return IP_FW_DENY;
                                        if (dyn_f != NULL) {
                                                /*
                                                 * Found a rule from a dynamic
@@ -2269,13 +2271,14 @@ check_body:
                                break;
 
                        case O_ACCEPT:
-                               retval = 0;     /* accept */
+                               retval = IP_FW_PASS;    /* accept */
                                goto done;
 
                        case O_PIPE:
                        case O_QUEUE:
                                args->rule = f; /* report matching rule */
-                               retval = cmd->arg1 | IP_FW_PORT_DYNT_FLAG;
+                               args->cookie = cmd->arg1;
+                               retval = IP_FW_DUMMYNET;
                                goto done;
 
                        case O_DIVERT:
@@ -2286,7 +2289,7 @@ check_body:
                                mtag = m_tag_get(PACKET_TAG_IPFW_DIVERT,
                                                 sizeof(*divinfo), MB_DONTWAIT);
                                if (mtag == NULL) {
-                                       retval = IP_FW_PORT_DENY_FLAG;
+                                       retval = IP_FW_DENY;
                                        goto done;
                                }
                                divinfo = m_tag_data(mtag);
@@ -2296,9 +2299,9 @@ check_body:
                                divinfo->tee = (cmd->opcode == O_TEE);
                                m_tag_prepend(m, mtag);
 
+                               args->cookie = cmd->arg1;
                                retval = (cmd->opcode == O_DIVERT) ?
-                                   cmd->arg1 :
-                                   cmd->arg1 | IP_FW_PORT_TEE_FLAG;
+                                        IP_FW_DIVERT : IP_FW_TEE;
                                goto done;
 
                        case O_COUNT:
@@ -2341,11 +2344,11 @@ check_body:
                                         * Return directly here, rule stats
                                         * have been updated above.
                                         */
-                                       return IP_FW_PORT_DENY_FLAG;
+                                       return IP_FW_DENY;
                                }
                                /* FALLTHROUGH */
                        case O_DENY:
-                               retval = IP_FW_PORT_DENY_FLAG;
+                               retval = IP_FW_DENY;
                                goto done;
 
                        case O_FORWARD_IP:
@@ -2357,7 +2360,7 @@ check_body:
                                        mtag = m_tag_get(PACKET_TAG_IPFORWARD,
                                               sizeof(*sin), MB_DONTWAIT);
                                        if (mtag == NULL) {
-                                               retval = IP_FW_PORT_DENY_FLAG;
+                                               retval = IP_FW_DENY;
                                                goto done;
                                        }
                                        sin = m_tag_data(mtag);
@@ -2369,7 +2372,7 @@ check_body:
                                        m->m_pkthdr.fw_flags |=
                                                IPFORWARD_MBUF_TAGGED;
                                }
-                               retval = 0;
+                               retval = IP_FW_PASS;
                                goto done;
 
                        default:
@@ -2393,7 +2396,7 @@ next_rule:;               /* try next rule                */
 
        }               /* end of outer for, scan rules */
        kprintf("+++ ipfw: ouch!, skip past end of rules, denying packet\n");
-       return(IP_FW_PORT_DENY_FLAG);
+       return IP_FW_DENY;
 
 done:
        /* Update statistics */
@@ -2405,7 +2408,7 @@ done:
 pullup_failed:
        if (fw_verbose)
                kprintf("pullup failed\n");
-       return(IP_FW_PORT_DENY_FLAG);
+       return IP_FW_DENY;
 }
 
 static void
index 774b9a3..a615395 100644 (file)
@@ -23,7 +23,7 @@
  * SUCH DAMAGE.
  *
  * $FreeBSD: src/sys/netinet/ip_fw2.h,v 1.1.2.2 2002/08/16 11:03:11 luigi Exp $
- * $DragonFly: src/sys/net/ipfw/ip_fw2.h,v 1.14 2008/08/22 09:14:16 sephe Exp $
+ * $DragonFly: src/sys/net/ipfw/ip_fw2.h,v 1.15 2008/09/07 10:03:45 sephe Exp $
  */
 
 #ifndef _IPFW2_H
@@ -356,9 +356,12 @@ struct _ipfw_dyn_rule {
  * Main firewall chains definitions and global var's definitions.
  */
 
-#define        IP_FW_PORT_DYNT_FLAG    0x10000
-#define        IP_FW_PORT_TEE_FLAG     0x20000
-#define        IP_FW_PORT_DENY_FLAG    0x40000
+/* ipfw_chk/ip_fw_chk_ptr return values */
+#define IP_FW_PASS     0
+#define IP_FW_DENY     1
+#define IP_FW_DIVERT   2
+#define IP_FW_TEE      3
+#define IP_FW_DUMMYNET 4
 
 /*
  * arguments for calling ipfw_chk() and dummynet_io(). We put them
@@ -376,7 +379,18 @@ struct ip_fw_args {
        int flags;                      /* for dummynet                 */
 
        struct ipfw_flow_id f_id;       /* grabbed from IP header       */
-       uint32_t        retval;
+
+       /*
+        * Depend on the return value of ipfw_chk/ip_fw_chk_ptr
+        * 'cookie' field may save following information:
+        *
+        * IP_FW_TEE or IP_FW_DIVERT
+        *   The divert port number
+        *
+        * IP_FW_DUMMYNET
+        *   The pipe or queue number
+        */
+       uint32_t        cookie;
 };
 
 /*
index 593b5c2..74392af 100644 (file)
@@ -65,7 +65,7 @@
  *
  *     @(#)ip_input.c  8.2 (Berkeley) 1/4/94
  * $FreeBSD: src/sys/netinet/ip_input.c,v 1.130.2.52 2003/03/07 07:01:28 silby Exp $
- * $DragonFly: src/sys/netinet/ip_input.c,v 1.98 2008/09/06 14:12:50 sephe Exp $
+ * $DragonFly: src/sys/netinet/ip_input.c,v 1.99 2008/09/07 10:03:44 sephe Exp $
  */
 
 #define        _IP_VHL
@@ -409,6 +409,114 @@ ip_input_handler(struct netmsg *msg0)
        /* msg0 was embedded in the mbuf, do not reply! */
 }
 
+#ifdef IPDIVERT
+static struct mbuf *
+ip_divert_in(struct mbuf *m, int tee, boolean_t *needredispatch)
+{
+       struct mbuf *clone = NULL;
+       struct ip *ip = mtod(m, struct ip *);
+       struct m_tag *mtag;
+
+       if (ip->ip_off & (IP_MF | IP_OFFMASK)) {
+               const struct divert_info *divinfo;
+               u_short frag_off;
+               int hlen;
+
+               /*
+                * Only trust divert info in the fragment
+                * at offset 0.
+                */
+               frag_off = ip->ip_off << 3;
+               if (frag_off != 0) {
+                       mtag = m_tag_find(m, PACKET_TAG_IPFW_DIVERT, NULL);
+                       m_tag_delete(m, mtag);
+               }
+
+               /*
+                * Attempt reassembly; if it succeeds, proceed.
+                * ip_reass() will return a different mbuf.
+                */
+               m = ip_reass(m);
+               if (m == NULL)
+                       return NULL;
+               ip = mtod(m, struct ip *);
+
+               *needredispatch = TRUE;
+
+               /*
+                * Get the header length of the reassembled
+                * packet
+                */
+               hlen = IP_VHL_HL(ip->ip_vhl) << 2;
+
+               /*
+                * Restore original checksum before diverting
+                * packet
+                */
+               ip->ip_len += hlen;
+               ip->ip_len = htons(ip->ip_len);
+               ip->ip_off = htons(ip->ip_off);
+               ip->ip_sum = 0;
+               if (hlen == sizeof(struct ip))
+                       ip->ip_sum = in_cksum_hdr(ip);
+               else
+                       ip->ip_sum = in_cksum(m, hlen);
+               ip->ip_off = ntohs(ip->ip_off);
+               ip->ip_len = ntohs(ip->ip_len);
+
+               /*
+                * Only use the saved divert info
+                */
+               mtag = m_tag_find(m, PACKET_TAG_IPFW_DIVERT, NULL);
+               if (mtag == NULL) {
+                       /* Wrongly configured ipfw */
+                       kprintf("ip_input no divert info\n");
+                       m_freem(m);
+                       return NULL;
+               }
+               divinfo = m_tag_data(mtag);
+               tee = divinfo->tee;
+       }
+
+       /*
+        * Divert or tee packet to the divert protocol if
+        * required.
+        */
+
+       /* Clone packet if we're doing a 'tee' */
+       if (tee)
+               clone = m_dup(m, MB_DONTWAIT);
+
+       /*
+        * Restore packet header fields to original
+        * values
+        */
+       ip->ip_len = htons(ip->ip_len);
+       ip->ip_off = htons(ip->ip_off);
+
+       /* Deliver packet to divert input routine */
+       divert_packet(m, 1);
+
+       /* Catch invalid reference */
+       m = NULL;
+       ip = NULL;
+
+       ipstat.ips_delivered++;
+
+       /* If 'tee', continue with original packet */
+       if (clone != NULL) {
+               /*
+                * Complete processing of the packet.
+                * XXX Better safe than sorry, remove the DIVERT tag.
+                */
+               mtag = m_tag_find(clone, PACKET_TAG_IPFW_DIVERT, NULL);
+               KKASSERT(mtag != NULL);
+               m_tag_delete(clone, mtag);
+       }
+       return clone;
+}
+#endif /* IPDIVERT */
+
 /*
  * IP input routine.  Checksum and byte swap header.  If fragmented
  * try to reassemble.  Process options.  Pass to next level.
@@ -565,6 +673,7 @@ iphack:
 
        if (fw_enable && IPFW_LOADED) {
                struct ip_fw_args args;
+               int tee = 0;
 
                /*
                 * If we've been forwarded from the output side, then
@@ -593,145 +702,49 @@ iphack:
                i = ip_fw_chk_ptr(&args);
                m = args.m;
 
-               if ((i & IP_FW_PORT_DENY_FLAG) || m == NULL) {  /* drop */
-                       if (m != NULL)
-                               m_freem(m);
+               if (m == NULL)
                        return;
-               }
                ip = mtod(m, struct ip *);      /* just in case m changed */
 
-               if (m->m_pkthdr.fw_flags & IPFORWARD_MBUF_TAGGED) {
-                       mtag = m_tag_find(m, PACKET_TAG_IPFORWARD, NULL);
-                       KKASSERT(mtag != NULL);
-                       next_hop = m_tag_data(mtag);
-               }
-
-               if (i == 0 && next_hop == NULL) /* common case */
-                       goto pass;
-               if (i & IP_FW_PORT_DYNT_FLAG) {
-                       /* Send packet to the appropriate pipe */
-                       ip_fw_dn_io_ptr(m, i&0xffff, DN_TO_IP_IN, &args);
-                       return;
-               }
-#ifdef IPDIVERT
-               if (i != 0 && !(i & IP_FW_PORT_DYNT_FLAG)) {
-                       struct mbuf *clone = NULL;
-                       int tee = 0;
-
-                       /* Divert or 'tee'? */
-                       if (i & IP_FW_PORT_TEE_FLAG)
-                               tee = 1;
-
-                       if (ip->ip_off & (IP_MF | IP_OFFMASK)) {
-                               const struct divert_info *divinfo;
-                               u_short frag_off;
-
-                               /*
-                                * Only trust divert info in the fragment
-                                * at offset 0.
-                                */
-                               frag_off = ip->ip_off << 3;
-                               if (frag_off != 0) {
-                                       mtag = m_tag_find(m,
-                                       PACKET_TAG_IPFW_DIVERT, NULL);
-                                       m_tag_delete(m, mtag);
-                               }
-
-                               /*
-                                * Attempt reassembly; if it succeeds, proceed.
-                                * ip_reass() will return a different mbuf.
-                                */
-                               m = ip_reass(m);
-                               if (m == NULL)
-                                       return;
-                               ip = mtod(m, struct ip *);
-
-                               needredispatch = TRUE;
-
-                               /*
-                                * Get the header length of the reassembled
-                                * packet
-                                */
-                               hlen = IP_VHL_HL(ip->ip_vhl) << 2;
-
-                               /*
-                                * Restore original checksum before diverting
-                                * packet
-                                */
-                               ip->ip_len += hlen;
-                               ip->ip_len = htons(ip->ip_len);
-                               ip->ip_off = htons(ip->ip_off);
-                               ip->ip_sum = 0;
-                               if (hlen == sizeof(struct ip))
-                                       ip->ip_sum = in_cksum_hdr(ip);
-                               else
-                                       ip->ip_sum = in_cksum(m, hlen);
-                               ip->ip_off = ntohs(ip->ip_off);
-                               ip->ip_len = ntohs(ip->ip_len);
-
-                               /*
-                                * Only use the saved divert info
-                                */
-                               mtag = m_tag_find(m, PACKET_TAG_IPFW_DIVERT,
+               switch (i) {
+               case IP_FW_PASS:
+                       if (m->m_pkthdr.fw_flags & IPFORWARD_MBUF_TAGGED) {
+                               mtag = m_tag_find(m, PACKET_TAG_IPFORWARD,
                                                  NULL);
-                               if (mtag == NULL) {
-                                       /* Wrongly configured ipfw */
-                                       kprintf("ip_input no divert info\n");
-                                       m_freem(m);
-                                       return;
-                               }
-                               divinfo = m_tag_data(mtag);
-                               tee = divinfo->tee;
+                               KKASSERT(mtag != NULL);
+                               next_hop = m_tag_data(mtag);
                        }
+                       goto pass;
 
-                       /*
-                        * Divert or tee packet to the divert protocol if
-                        * required.
-                        */
-
-                       /* Clone packet if we're doing a 'tee' */
-                       if (tee)
-                               clone = m_dup(m, MB_DONTWAIT);
+               case IP_FW_DENY:
+                       m_freem(m);
+                       return;
 
-                       /*
-                        * Restore packet header fields to original
-                        * values
-                        */
-                       ip->ip_len = htons(ip->ip_len);
-                       ip->ip_off = htons(ip->ip_off);
+               case IP_FW_DUMMYNET:
+                       /* Send packet to the appropriate pipe */
+                       ip_fw_dn_io_ptr(m, args.cookie, DN_TO_IP_IN, &args);
+                       return;
 
-                       /* Deliver packet to divert input routine */
-                       divert_packet(m, 1);
-                       ipstat.ips_delivered++;
+               case IP_FW_TEE:
+                       tee = 1;
+                       /* FALL THROUGH */
 
-                       /* If 'tee', continue with original packet */
-                       if (clone == NULL)
+               case IP_FW_DIVERT:
+#ifdef IPDIVERT
+                       m = ip_divert_in(m, tee, &needredispatch);
+                       if (m == NULL)
                                return;
-                       m = clone;
                        ip = mtod(m, struct ip *);
-
-                       /*
-                        * Complete processing of the packet.
-                        *
-                        * XXX
-                        * Better safe than sorry, remove the DIVERT
-                        * tag.
-                        */
-                       mtag = m_tag_find(m, PACKET_TAG_IPFW_DIVERT,
-                                         NULL);
-                       KKASSERT(mtag != NULL);
-                       m_tag_delete(m, mtag);
-
+                       hlen = IP_VHL_HL(ip->ip_vhl) << 2;
                        goto pass;
-               }
+#else
+                       m_freem(m);
+                       return;
 #endif
-               if (i == 0 && next_hop != NULL)
-                       goto pass;
-               /*
-                * if we get here, the packet must be dropped
-                */
-               m_freem(m);
-               return;
+
+               default:
+                       panic("unknown ipfw return value: %d\n", i);
+               }
        }
 pass:
 
index ae29973..59d234f 100644 (file)
@@ -28,7 +28,7 @@
  *
  *     @(#)ip_output.c 8.3 (Berkeley) 1/21/94
  * $FreeBSD: src/sys/netinet/ip_output.c,v 1.99.2.37 2003/04/15 06:44:45 silby Exp $
- * $DragonFly: src/sys/netinet/ip_output.c,v 1.56 2008/09/07 08:15:25 sephe Exp $
+ * $DragonFly: src/sys/netinet/ip_output.c,v 1.57 2008/09/07 10:03:44 sephe Exp $
  */
 
 #define _IP_VHL
@@ -121,6 +121,39 @@ int        ip_optcopy(struct ip *, struct ip *);
 
 extern struct protosw inetsw[];
 
+#ifdef IPDIVERT
+static struct mbuf *
+ip_divert_out(struct mbuf *m, int tee)
+{
+       struct mbuf *clone = NULL;
+       struct ip *ip = mtod(m, struct ip *);
+
+       /* Clone packet if we're doing a 'tee' */
+       if (tee)
+               clone = m_dup(m, MB_DONTWAIT);
+
+       /*
+        * XXX
+        * delayed checksums are not currently compatible
+        * with divert sockets.
+        */
+       if (m->m_pkthdr.csum_flags & CSUM_DELAY_DATA) {
+               in_delayed_cksum(m);
+               m->m_pkthdr.csum_flags &= ~CSUM_DELAY_DATA;
+       }
+
+       /* Restore packet header fields to original values */
+       ip->ip_len = htons(ip->ip_len);
+       ip->ip_off = htons(ip->ip_off);
+
+       /* Deliver packet to divert input routine */
+       divert_packet(m, 0);
+
+       /* If 'tee', continue with original packet */
+       return clone;
+}
+#endif /* IPDIVERT */
+
 /*
  * IP output.  The packet in mbuf chain m contains a skeletal IP
  * header (with len, off, ttl, proto, tos, src, dst).
@@ -721,6 +754,7 @@ spd_done:
        if (fw_enable && IPFW_LOADED && !next_hop) {
                struct sockaddr_in *old = dst;
                struct ip_fw_args args;
+               int tee = 0;
 
                if (m->m_pkthdr.fw_flags & DUMMYNET_MBUF_TAGGED) {
                        /* Extract info from dummynet tag */
@@ -748,35 +782,24 @@ spd_done:
                }
                ip = mtod(m, struct ip *);
 
-               if (m->m_pkthdr.fw_flags & IPFORWARD_MBUF_TAGGED) {
-                       mtag = m_tag_find(m, PACKET_TAG_IPFORWARD, NULL);
-                       KKASSERT(mtag != NULL);
-                       next_hop = m_tag_data(mtag);
-                       dst = next_hop;
-               }
+               switch (off) {
+               case IP_FW_PASS:
+                       if (m->m_pkthdr.fw_flags & IPFORWARD_MBUF_TAGGED) {
+                               mtag = m_tag_find(m, PACKET_TAG_IPFORWARD,
+                                                 NULL);
+                               KKASSERT(mtag != NULL);
+                               next_hop = m_tag_data(mtag);
+                               dst = next_hop;
+                               break;
+                       }
+                       goto pass;
 
-               /*
-                * On return we must do the following:
-                * (off & IP_FW_PORT_DENY_FLAG) -> drop the pkt (new interface)
-                * 1<=off<= 0xffff              -> DIVERT
-                * (off & IP_FW_PORT_DYNT_FLAG) -> send to a DUMMYNET pipe
-                * (off & IP_FW_PORT_TEE_FLAG)  -> TEE the packet
-                * dst != old                   -> IPFIREWALL_FORWARD
-                * off==0, dst==old             -> accept
-                * If some of the above modules are not compiled in, then
-                * we should't have to check the corresponding condition
-                * (because the ipfw control socket should not accept
-                * unsupported rules), but better play safe and drop
-                * packets in case of doubt.
-                */
-               if (off & IP_FW_PORT_DENY_FLAG) {
+               case IP_FW_DENY:
                        m_freem(m);
                        error = EACCES;
                        goto done;
-               }
-               if (off == 0 && dst == old)             /* common case */
-                       goto pass;
-               if (off & IP_FW_PORT_DYNT_FLAG) {
+
+               case IP_FW_DUMMYNET:
                        /*
                         * pass the pkt to dummynet. Need to include
                         * pipe number, m, ifp, ro, dst because these are
@@ -791,45 +814,34 @@ spd_done:
                        args.flags = flags;
 
                        error = 0;
-                       ip_fw_dn_io_ptr(m, off & 0xffff, DN_TO_IP_OUT, &args);
+                       ip_fw_dn_io_ptr(m, args.cookie, DN_TO_IP_OUT, &args);
                        goto done;
-               }
-#ifdef IPDIVERT
-               if (off != 0 && !(off & IP_FW_PORT_DYNT_FLAG)) {
-                       struct mbuf *clone = NULL;
-
-                       /* Clone packet if we're doing a 'tee' */
-                       if ((off & IP_FW_PORT_TEE_FLAG))
-                               clone = m_dup(m, MB_DONTWAIT);
-
-                       /*
-                        * XXX
-                        * delayed checksums are not currently compatible
-                        * with divert sockets.
-                        */
-                       if (m->m_pkthdr.csum_flags & CSUM_DELAY_DATA) {
-                               in_delayed_cksum(m);
-                               m->m_pkthdr.csum_flags &= ~CSUM_DELAY_DATA;
-                       }
-
-                       /* Restore packet header fields to original values */
-                       ip->ip_len = htons(ip->ip_len);
-                       ip->ip_off = htons(ip->ip_off);
 
-                       /* Deliver packet to divert input routine */
-                       divert_packet(m, 0);
+               case IP_FW_TEE:
+                       tee = 1;
+                       /* FALL THROUGH */
 
-                       /* If 'tee', continue with original packet */
-                       if (clone != NULL) {
-                               m = clone;
-                               ip = mtod(m, struct ip *);
-                               goto pass;
-                       }
+               case IP_FW_DIVERT:
+#ifdef IPDIVERT
+                       m = ip_divert_out(m, tee);
+                       if (m == NULL)
+                               goto done;
+                       ip = mtod(m, struct ip *);
+                       goto pass;
+#else
+                       m_freem(m);
+                       /* not sure this is the right error msg */
+                       error = EACCES;
                        goto done;
-               }
 #endif
 
+               default:
+                       panic("unknown ipfw return value: %d\n", off);
+               }
+
                /* IPFIREWALL_FORWARD */
+               KKASSERT(off == IP_FW_PASS); /* only if PASS */
+
                /*
                 * Check dst to make sure it is directly reachable on the
                 * interface we previously thought it was.
@@ -840,7 +852,7 @@ spd_done:
                 * such control is nigh impossible. So we do it here.
                 * And I'm babbling.
                 */
-               if (off == 0 && old != dst) { /* FORWARD, dst has changed */
+               if (old != dst) { /* FORWARD, dst has changed */
 #if 0
                        /*
                         * XXX To improve readability, this block should be
@@ -952,16 +964,7 @@ spd_done:
                         */
                        if (src_was_INADDR_ANY)
                                ip->ip_src = IA_SIN(ia)->sin_addr;
-                       goto pass ;
                }
-
-               /*
-                * if we get here, none of the above matches, and
-                * we have to drop the pkt
-                */
-               m_freem(m);
-               error = EACCES; /* not sure this is the right error msg */
-               goto done;
        }
 
 pass: