Major changes:
[ipfw.git] / dummynet / ip_fw2.c
index e3483df..4c9b0a3 100644 (file)
@@ -2002,25 +2002,6 @@ check_uidgid(ipfw_insn_u32 *insn, int proto, struct ifnet *oif,
        int match = 0;
        struct sk_buff *skb = ((struct mbuf *)inp)->m_skb;
 
-#if 0 /* debug */
-       printf("%s opcode %d arg %d oif %p src 0x%x:%d dst 0x%x:%d\n", __FUNCTION__,
-               insn->o.opcode, insn->d[0], oif,
-               ntohl(src_ip.s_addr), ntohs(src_port),
-               ntohl(dst_ip.s_addr), ntohs(dst_port)
-               );
-#endif
-       if (insn->o.opcode == O_JAIL) {
-#ifdef IPFW_PLANETLAB
-               match = (skb->skb_tag == insn->d[0]);
-#if 0 /* debug */
-               printf("JAIL compiled for planetlab xid %d want %d result %d\n",
-                       skb->skb_tag, insn->d[0], match);
-#endif
-
-#endif
-               return match;
-       }
-
        if (*ugid_lookupp == 0) {       /* actively lookup and copy in cache */
 
                /* returns null if any element of the chain up to file is null.
@@ -2037,6 +2018,11 @@ check_uidgid(ipfw_insn_u32 *insn, int proto, struct ifnet *oif,
 
        if (insn->o.opcode == O_UID)
                match = (ugp->fw_uid == (uid_t)insn->d[0]);
+       else if (insn->o.opcode == O_JAIL)
+               match = (ugp->fw_groups[1] == (uid_t)insn->d[0]);
+       else if (insn->o.opcode == O_GID)
+               match = (ugp->fw_groups[0] == (uid_t)insn->d[0]);
        return match;
 
 #else /* FreeBSD */
@@ -2273,6 +2259,8 @@ ipfw_chk(struct ip_fw_args *args)
        /* end of ipv6 variables */
        int is_ipv4 = 0;
 
+       int done = 0;           /* flag for actions match */
+
        if (m->m_flags & M_SKIP_FIREWALL)
                return (IP_FW_PASS);    /* accept */
 
@@ -2583,7 +2571,7 @@ do {                                                                      \
                uint32_t tablearg = 0;
                int l, cmdlen, skip_or; /* skip rest of OR block */
 
-again:
+/* again: */
                if (set_disable & (1 << f->set) )
                        continue;
 
@@ -2598,7 +2586,7 @@ again:
                         * the target rule.
                         */
 
-check_body:
+/* check_body: */
                        cmdlen = F_LEN(cmd);
                        /*
                         * An OR block (insn_1 || .. || insn_n) has the
@@ -3178,8 +3166,8 @@ check_body:
                         * Exceptions:
                         * O_COUNT and O_SKIPTO actions:
                         *   instead of terminating, we jump to the next rule
-                        *   ('goto next_rule', equivalent to a 'break 2'),
-                        *   or to the SKIPTO target ('goto again' after
+                        *   ('break' after setting match and l)
+                        *   or to the SKIPTO target ('break' after
                         *   having set f, cmd and l), respectively.
                         *
                         * O_TAG, O_LOG and O_ALTQ action parameters:
@@ -3197,19 +3185,24 @@ check_body:
                         * O_PROBE_STATE and O_CHECK_STATE: these opcodes
                         *   cause a lookup of the state table, and a jump
                         *   to the 'action' part of the parent rule
-                        *   ('goto check_body') if an entry is found, or
+                        *   if an entry is found, or
                         *   (CHECK_STATE only) a jump to the next rule if
                         *   the entry is not found ('goto next_rule').
                         *   The result of the lookup is cached to make
                         *   further instances of these opcodes are
                         *   effectively NOPs.
+                        *   The jump to the next rule is done by a break
+                        *   after zeroing the cmdlen value and setting
+                        *   match.
                         */
                        case O_LIMIT:
                        case O_KEEP_STATE:
                                if (install_state(f,
                                    (ipfw_insn_limit *)cmd, args, tablearg)) {
                                        retval = IP_FW_DENY;
-                                       goto done; /* error/limit violation */
+                                       /* was goto done; */ /* error/limit violation */
+                                       l = 0;          /* break the inner loop */
+                                       done = 1;       /* break the external loop */
                                }
                                match = 1;
                                break;
@@ -3241,7 +3234,10 @@ check_body:
                                        cmd = ACTION_PTR(f);
                                        l = f->cmd_len - f->act_ofs;
                                        IPFW_DYN_UNLOCK();
-                                       goto check_body;
+                                       /* previously was goto check_body; */
+                                       cmdlen = 0;     /* make null for() changes */
+                                       match = 1;      /* do not break to the external loop */
+                                       break;
                                }
                                /*
                                 * Dynamic entry not found. If CHECK_STATE,
@@ -3249,13 +3245,16 @@ check_body:
                                 * ignore and continue with next opcode.
                                 */
                                if (cmd->opcode == O_CHECK_STATE)
-                                       goto next_rule;
+                                       l = 0; /* was goto next_rule; */
                                match = 1;
                                break;
 
                        case O_ACCEPT:
                                retval = 0;     /* accept */
-                               goto done;
+                               /* was goto done; */
+                               l = 0;          /* break the inner loop */
+                               done = 1;       /* break the external loop */
+                               break;
 
                        case O_PIPE:
                        case O_QUEUE:
@@ -3265,7 +3264,10 @@ check_body:
                                else
                                        args->cookie = cmd->arg1;
                                retval = IP_FW_DUMMYNET;
-                               goto done;
+                               /* was goto done; */
+                               l = 0;          /* break the inner loop */
+                               done = 1;       /* break the external loop */
+                               break;
 
 #if 0
                        case O_DIVERT:
@@ -3292,7 +3294,10 @@ check_body:
                                m_tag_prepend(m, mtag);
                                retval = (cmd->opcode == O_DIVERT) ?
                                    IP_FW_DIVERT : IP_FW_TEE;
-                               goto done;
+                               /* was goto done; */
+                               l = 0;          /* break the inner loop */
+                               done = 1;       /* break the external loop */
+                               break;
                        }
 #endif
 
@@ -3301,8 +3306,12 @@ check_body:
                                f->pcnt++;      /* update stats */
                                f->bcnt += pktlen;
                                f->timestamp = time_uptime;
-                               if (cmd->opcode == O_COUNT)
-                                       goto next_rule;
+                               if (cmd->opcode == O_COUNT) {
+                                       /* was goto next_rule; */
+                                       l = 0;          /* exit the inner loop */
+                                       match = 1;      /* do not break the loop */
+                                       break;
+                               }
                                /* handle skipto */
                                if (cmd->arg1 == IP_FW_TABLEARG) {
                                        f = lookup_next_rule(f, tablearg);
@@ -3311,7 +3320,26 @@ check_body:
                                                lookup_next_rule(f, 0);
                                        f = f->next_rule;
                                }
-                               goto again;
+                               /* previously was "goto again;"
+                                * We emulate by re-entering the inner loop
+                                * with the correct f, l and cmd.
+                                * First, skip over disabled rules.
+                                * Should at least match the default rule,
+                                * but try to be robust.
+                                */
+                               while (f && (set_disable & (1 << f->set)))
+                                       f = f->next;
+                               /* prepare to re-enter the inner loop. */
+                               if (f) {        /* better safe than sorry */
+                                       l = f->cmd_len;
+                                       cmd = f->cmd;
+                               } else {
+                                       l = 0;  /* this will break the inner loop */
+                               }
+                               cmdlen = 0;     /* reset loop condition */
+                               skip_or = 0;
+                               match = 1;      /* do not break the loop */
+                               break;
 
                        case O_REJECT:
                                /*
@@ -3345,7 +3373,10 @@ check_body:
 #endif
                        case O_DENY:
                                retval = IP_FW_DENY;
-                               goto done;
+                               /* goto done; */
+                               l = 0;          /* break the inner loop */
+                               done = 1;       /* break the external loop */
+                               break;
 
                        case O_FORWARD_IP: {
                                struct sockaddr_in *sa;
@@ -3366,7 +3397,10 @@ check_body:
                                }
                                retval = IP_FW_PASS;
                            }
-                           goto done;
+                       /* goto done; */
+                       l = 0;              /* break the inner loop */
+                       done = 1;   /* break the external loop */
+                       break;
 
                        case O_NETGRAPH:
                        case O_NGTEE:
@@ -3377,7 +3411,10 @@ check_body:
                                        args->cookie = cmd->arg1;
                                retval = (cmd->opcode == O_NETGRAPH) ?
                                    IP_FW_NETGRAPH : IP_FW_NGTEE;
-                               goto done;
+                               /* goto done; */
+                               l = 0;          /* break the inner loop */
+                               done = 1;       /* break the external loop */
+                               break;
 
 #if 0
                        case O_SETFIB:
@@ -3386,7 +3423,10 @@ check_body:
                                f->timestamp = time_uptime;
                                M_SETFIB(m, cmd->arg1);
                                args->f_id.fib = cmd->arg1;
-                               goto next_rule;
+                               /* was goto next_rule; */
+                               l = 0;
+                               match = 1;
+                               break;
 
                        case O_NAT: {
                                struct cfg_nat *t;
@@ -3401,7 +3441,10 @@ check_body:
                                                LOOKUP_NAT(layer3_chain, nat_id, t);
                                                if (t == NULL) {
                                                        retval = IP_FW_DENY;
-                                                       goto done;
+                                                       /* goto done; */
+                                                       l = 0;          /* break the inner loop */
+                                                       done = 1;       /* break the external loop */
+                                                       break;
                                                }
                                                if (cmd->arg1 != IP_FW_TABLEARG)
                                                        ((ipfw_insn_nat *)cmd)->nat = t;
@@ -3409,7 +3452,10 @@ check_body:
                                        retval = ipfw_nat_ptr(args, t, m);
                                } else
                                        retval = IP_FW_DENY;
-                               goto done;
+                               /* goto done; */
+                               l = 0;          /* break the inner loop */
+                               done = 1;       /* break the external loop */
+                               break;
                        }
 #endif
 
@@ -3431,21 +3477,25 @@ check_body:
 
                }       /* end of inner for, scan opcodes */
 
-next_rule:;            /* try next rule                */
+               if (done)
+                       break;
 
+/* next_rule:; */              /* try next rule                */
        }               /* end of outer for, scan rules */
+
+       if (done) {
+               /* Update statistics */
+               f->pcnt++;
+               f->bcnt += pktlen;
+               f->timestamp = time_uptime;
+               IPFW_RUNLOCK(chain);
+               return (retval);
+       }
+
        printf("ipfw: ouch!, skip past end of rules, denying packet\n");
        IPFW_RUNLOCK(chain);
        return (IP_FW_DENY);
 
-done:
-       /* Update statistics */
-       f->pcnt++;
-       f->bcnt += pktlen;
-       f->timestamp = time_uptime;
-       IPFW_RUNLOCK(chain);
-       return (retval);
-
 pullup_failed:
        if (fw_verbose)
                printf("ipfw: pullup failed\n");
@@ -4147,7 +4197,7 @@ bad_size:
 }
 
 /*
- * Copy the static and dynamic rules to the supplied buffer
+ * Copy the static rules to the supplied buffer
  * and return the amount of space actually used.
  */
 static size_t
@@ -4160,6 +4210,7 @@ ipfw_getrules(struct ip_fw_chain *chain, void *buf, size_t space)
        time_t  boot_seconds;
 
         boot_seconds = boottime.tv_sec;
+
        /* XXX this can take a long time and locking will block packet flow */
        IPFW_RLOCK(chain);
        for (rule = chain->rules; rule ; rule = rule->next) {
@@ -4185,6 +4236,27 @@ ipfw_getrules(struct ip_fw_chain *chain, void *buf, size_t space)
                }
        }
        IPFW_RUNLOCK(chain);
+
+       return (bp - (char *)buf);
+}
+
+/*
+ * Copy the dynamic rules to the supplied buffer
+ * and return the amount of space actually used.
+ * XXX marta if we allocate X and rules grows
+ * we check for size limit while copying rules into the buffer
+ */
+static size_t
+ipfw_getdynrules(struct ip_fw_chain *chain, void *buf, size_t space)
+{
+       char *bp = buf;
+       char *ep = bp + space;
+       int i;
+       time_t  boot_seconds;
+
+       printf("dynrules requested\n");
+       boot_seconds = boottime.tv_sec;
+
        if (ipfw_dyn_v) {
                ipfw_dyn_rule *p, *last = NULL;
 
@@ -4217,12 +4289,16 @@ ipfw_getrules(struct ip_fw_chain *chain, void *buf, size_t space)
                                            TIME_LEQ(dst->expire, time_uptime) ?
                                                0 : dst->expire - time_uptime ;
                                        bp += sizeof(ipfw_dyn_rule);
+                               } else {
+                                       p = NULL;       /* break the loop */
+                                       i = curr_dyn_buckets;
                                }
                        }
                IPFW_DYN_UNLOCK();
                if (last != NULL) /* mark last dynamic rule */
                        bzero(&last->next, sizeof(last));
        }
+
        return (bp - (char *)buf);
 }
 
@@ -4259,10 +4335,8 @@ ipfw_ctl(struct sockopt *sopt)
        switch (sopt->sopt_name) {
        case IP_FW_GET:
                /*
-                * pass up a copy of the current rules. Static rules
-                * come first (the last of which has number IPFW_DEFAULT_RULE),
-                * followed by a possibly empty list of dynamic rule.
-                * The last dynamic rule has NULL in the "next" field.
+                * pass up a copy of the current static rules.
+                * The last static rule has number IPFW_DEFAULT_RULE.
                 *
                 * Note that the calculated size is used to bound the
                 * amount of data returned to the user.  The rule set may
@@ -4270,8 +4344,6 @@ ipfw_ctl(struct sockopt *sopt)
                 * data in which case we'll just return what fits.
                 */
                size = static_len;      /* size of static rules */
-               if (ipfw_dyn_v)         /* add size of dyn.rules */
-                       size += (dyn_count * sizeof(ipfw_dyn_rule));
 
                /*
                 * XXX todo: if the user passes a short length just to know
@@ -4284,6 +4356,20 @@ ipfw_ctl(struct sockopt *sopt)
                free(buf, M_TEMP);
                break;
 
+       case IP_FW_DYN_GET:
+               /*
+                * pass up a copy of the current dynamic rules.
+                * The last dynamic rule has NULL in the "next" field.
+                */
+               /* if (!ipfw_dyn_v) XXX check for empty set ? */
+               size = (dyn_count * sizeof(ipfw_dyn_rule)); /* size of dyn. rules */
+
+               buf = malloc(size, M_TEMP, M_WAITOK);
+               error = sooptcopyout(sopt, buf,
+                               ipfw_getdynrules(&layer3_chain, buf, size));
+               free(buf, M_TEMP);
+               break;
+
        case IP_FW_FLUSH:
                /*
                 * Normally we cannot release the lock on each iteration.